From 1d84ac50711ce9d23cb1dafd83158d4ffa938a15 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 10 Jun 2013 22:58:28 +0300 Subject: [PATCH] More flexible storage for Lsit and Dropdown items In many cases these widgets are used to pick an object. Previously, the calling code needed to explicitly maintain synchronization between the objects and list contents. This change allows the list to actually hold pointers to the objects, making the retrieval of the selected object vastly easier. --- source/dropdown.cpp | 79 +++++++++------------- source/dropdown.h | 21 +++--- source/list.cpp | 155 +++++++++++++++++++++++++++----------------- source/list.h | 38 ++++++++--- source/listdata.h | 103 +++++++++++++++++++++++++++++ 5 files changed, 269 insertions(+), 127 deletions(-) create mode 100644 source/listdata.h diff --git a/source/dropdown.cpp b/source/dropdown.cpp index 6ec88fe..fcf35ba 100644 --- a/source/dropdown.cpp +++ b/source/dropdown.cpp @@ -12,9 +12,21 @@ using namespace std; namespace Msp { namespace GLtk { -Dropdown::Dropdown(): - dropped(false) +Dropdown::Dropdown() { + init(); +} + +Dropdown::Dropdown(ListData &d): + list(d) +{ + init(); +} + +void Dropdown::init() +{ + dropped = false; + add(list); list.signal_item_selected.connect(sigc::mem_fun(this, &Dropdown::list_item_selected)); list.signal_autosize_changed.connect(sigc::mem_fun(this, &Dropdown::list_autosize_changed)); @@ -34,6 +46,16 @@ void Dropdown::autosize() const Sides &margin = text_part->get_margin(); const GL::Font &font = style->get_font(); float font_size = style->get_font_size(); + + unsigned max_w = 0; + const ListData &data = list.get_data(); + for(unsigned i=0; i(font.get_string_width(data.get_string(i))*font_size); + max_w = max(max_w, w); + } + geom.w = max(geom.w, max_w+margin.left+margin.right); + unsigned line_height = static_cast((font.get_ascent()-font.get_descent())*font_size); geom.h = max(geom.h, line_height+margin.top+margin.bottom); } @@ -41,52 +63,13 @@ void Dropdown::autosize() rebuild(); } -void Dropdown::append(const string &item) -{ - list.append(item); -} - -void Dropdown::insert(unsigned i, const string &v) -{ - list.insert(i, v); -} - -void Dropdown::remove(unsigned i) -{ - list.remove(i); -} - -void Dropdown::clear() -{ - list.clear(); -} - -unsigned Dropdown::get_n_items() const -{ - return list.get_n_items(); -} - -void Dropdown::set_selected_index(int i) -{ - list.set_selected_index(i); -} - -const string &Dropdown::get_selected() const -{ - return list.get_selected(); -} - -int Dropdown::get_selected_index() const -{ - return list.get_selected_index(); -} - void Dropdown::rebuild_special(const Part &part, CachedPart &cache) { if(part.get_name()=="text") { - if(list.get_selected_index()>=0) - Text(*style, list.get_selected()).build(part, geom, cache); + int sel = list.get_selected_index(); + if(sel>=0) + Text(*style, list.get_data().get_string(sel)).build(part, geom, cache); else cache.texture = 0; } @@ -164,7 +147,7 @@ void Dropdown::resize_list() list.set_geometry(lgeom); } -void Dropdown::list_item_selected(unsigned index, const std::string &item) +void Dropdown::list_item_selected(unsigned index) { if(dropped) { @@ -173,7 +156,7 @@ void Dropdown::list_item_selected(unsigned index, const std::string &item) signal_ungrab_pointer.emit(); } - signal_item_selected.emit(index, item); + signal_item_selected.emit(index); rebuild(); } @@ -184,9 +167,9 @@ Dropdown::Loader::Loader(Dropdown &d): add("item", &Loader::item); } -void Dropdown::Loader::item(const string &str) +void Dropdown::Loader::item(const string &v) { - dynamic_cast(obj).append(str); + dynamic_cast &>(dynamic_cast(obj).list.get_data()).append(v); } } // namespace GLtk diff --git a/source/dropdown.h b/source/dropdown.h index 166eebf..fd98f11 100644 --- a/source/dropdown.h +++ b/source/dropdown.h @@ -21,7 +21,7 @@ public: void item(const std::string &); }; - sigc::signal signal_item_selected; + sigc::signal signal_item_selected; private: List list; @@ -29,20 +29,21 @@ private: public: Dropdown(); + Dropdown(ListData &); +private: + void init(); +public: virtual const char *get_class() const { return "dropdown"; } virtual void autosize(); - void append(const std::string &); - void insert(unsigned, const std::string &); - void remove(unsigned); - void clear(); - unsigned get_n_items() const; + void set_data(ListData &d) { list.set_data(d); } + ListData &get_data() { return list.get_data(); } + const ListData &get_data() const { return list.get_data(); } - void set_selected_index(int); - const std::string &get_selected() const; - int get_selected_index() const; + void set_selected_index(int i) { list.set_selected_index(i); } + int get_selected_index() const { return list.get_selected_index(); } private: virtual void rebuild_special(const Part &, CachedPart &); @@ -56,7 +57,7 @@ private: void list_autosize_changed(); void resize_list(); - void list_item_selected(unsigned, const std::string &); + void list_item_selected(unsigned); }; } // namespace GLtk diff --git a/source/list.cpp b/source/list.cpp index 218b26b..8c26ef7 100644 --- a/source/list.cpp +++ b/source/list.cpp @@ -13,17 +13,41 @@ namespace Msp { namespace GLtk { List::List(): - sel_index(-1), - first(0), - n_visible(1), - row_height(1), - items_part(0) + data(new BasicListData), + own_data(true) { + init(); +} + +List::List(ListData &d): + data(&d), + own_data(false) +{ + init(); +} + +void List::init() +{ + sel_index = -1; + first = 0; + n_visible = 1; + row_height = 1; + items_part = 0; + + observer = new DataObserver(*this); + add(slider); slider.set_step(1); slider.signal_value_changed.connect(sigc::mem_fun(this, &List::slider_value_changed)); } +List::~List() +{ + delete observer; + if(own_data) + delete data; +} + void List::autosize() { autosize_rows(5); @@ -43,9 +67,10 @@ void List::autosize_rows(unsigned n) float font_size = style->get_font_size(); unsigned max_w = 0; - for(vector::iterator i=items.begin(); i!=items.end(); ++i) + unsigned n_items = data->size(); + for(unsigned i=0; i(font.get_string_width(*i)*font_size); + unsigned w = static_cast(font.get_string_width(data->get_string(i))*font_size); max_w = max(max_w, w); } @@ -78,45 +103,22 @@ void List::autosize_rows(unsigned n) void List::autosize_all() { - autosize_rows(items.size()); + autosize_rows(data->size()); } -void List::append(const string &v) +void List::set_data(ListData &d) { - items.push_back(v); - items_changed(); -} - -void List::insert(unsigned i, const string &v) -{ - if(i>items.size()) - throw out_of_range("List::insert"); - - items.insert(items.begin()+i, v); - items_changed(); -} + delete observer; + if(own_data) + delete data; -void List::remove(unsigned i) -{ - if(i>items.size()) - throw out_of_range("List::remove"); - - items.erase(items.begin()+i); - if(sel_index>static_cast(i)) - --sel_index; - else if(sel_index==static_cast(i)) - sel_index = -1; + data = &d; + own_data = false; + observer = new DataObserver(*this); items_changed(); } -void List::clear() -{ - items.clear(); - sel_index = -1; - items_changed(); -} - void List::items_changed() { check_view_range(); @@ -128,24 +130,16 @@ void List::set_selected_index(int i) { if(i<0) sel_index = -1; - else if(i(items.size())) + else if(i(data->size())) { sel_index = i; - signal_item_selected.emit(sel_index, items[sel_index]); + signal_item_selected.emit(sel_index); rebuild(); } else throw out_of_range("List::set_selected_index"); } -const string &List::get_selected() const -{ - if(sel_index<0) - throw logic_error("sel_index<0"); - - return items[sel_index]; -} - void List::rebuild_special(const Part &part, CachedPart &cache) { if(part.get_name()=="items") @@ -162,7 +156,7 @@ void List::rebuild_special(const Part &part, CachedPart &cache) bld.color(style->get_font_color()); bld.matrix() *= GL::Matrix::translation(margin.left, geom.h-pgeom.h-font.get_descent()*style->get_font_size(), 0); - for(unsigned i=0; (isize()); ++i) { if(i!=0) bld.matrix() *= GL::Matrix::translation(0, -static_cast(row_height), 0); @@ -170,7 +164,7 @@ void List::rebuild_special(const Part &part, CachedPart &cache) GL::MatrixStack::Push _pushm(bld.matrix()); bld.matrix() *= GL::Matrix::scaling(style->get_font_size()); - style->get_font().build_string(items[first+i], bld); + style->get_font().build_string(data->get_string(first+i), bld); } } else if(part.get_name()=="selection") @@ -215,11 +209,11 @@ void List::button_press(int x, int y, unsigned btn) y += items_part->get_margin().top; unsigned i = (geom.h-1-y)/row_height; - if(isize()) { sel_index = first+i; - signal_item_selected.emit(sel_index, items[sel_index]); + signal_item_selected.emit(sel_index); rebuild(); } } @@ -274,18 +268,18 @@ void List::check_view_range() n_visible = h/row_height; - if(first+n_visible>items.size()) + if(first+n_visible>data->size()) { - if(items.size()>n_visible) - first = items.size()-n_visible; + if(data->size()>n_visible) + first = data->size()-n_visible; else first = 0; } - if(items.size()>n_visible) + if(data->size()>n_visible) { - slider.set_range(0, items.size()-n_visible); - slider.set_value(items.size()-n_visible-first); + slider.set_range(0, data->size()-n_visible); + slider.set_value(data->size()-n_visible-first); } else { @@ -296,14 +290,53 @@ void List::check_view_range() void List::slider_value_changed(double value) { - if(items.size()>n_visible) + if(data->size()>n_visible) { - first = items.size()-n_visible-static_cast(value); + first = data->size()-n_visible-static_cast(value); rebuild(); } } +List::DataObserver::DataObserver(List &l): + list(l) +{ + list.data->signal_item_added.connect(sigc::mem_fun(this, &DataObserver::item_added)); + list.data->signal_item_removed.connect(sigc::mem_fun(this, &DataObserver::item_removed)); + list.data->signal_cleared.connect(sigc::mem_fun(this, &DataObserver::cleared)); + list.data->signal_refresh_strings.connect(sigc::mem_fun(this, &DataObserver::refresh_strings)); +} + +void List::DataObserver::item_added(unsigned i) +{ + if(list.sel_index>=static_cast(i)) + ++list.sel_index; + + list.items_changed(); +} + +void List::DataObserver::item_removed(unsigned i) +{ + if(list.sel_index>static_cast(i)) + --list.sel_index; + else if(list.sel_index==static_cast(i)) + list.sel_index = -1; + + list.items_changed(); +} + +void List::DataObserver::cleared() +{ + list.sel_index = -1; + list.items_changed(); +} + +void List::DataObserver::refresh_strings() +{ + list.items_changed(); +} + + List::Loader::Loader(List &l): Widget::Loader(l) { @@ -312,7 +345,7 @@ List::Loader::Loader(List &l): void List::Loader::item(const string &v) { - dynamic_cast(obj).append(v); + dynamic_cast &>(*dynamic_cast(obj).data).append(v); } } // namespace GLtk diff --git a/source/list.h b/source/list.h index 1c7a65d..1ecd854 100644 --- a/source/list.h +++ b/source/list.h @@ -3,6 +3,7 @@ #include #include "container.h" +#include "listdata.h" #include "vslider.h" namespace Msp { @@ -23,10 +24,29 @@ public: void item(const std::string &); }; - sigc::signal signal_item_selected; +private: + /// This exists to make disconnecting signals easier + class DataObserver: public sigc::trackable + { + private: + List &list; + + public: + DataObserver(List &); + + void item_added(unsigned); + void item_removed(unsigned); + void cleared(); + void refresh_strings(); + }; + +public: + sigc::signal signal_item_selected; private: - std::vector items; + ListData *data; + bool own_data; + DataObserver *observer; int sel_index; unsigned first; unsigned n_visible; @@ -38,6 +58,11 @@ private: public: List(); + List(ListData &); +private: + void init(); +public: + virtual ~List(); virtual const char *get_class() const { return "list"; } @@ -45,17 +70,14 @@ public: void autosize_rows(unsigned); void autosize_all(); - void append(const std::string &); - void insert(unsigned, const std::string &); - void remove(unsigned); - void clear(); + void set_data(ListData &); + ListData &get_data() { return *data; } + const ListData &get_data() const { return *data; } private: void items_changed(); public: - unsigned get_n_items() const { return items.size(); } void set_selected_index(int); - const std::string &get_selected() const; int get_selected_index() const { return sel_index; } private: diff --git a/source/listdata.h b/source/listdata.h new file mode 100644 index 0000000..38706f8 --- /dev/null +++ b/source/listdata.h @@ -0,0 +1,103 @@ +#ifndef MSP_GLTK_LISTDATA_H_ +#define MSP_GLTK_LISTDATA_H_ + +#include +#include +#include +#include + +namespace Msp { +namespace GLtk { + +class ListData +{ +public: + sigc::signal signal_item_added; + sigc::signal signal_item_removed; + sigc::signal signal_cleared; + sigc::signal signal_refresh_strings; + +protected: + ListData() { } +public: + virtual ~ListData() { } + + virtual unsigned size() const = 0; + virtual std::string get_string(unsigned) const = 0; + void refresh_strings() const { signal_refresh_strings.emit(); } +}; + +template +class ListDataStore: public ListData +{ +protected: + std::vector items; + + ListDataStore() { } + +public: + void append(const T &v) { insert(items.size(), v); } + + void insert(unsigned i, const T & v) + { + if(i>items.size()) + throw std::out_of_range("ListDataStore::insert"); + + items.insert(items.begin()+i, v); + signal_item_added.emit(i); + } + + const T &get(unsigned i) const + { + if(i>=items.size()) + throw std::out_of_range("ListDataStore::get"); + + return items[i]; + } + + void remove(unsigned i) + { + if(i>=items.size()) + throw std::out_of_range("ListDataStore::remove"); + + items.erase(items.begin()+i); + signal_item_removed.emit(i); + } + + void clear() + { + items.clear(); + signal_cleared.emit(); + } + + virtual unsigned size() const { return items.size(); } +}; + +template +class BasicListData: public ListDataStore +{ +public: + virtual std::string get_string(unsigned i) const + { return lexical_cast(this->get(i)); } +}; + +template +class FunctionListData: public ListDataStore +{ +public: + typedef std::string Func(const T &); + +private: + Func *func; + +public: + FunctionListData(Func f): func(f) { } + + virtual std::string get_string(unsigned i) const + { return func(this->get(i)); } +}; + +} // namespace GLtk +} // namespace Msp + +#endif -- 2.43.0