From d4f439c9b0f1492a1e0f055aca8ca2aeccc0d93d Mon Sep 17 00:00:00 2001 From: Zax Zaxu Date: Wed, 25 Nov 2015 22:53:05 +0100 Subject: [PATCH] ListBox widget added. --- CMakeLists.txt | 2 + examples/CMakeLists.txt | 1 + examples/ListBox.cpp | 91 ++++++++++++++ include/SFGUI/ListBox.hpp | 60 ++++++++++ include/SFGUI/Widgets.hpp | 1 + src/SFGUI/ListBox.cpp | 243 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 398 insertions(+) create mode 100644 examples/ListBox.cpp create mode 100644 include/SFGUI/ListBox.hpp create mode 100644 src/SFGUI/ListBox.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8936030c..3d1fdf28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ set( "${INCLUDE_PATH}/SFGUI/Frame.hpp" "${INCLUDE_PATH}/SFGUI/Image.hpp" "${INCLUDE_PATH}/SFGUI/Label.hpp" + "${INCLUDE_PATH}/SFGUI/ListBox.hpp" "${INCLUDE_PATH}/SFGUI/Misc.hpp" "${INCLUDE_PATH}/SFGUI/Notebook.hpp" "${INCLUDE_PATH}/SFGUI/Object.hpp" @@ -135,6 +136,7 @@ set( "${SOURCE_PATH}/SFGUI/GLLoader.hpp" "${SOURCE_PATH}/SFGUI/Image.cpp" "${SOURCE_PATH}/SFGUI/Label.cpp" + "${SOURCE_PATH}/SFGUI/ListBox.cpp" "${SOURCE_PATH}/SFGUI/Misc.cpp" "${SOURCE_PATH}/SFGUI/Notebook.cpp" "${SOURCE_PATH}/SFGUI/Object.cpp" diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0a81d307..e8f0d547 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -38,6 +38,7 @@ build_example( "ProgressBar" "ProgressBar.cpp" ) build_example( "SpinButton" "SpinButton.cpp" ) build_example( "Canvas" "Canvas.cpp" ) build_example( "CustomWidget" "CustomWidget.cpp" ) +build_example( "ListBox" "ListBox.cpp" ) build_example( "SFGUI-Test" "Test.cpp" ) # Copy data directory to build cache directory to be able to run examples from diff --git a/examples/ListBox.cpp b/examples/ListBox.cpp new file mode 100644 index 00000000..aec1de5f --- /dev/null +++ b/examples/ListBox.cpp @@ -0,0 +1,91 @@ +// Always include the necessary header files. +// Including SFGUI/Widgets.hpp includes everything +// you can possibly need automatically. +#include +#include + +#include + +int main() { + sfg::SFGUI sfgui; + sf::RenderWindow window(sf::VideoMode(640, 480), "ListBox Example"); + window.setVerticalSyncEnabled(true); + window.setFramerateLimit(30); + + sfg::Desktop desktop; + + auto sfg_window = sfg::Window::Create(); + sfg_window->SetTitle( "ListBoxExample" ); + + auto box_outer = sfg::Box::Create( sfg::Box::Orientation::HORIZONTAL, 15.0f ); + auto box_inner1 = sfg::Box::Create( sfg::Box::Orientation::VERTICAL, 5.0f ); + auto box_inner2 = sfg::Box::Create( sfg::Box::Orientation::VERTICAL, 5.0f ); + + auto label1 = sfg::Label::Create("I'm single-select list."); + auto label2 = sfg::Label::Create("I'm multi-select list."); + + auto input1 = sfg::Entry::Create(""); + auto input2 = sfg::Entry::Create(""); + + auto list1 = sfg::ListBox::Create(); + list1->AppendItem( "Item1" ); + list1->AppendItem( "Item2" ); + list1->AppendItem( "Item3" ); + list1->GetSignal( sfg::ListBox::OnSelect ).Connect( std::bind( [list1, input1](){ if(list1->GetSelectedItemsCount()) input1->SetText(list1->GetSelectedItemText()); else input1->SetText(""); } ) ); + + auto list2 = sfg::ListBox::Create(); + list2->AppendItem( "Item1" ); + list2->AppendItem( "Item2" ); + list2->AppendItem( "Item3" ); + list2->multiselect = true; + list2->GetSignal( sfg::ListBox::OnSelect ).Connect( std::bind( [list2, input2](){ + std::string str = ""; + for(unsigned i=0; iGetSelectedItemsCount(); i++) + { + str += list2->GetSelectedItemText(i); + str += ' '; + } + input2->SetText(str); + } ) ); + + box_outer->Pack(box_inner1); + box_outer->Pack(box_inner2); + + box_inner1->Pack(label1); + box_inner1->Pack(list1); + box_inner1->Pack(input1); + + box_inner2->Pack(label2); + box_inner2->Pack(list2); + box_inner2->Pack(input2); + + sfg_window->Add( box_outer ); + desktop.Add( sfg_window ); + + sfg_window->SetPosition(sf::Vector2f(window.getSize().x/2-sfg_window->GetRequisition().x/2, window.getSize().y/2-sfg_window->GetRequisition().y/2)); + + sf::Event event; + sf::Clock clock; + + window.resetGLStates(); + + while (window.isOpen()) + { + while (window.pollEvent(event)) + { + desktop.HandleEvent( event ); + switch(event.type) + { + case sf::Event::Closed: + window.close(); + break; + } + } + desktop.Update( clock.restart().asSeconds() ); + window.clear(); + sfgui.Display( window ); + window.display(); + } + + return 0; +} diff --git a/include/SFGUI/ListBox.hpp b/include/SFGUI/ListBox.hpp new file mode 100644 index 00000000..8ab55c0b --- /dev/null +++ b/include/SFGUI/ListBox.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include + +#include +#include +#include + +namespace sfg { + +class SFGUI_API ListBox : public Widget { + public: + typedef std::shared_ptr Ptr; //!< Shared pointer. + typedef std::shared_ptr PtrConst; //!< Shared pointer. + + /** Create button. + * @return ListBox. + */ + static Ptr Create( ); + + const std::string& GetName() const override; + + unsigned GetSelectedItemsCount(); + unsigned GetSelectedItemIndex(unsigned n=0); + const std::string& GetSelectedItemText(unsigned n=0); + + void AppendItem(std::string str); + void PrependItem(std::string str); + void RemoveItem(unsigned index); + void Clear(); + + // Signals. + static Signal::SignalID OnSelect; //!< Fired when an entry is selected. + + bool resize_automatically = true; + bool multiselect = false; + + protected: + /** Ctor. + */ + ListBox() = default; + + std::unique_ptr InvalidateImpl() const override; + sf::Vector2f CalculateRequisition() override; + + private: + void HandleMouseEnter( int x, int y ) override; + void HandleMouseLeave( int x, int y ) override; + void HandleMouseMoveEvent( int x, int y ) override; + void HandleMouseButtonEvent( sf::Mouse::Button button, bool press, int x, int y ) override; + + std::vector m_entries; + int selection_begin = -1, selection_end = -1; + int hovered_element = -1; bool pressed_on_widget = false; + std::vector selection_odds; + + bool widget_hover = false; +}; + +} diff --git a/include/SFGUI/Widgets.hpp b/include/SFGUI/Widgets.hpp index 45774997..ebc07f25 100644 --- a/include/SFGUI/Widgets.hpp +++ b/include/SFGUI/Widgets.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/src/SFGUI/ListBox.cpp b/src/SFGUI/ListBox.cpp new file mode 100644 index 00000000..8e487792 --- /dev/null +++ b/src/SFGUI/ListBox.cpp @@ -0,0 +1,243 @@ +#include +#include +#include +#include +#include +#include + +float h = 0; +float padding; +int odds_last_sel = -1; +std::vector sel; + +namespace sfg { + +Signal::SignalID ListBox::OnSelect = 0; + +ListBox::Ptr ListBox::Create( ) { + auto ptr = Ptr( new ListBox ); + return ptr; +} + +std::unique_ptr ListBox::InvalidateImpl() const { + + auto background_color = sfg::Context::Get().GetEngine().GetProperty( "BackgroundColor", shared_from_this() ); + const auto& font_name = sfg::Context::Get().GetEngine().GetProperty( "FontName", shared_from_this() ); + auto font_size = sfg::Context::Get().GetEngine().GetProperty( "FontSize", shared_from_this() ); + padding = Context::Get().GetEngine().GetProperty( "Padding", shared_from_this() ); + + auto inverted_color = sf::Color::White - background_color; + inverted_color.a = 255; + + sf::Color med_color(background_color.r/2+inverted_color.r/2, background_color.g/2+inverted_color.g/2, background_color.b/2+inverted_color.b/2, 255); + + const auto& font = sfg::Context::Get().GetEngine().GetResourceManager().GetFont( font_name ); + + std::unique_ptr queue( new sfg::RenderQueue ); + + queue->Add( + sfg::Renderer::Get().CreatePane( + sf::Vector2f( 0.f, 0.f ), + sf::Vector2f( GetAllocation().width, GetAllocation().height ), + 1.f, + background_color, + sf::Color(0, 0, 0, 128), + 5.f + ) + ); + + h = sfg::Context::Get().GetEngine().GetFontLineHeight( *font, font_size ); + + sel.clear(); + for(unsigned i=0; i=selection_begin && i<=selection_end) || (i>=selection_end && i<=selection_begin)) selected = true; + + if(selection_odds[i]) selected = !selected; + + if(selected) sel.push_back(i); + + text.setPosition( + padding, + y + padding + ); + + queue->Add( + sfg::Renderer::Get().CreatePane( + sf::Vector2f( 0.f, y ), + sf::Vector2f( GetAllocation().width, h+padding*2 ), + 1.f, + (selected?sf::Color::Blue:(hover?med_color:background_color)), + sf::Color(0, 0, 0, 128), + 5.f + ) + ); + + queue->Add( sfg::Renderer::Get().CreateText( text ) ); + } + + return queue; +} + +sf::Vector2f ListBox::CalculateRequisition() { + padding = Context::Get().GetEngine().GetProperty( "Padding", shared_from_this() ); + float spacing( Context::Get().GetEngine().GetProperty( "Spacing", shared_from_this() ) ); + const std::string& font_name( Context::Get().GetEngine().GetProperty( "FontName", shared_from_this() ) ); + unsigned int font_size( Context::Get().GetEngine().GetProperty( "FontSize", shared_from_this() ) ); + const sf::Font& font( *Context::Get().GetEngine().GetResourceManager().GetFont( font_name ) ); + + h = sfg::Context::Get().GetEngine().GetFontLineHeight( font, font_size ); + sf::Vector2f ret(100, 100); + + for(unsigned i=0; i ret.x) ret.x = requisition.x; + } + + float lines_h = (h+2 * padding-1) * m_entries.size(); + if(lines_h>ret.y) ret.y = lines_h; + + return ret; +} + +const std::string& ListBox::GetName() const { + static const std::string name( "ListBox" ); + return name; +} + +unsigned ListBox::GetSelectedItemsCount() +{ + return sel.size(); +} + +unsigned ListBox::GetSelectedItemIndex(unsigned i) +{ + if(i>sel.size()) return (unsigned)(-1); + return sel[i]; +} + +const std::string& ListBox::GetSelectedItemText(unsigned i) +{ + if(i>sel.size()) return 0; + return m_entries[sel[i]]; +} + +void ListBox::AppendItem(std::string str) +{ + m_entries.push_back(str); + selection_odds.push_back(false); + if(resize_automatically) + { + RequestResize(); + Invalidate(); + } +} + +void ListBox::PrependItem(std::string str) +{ + m_entries.insert(m_entries.begin(),str); + selection_odds.insert(selection_odds.begin(), false); +} + +void ListBox::RemoveItem(unsigned index) +{ + m_entries.erase(m_entries.begin()+index); + selection_odds.erase(selection_odds.begin()+index); +} + +void ListBox::Clear() +{ + m_entries.clear(); + selection_odds.clear(); +} + +void ListBox::HandleMouseEnter( int /*x*/, int /*y*/ ) { + widget_hover = true; +} + +void ListBox::HandleMouseLeave( int /*x*/, int /*y*/ ) { + widget_hover = false; + pressed_on_widget = false; + hovered_element = -1; + Invalidate(); +} + +void ListBox::HandleMouseMoveEvent( int x, int y ) { + if( ( x == std::numeric_limits::min() ) || ( y == std::numeric_limits::min() ) ) { + pressed_on_widget = false; return; + } + + if(widget_hover) + { + int hov_el_cpy = hovered_element; + hovered_element = (h?((y-padding+1)/(h+padding*2)-1):0); + if(pressed_on_widget && hov_el_cpy!=hovered_element) + { + if(multiselect){ selection_end=hovered_element; } + else { selection_begin=selection_end=hovered_element; } + } + Invalidate(); + + } +} + +void ListBox::HandleMouseButtonEvent( sf::Mouse::Button button, bool press, int /*x*/, int /*y*/ ) { + if(press && button==sf::Mouse::Left && hovered_element != -1) + { + pressed_on_widget = true; + bool shift = sf::Keyboard::isKeyPressed(sf::Keyboard::LShift) && !sf::Keyboard::isKeyPressed(sf::Keyboard::LControl); + bool control = sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) && !sf::Keyboard::isKeyPressed(sf::Keyboard::LShift); + if(multiselect && shift && selection_begin!=-1) + { + if(odds_last_sel==-1) + { + selection_end = hovered_element; + int beg = std::min(selection_begin, selection_end); + int end = std::max(selection_begin, selection_end); + for (std::vector::iterator it = selection_odds.begin()+beg; it != selection_odds.begin()+end; ++it) + *it = false; + } + else + { + selection_begin = odds_last_sel; + odds_last_sel = -1; + selection_end = hovered_element; + for (std::vector::iterator it = selection_odds.begin(); it != selection_odds.end(); ++it) + *it = false; + } + + } + else if(multiselect && control && selection_begin!=-1) + { + selection_odds[hovered_element] = !selection_odds[hovered_element]; + odds_last_sel = hovered_element; + } + else + { + selection_begin = selection_end = hovered_element; + for (std::vector::iterator it = selection_odds.begin(); it != selection_odds.end(); ++it) + *it = false; + } + Invalidate(); + } + if(pressed_on_widget && !press && button==sf::Mouse::Left && hovered_element != -1) + { + pressed_on_widget = false; + GetSignals().Emit( OnSelect ); + } +} + + +}