]> git.tdb.fi Git - libs/gltk.git/commitdiff
Turn List items into widgets
authorMikko Rasa <tdb@tdb.fi>
Tue, 11 Jun 2013 19:33:25 +0000 (22:33 +0300)
committerMikko Rasa <tdb@tdb.fi>
Tue, 11 Jun 2013 19:33:25 +0000 (22:33 +0300)
This takes somewhat more resources, but offers much greader flexibility.
Items can be styled more freely and can contain pretty much anything.

source/list.cpp
source/list.h

index 8c26ef745815ccdfa95540dadba1641a0eabde32..378bf707c1a7d702f2192d50b0da8cd2ea70d9bc 100644 (file)
@@ -30,9 +30,7 @@ void List::init()
 {
        sel_index = -1;
        first = 0;
-       n_visible = 1;
-       row_height = 1;
-       items_part = 0;
+       max_scroll = 0;
 
        observer = new DataObserver(*this);
 
@@ -60,41 +58,25 @@ void List::autosize_rows(unsigned n)
 
        Widget::autosize();
 
-       if(items_part)
+       if(const Part *items_part = style->get_part("items"))
        {
                const Sides &margin = items_part->get_margin();
-               const GL::Font &font = style->get_font();
-               float font_size = style->get_font_size();
 
                unsigned max_w = 0;
-               unsigned n_items = data->size();
-               for(unsigned i=0; i<n_items; ++i)
+               unsigned total_h = 0;
+               for(unsigned i=0; (i<n && i<items.size()); ++i)
                {
-                       unsigned w = static_cast<unsigned>(font.get_string_width(data->get_string(i))*font_size);
-                       max_w = max(max_w, w);
+                       items[i]->autosize();
+                       const Geometry &igeom = items[i]->get_geometry();
+                       max_w = max(max_w, igeom.w);
+                       total_h += igeom.h;
                }
 
-               geom.w = max(geom.w, max_w+margin.left+margin.right);
-               geom.h = max(geom.h, n*row_height+margin.top+margin.bottom);
-       }
+               if(items.size()<n)
+                       total_h = total_h*n/items.size();
 
-       if(const Part *slider_part = style->get_part("slider"))
-       {
-               Geometry sgeom = slider_part->get_geometry();
-               if(!sgeom.w || !sgeom.h)
-               {
-                       slider.autosize();
-                       if(!sgeom.w)
-                               sgeom.w = slider.get_geometry().w;
-                       if(!sgeom.h)
-                               sgeom.h = slider.get_geometry().h;
-               }
-
-               const Sides &margin = slider_part->get_margin();
-               geom.w = max(geom.w, sgeom.w+margin.left+margin.right);
-               geom.h = max(geom.h, sgeom.h+margin.top+margin.bottom);
-
-               reposition_slider();
+               geom.w = max(geom.w, max_w+margin.left+margin.right);
+               geom.h = max(geom.h, total_h+margin.top+margin.bottom);
        }
 
        check_view_range();
@@ -123,105 +105,60 @@ void List::items_changed()
 {
        check_view_range();
        signal_autosize_changed.emit();
-       rebuild();
+       reposition_items();
+}
+
+List::Item *List::create_item(unsigned index)
+{
+       return new BasicItem(data->get_string(index));
 }
 
 void List::set_selected_index(int i)
 {
+       if(i>static_cast<int>(data->size()))
+               throw out_of_range("List::set_selected_index");
+
+       if(sel_index>=0)
+               items[sel_index]->set_active(false);
        if(i<0)
                sel_index = -1;
-       else if(i<static_cast<int>(data->size()))
+       else
        {
                sel_index = i;
+               items[sel_index]->set_active(true);
                signal_item_selected.emit(sel_index);
-               rebuild();
        }
-       else
-               throw out_of_range("List::set_selected_index");
 }
 
-void List::rebuild_special(const Part &part, CachedPart &cache)
+void List::render_special(const Part &part, GL::Renderer &renderer) const
 {
        if(part.get_name()=="items")
        {
-               const Sides &margin = part.get_margin();
-               Geometry pgeom = geom;
-               pgeom.h = row_height+margin.top+margin.bottom;
-
-               const GL::Font &font = style->get_font();
-               cache.texture = &font.get_texture();
-               cache.clear_mesh();
-
-               GL::MeshBuilder bld(*cache.mesh);
-               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<data->size()); ++i)
-               {
-                       if(i!=0)
-                               bld.matrix() *= GL::Matrix::translation(0, -static_cast<int>(row_height), 0);
-
-                       GL::MatrixStack::Push _pushm(bld.matrix());
-                       bld.matrix() *= GL::Matrix::scaling(style->get_font_size());
-
-                       style->get_font().build_string(data->get_string(first+i), bld);
-               }
+               for(unsigned i=first; (i<items.size() && items[i]->is_visible()); ++i)
+                       items[i]->render(renderer);
        }
-       else if(part.get_name()=="selection")
-       {
-               if(sel_index>=static_cast<int>(first) && sel_index<static_cast<int>(first+n_visible))
-               {
-                       const Sides &margin = part.get_margin();
-
-                       Geometry pgeom = geom;
-                       pgeom.h = row_height;
-                       pgeom.w -= margin.left+margin.right;
-
-                       Geometry rgeom = part.get_geometry();
-                       rgeom.y += geom.h-margin.top-row_height*(sel_index-first+1);
-                       rgeom.x += margin.left;
-                       part.get_alignment().apply(rgeom, pgeom);
-
-                       cache.texture = part.get_graphic(state)->get_texture();
-                       cache.clear_mesh();
-
-                       GL::MeshBuilder bld(*cache.mesh);
-                       bld.matrix() *= GL::Matrix::translation(rgeom.x, rgeom.y, 0);
-                       part.get_graphic(state)->build(rgeom.w, rgeom.h, bld);
-               }
-               else
-                       cache.texture = 0;
-       }
-}
-
-void List::render_special(const Part &part, GL::Renderer &renderer) const
-{
-       if(part.get_name()=="slider")
+       else if(part.get_name()=="slider")
                slider.render(renderer);
 }
 
 void List::button_press(int x, int y, unsigned btn)
 {
        Container::button_press(x, y, btn);
-       if(!click_focus && btn==1)
+       if(click_focus && btn==1)
        {
-               if(items_part)
-                       y += items_part->get_margin().top;
-
-               unsigned i = (geom.h-1-y)/row_height;
-               if(i<n_visible && first+i<data->size())
-               {
-                       sel_index = first+i;
-
-                       signal_item_selected.emit(sel_index);
-                       rebuild();
-               }
+               for(unsigned i=first; (i<items.size() && items[i]->is_visible()); ++i)
+                       if(click_focus==items[i])
+                       {
+                               set_selected_index(i);
+                               break;
+                       }
        }
 }
 
 void List::on_geometry_change()
 {
        reposition_slider();
+       reposition_items();
 
        check_view_range();
 }
@@ -229,17 +166,10 @@ void List::on_geometry_change()
 void List::on_style_change()
 {
        if(!style)
-       {
-               items_part = 0;
                return;
-       }
 
        reposition_slider();
-
-       items_part = style->get_part("items");
-
-       const GL::Font &font = style->get_font();
-       row_height = static_cast<unsigned>((font.get_ascent()-font.get_descent())*style->get_font_size());
+       reposition_items();
 
        check_view_range();
 }
@@ -257,43 +187,78 @@ void List::reposition_slider()
        }
 }
 
-void List::check_view_range()
+void List::reposition_items()
 {
-       unsigned h = geom.h;
-       if(items_part)
+       if(!style)
+               return;
+
+       if(const Part *items_part = style->get_part("items"))
        {
                const Sides &margin = items_part->get_margin();
-               h -= margin.top+margin.bottom;
+               unsigned w = geom.w-margin.left-margin.right;
+               unsigned y = geom.h-margin.top;
+               for(unsigned i=0; i<items.size(); ++i)
+               {
+                       if(i<first || !y)
+                               items[i]->set_visible(false);
+                       else
+                       {
+                               Geometry igeom = items[i]->get_geometry();
+                               if(igeom.h+margin.bottom<=y)
+                               {
+                                       items[i]->set_visible(true);
+                                       y -= igeom.h;
+                                       igeom.x = margin.left;
+                                       igeom.y = y;
+                                       igeom.w = w;
+                                       items[i]->set_geometry(igeom);
+                               }
+                               else
+                               {
+                                       items[i]->set_visible(false);
+                                       y = 0;
+                               }
+                       }
+               }
        }
+}
 
-       n_visible = h/row_height;
+void List::check_view_range()
+{
+       if(!style)
+               return;
 
-       if(first+n_visible>data->size())
+       unsigned h = geom.h;
+       if(const Part *items_part = style->get_part("items"))
        {
-               if(data->size()>n_visible)
-                       first = data->size()-n_visible;
-               else
-                       first = 0;
+               const Sides &margin = items_part->get_margin();
+               h -= margin.top+margin.bottom;
        }
 
-       if(data->size()>n_visible)
+       max_scroll = items.size();
+       for(unsigned i=items.size(); i-->0; )
        {
-               slider.set_range(0, data->size()-n_visible);
-               slider.set_value(data->size()-n_visible-first);
-       }
-       else
-       {
-               slider.set_range(0, 0);
-               slider.set_value(0);
+               unsigned ih = items[i]->get_geometry().h;
+               if(ih<=h)
+               {
+                       h -= ih;
+                       --max_scroll;
+               }
        }
+
+       if(first>max_scroll)
+               first = max_scroll;
+
+       slider.set_range(0, max_scroll);
+       slider.set_value(max_scroll-first);
 }
 
 void List::slider_value_changed(double value)
 {
-       if(data->size()>n_visible)
+       if(max_scroll>0)
        {
-               first = data->size()-n_visible-static_cast<unsigned>(value);
-               rebuild();
+               first = max_scroll-static_cast<unsigned>(value);
+               reposition_items();
        }
 }
 
@@ -312,6 +277,9 @@ void List::DataObserver::item_added(unsigned i)
        if(list.sel_index>=static_cast<int>(i))
                ++list.sel_index;
 
+       Item *item = list.create_item(i);
+       list.add(*item);
+       list.items.insert(list.items.begin()+i, item);
        list.items_changed();
 }
 
@@ -322,12 +290,17 @@ void List::DataObserver::item_removed(unsigned i)
        else if(list.sel_index==static_cast<int>(i))
                list.sel_index = -1;
 
+       delete list.items[i];
+       list.items.erase(list.items.begin()+i);
        list.items_changed();
 }
 
 void List::DataObserver::cleared()
 {
        list.sel_index = -1;
+       for(vector<Item *>::iterator i=list.items.begin(); i!=list.items.end(); ++i)
+               delete *i;
+       list.items.clear();
        list.items_changed();
 }
 
@@ -343,6 +316,58 @@ List::Loader::Loader(List &l):
        add("item", &Loader::item);
 }
 
+
+void List::Item::autosize()
+{
+       Widget::autosize();
+
+       if(const Part *part = style->get_part("children"))
+       {
+               const Sides &margin = part->get_margin();
+               for(list<Child *>::const_iterator i=children.begin(); i!=children.end(); ++i)
+               {
+                       const Geometry &cgeom = (*i)->widget->get_geometry();
+                       geom.w = max(geom.w, cgeom.x+cgeom.w+margin.right);
+                       geom.h = max(geom.h, cgeom.y+cgeom.h+margin.top);
+               }
+       }
+}
+
+void List::Item::set_active(bool a)
+{
+       set_state(ACTIVE, (a ? ACTIVE : NORMAL));
+}
+
+void List::Item::render_special(const Part &part, GL::Renderer &renderer) const
+{
+       if(part.get_name()=="children")
+       {
+               for(list<Child *>::const_iterator i=children.begin(); i!=children.end(); ++i)
+                       (*i)->widget->render(renderer);
+       }
+}
+
+
+List::BasicItem::BasicItem(const string &text):
+       label(text)
+{
+       add(label);
+}
+
+void List::BasicItem::on_style_change()
+{
+       if(!style)
+               return;
+
+       label.autosize();
+       if(const Part *part = style->get_part("children"))
+       {
+               const Sides &margin = part->get_margin();
+               label.set_position(margin.left, margin.bottom);
+       }
+}
+
+
 void List::Loader::item(const string &v)
 {
        dynamic_cast<BasicListData<string> &>(*dynamic_cast<List &>(obj).data).append(v);
index 1ecd854c26e048ddd9a10ba6178d2752e5f2b494..caf403a84a0f1b5ddaf39f8f0ce9e70a5ce71401 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <sigc++/signal.h>
 #include "container.h"
+#include "label.h"
 #include "listdata.h"
 #include "vslider.h"
 
@@ -40,6 +41,31 @@ private:
                void refresh_strings();
        };
 
+protected:
+       class Item: public Container
+       {
+       public:
+               virtual const char *get_class() const { return "listitem"; }
+
+               virtual void autosize();
+               void set_active(bool);
+
+               virtual void render_special(const Part &, GL::Renderer &) const;
+       };
+
+public:
+       class BasicItem: public Item
+       {
+       private:
+               Label label;
+
+       public:
+               BasicItem(const std::string &);
+
+       private:
+               virtual void on_style_change();
+       };
+
 public:
        sigc::signal<void, unsigned> signal_item_selected;
 
@@ -49,12 +75,10 @@ private:
        DataObserver *observer;
        int sel_index;
        unsigned first;
-       unsigned n_visible;
-       unsigned row_height;
-
-       const Part *items_part;
+       unsigned max_scroll;
 
        VSlider slider;
+       std::vector<Item *> items;
 
 public:
        List();
@@ -75,13 +99,14 @@ public:
        const ListData &get_data() const { return *data; }
 private:
        void items_changed();
+protected:
+       virtual Item *create_item(unsigned);
 public:
 
        void set_selected_index(int);
        int get_selected_index() const { return sel_index; }
 
 private:
-       virtual void rebuild_special(const Part &, CachedPart &);
        virtual void render_special(const Part &, GL::Renderer &) const;
 
 public:
@@ -91,6 +116,7 @@ private:
        virtual void on_style_change();
 
        void reposition_slider();
+       void reposition_items();
        void check_view_range();
        void slider_value_changed(double);
 };