]> git.tdb.fi Git - libs/gltk.git/commitdiff
More flexible storage for Lsit and Dropdown items
authorMikko Rasa <tdb@tdb.fi>
Mon, 10 Jun 2013 19:58:28 +0000 (22:58 +0300)
committerMikko Rasa <tdb@tdb.fi>
Mon, 10 Jun 2013 19:58:28 +0000 (22:58 +0300)
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
source/dropdown.h
source/list.cpp
source/list.h
source/listdata.h [new file with mode: 0644]

index 6ec88fe10e30ad8be3e0dc89279f48b448705b6f..fcf35babdf132234476f9989eeac7c37a83a583e 100644 (file)
@@ -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<data.size(); ++i)
+               {
+                       unsigned w = static_cast<unsigned>(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<unsigned>((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<Dropdown &>(obj).append(str);
+       dynamic_cast<BasicListData<string> &>(dynamic_cast<Dropdown &>(obj).list.get_data()).append(v);
 }
 
 } // namespace GLtk
index 166eebf53da3943040c2565b9f4d471cca7d9919..fd98f11ddfa2d4e1b8940dfb37f1a639f6790d00 100644 (file)
@@ -21,7 +21,7 @@ public:
                void item(const std::string &);
        };
 
-       sigc::signal<void, int, const std::string &> signal_item_selected;
+       sigc::signal<void, unsigned> 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
index 218b26bd1410b82d7f33fb80c0dd6cbfa3bf86f1..8c26ef745815ccdfa95540dadba1641a0eabde32 100644 (file)
@@ -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<string>),
+       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<string>::iterator i=items.begin(); i!=items.end(); ++i)
+               unsigned n_items = data->size();
+               for(unsigned i=0; i<n_items; ++i)
                {
-                       unsigned w = static_cast<unsigned>(font.get_string_width(*i)*font_size);
+                       unsigned w = static_cast<unsigned>(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<int>(i))
-               --sel_index;
-       else if(sel_index==static_cast<int>(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<static_cast<int>(items.size()))
+       else if(i<static_cast<int>(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; (i<n_visible && first+i<items.size()); ++i)
+               for(unsigned i=0; (i<n_visible && first+i<data->size()); ++i)
                {
                        if(i!=0)
                                bld.matrix() *= GL::Matrix::translation(0, -static_cast<int>(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(i<n_visible && first+i<items.size())
+               if(i<n_visible && first+i<data->size())
                {
                        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<unsigned>(value);
+               first = data->size()-n_visible-static_cast<unsigned>(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<int>(i))
+               ++list.sel_index;
+
+       list.items_changed();
+}
+
+void List::DataObserver::item_removed(unsigned i)
+{
+       if(list.sel_index>static_cast<int>(i))
+               --list.sel_index;
+       else if(list.sel_index==static_cast<int>(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<List &>(obj).append(v);
+       dynamic_cast<BasicListData<string> &>(*dynamic_cast<List &>(obj).data).append(v);
 }
 
 } // namespace GLtk
index 1c7a65dc161e0eae62011cbad1a50a14b06014d7..1ecd854c26e048ddd9a10ba6178d2752e5f2b494 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <sigc++/signal.h>
 #include "container.h"
+#include "listdata.h"
 #include "vslider.h"
 
 namespace Msp {
@@ -23,10 +24,29 @@ public:
                void item(const std::string &);
        };
 
-       sigc::signal<void, unsigned, const std::string &> 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<void, unsigned> signal_item_selected;
 
 private:
-       std::vector<std::string> 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 (file)
index 0000000..38706f8
--- /dev/null
@@ -0,0 +1,103 @@
+#ifndef MSP_GLTK_LISTDATA_H_
+#define MSP_GLTK_LISTDATA_H_
+
+#include <string>
+#include <vector>
+#include <sigc++/signal.h>
+#include <msp/strings/lexicalcast.h>
+
+namespace Msp {
+namespace GLtk {
+
+class ListData
+{
+public:
+       sigc::signal<void, unsigned> signal_item_added;
+       sigc::signal<void, unsigned> signal_item_removed;
+       sigc::signal<void> signal_cleared;
+       sigc::signal<void> 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<typename T>
+class ListDataStore: public ListData
+{
+protected:
+       std::vector<T> 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<typename T>
+class BasicListData: public ListDataStore<T>
+{
+public:
+       virtual std::string get_string(unsigned i) const
+       { return lexical_cast<std::string>(this->get(i)); }
+};
+
+template<typename T>
+class FunctionListData: public ListDataStore<T>
+{
+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