]> git.tdb.fi Git - libs/gltk.git/blobdiff - source/list.cpp
Rework how widget ownership works in Container
[libs/gltk.git] / source / list.cpp
index 963b442775f982840f27b6d796437f02227e9fac..a028571c5a1eabd0f56c43a8367b65be7b8b048d 100644 (file)
@@ -20,15 +20,19 @@ incompatible_data::incompatible_data(const type_info &ti):
 { }
 
 
-List::List():
-       List(*new BasicListData<string>)
+List::List(unique_ptr<ListData> d):
+       List(*d)
 {
-       own_data = true;
+       own_data = move(d);
 }
 
+List::List():
+       List(make_unique<BasicListData<string>>())
+{ }
+
 List::List(ListData &d):
        data(&d),
-       observer(new DataObserver(*this))
+       observer(make_unique<DataObserver>(*this))
 {
        input_type = INPUT_NAVIGATION;
 
@@ -37,14 +41,6 @@ List::List(ListData &d):
        slider.signal_value_changed.connect(sigc::mem_fun(this, &List::slider_value_changed));
 }
 
-List::~List()
-{
-       delete item_factory;
-       delete observer;
-       if(own_data)
-               delete data;
-}
-
 void List::autosize_special(const Part &part, Geometry &ageom) const
 {
        if(part.get_name()=="items")
@@ -53,7 +49,7 @@ void List::autosize_special(const Part &part, Geometry &ageom) const
 
                unsigned items_w = 0;
                unsigned items_h = 0;
-               for(unsigned i=0; i<items.size(); ++i)
+               for(size_t i=0; i<items.size(); ++i)
                {
                        Geometry igeom;
                        items[i]->autosize(igeom);
@@ -89,23 +85,16 @@ void List::set_data(ListData &d)
        if(item_factory)
                item_factory->set_data(d);
 
-       delete observer;
-       if(own_data)
-               delete data;
+       observer.reset();
+       own_data.reset();
 
        data = &d;
-       own_data = false;
-       observer = new DataObserver(*this);
+       observer = make_unique<DataObserver>(*this);
 
-       for(Item *i: items)
-               delete i;
        items.clear();
-       unsigned n_items = data->size();
-       for(unsigned i=0; i<n_items; ++i)
-       {
-               Item *item = create_item(i);
-               items.push_back(item);
-       }
+       size_t n_items = data->size();
+       for(size_t i=0; i<n_items; ++i)
+               items.emplace_back(create_item(i));
 
        items_changed();
 }
@@ -116,18 +105,18 @@ void List::items_changed()
        mark_rebuild();
 }
 
-List::Item *List::create_item(unsigned index)
+unique_ptr<List::Item> List::create_item(size_t index)
 {
-       Item *item = nullptr;
+       unique_ptr<Item> item;
        if(item_factory)
                item = item_factory->create_item(index);
        else
-               item = new BasicItem(data->get_string(index));
-       if(static_cast<int>(index)==sel_index)
+               item = make_unique<BasicItem>(data->get_string(index));
+       if(index==sel_index)
                item->set_active(true);
        add(*item);
        item->autosize();
-       item->signal_autosize_changed.connect(sigc::bind(sigc::mem_fun(this, &List::item_autosize_changed), item));
+       item->signal_autosize_changed.connect(sigc::bind(sigc::mem_fun(this, &List::item_autosize_changed), item.get()));
        return item;
 }
 
@@ -148,38 +137,37 @@ void List::set_view_all()
        set_view_size(0);
 }
 
-void List::set_selected_index(int i)
+void List::set_selected_index(size_t i)
 {
-       if(i>=static_cast<int>(data->size()))
+       if(i>=data->size() && i!=INVALID_INDEX)
                throw out_of_range("List::set_selected_index");
 
-       if(i==sel_index || (i<0 && sel_index<0))
+       if(i==sel_index)
                return;
 
-       if(sel_index>=0)
+       if(sel_index!=INVALID_INDEX)
                items[sel_index]->set_active(false);
-       if(i<0)
+
+       sel_index = i;
+       focus_index = i;
+       if(i==INVALID_INDEX)
        {
-               sel_index = -1;
-               focus_index = -1;
                set_input_focus(nullptr);
                signal_selection_cleared.emit();
        }
        else
        {
-               sel_index = i;
-               focus_index = i;
                items[sel_index]->set_active(true);
                if(state&FOCUS)
-                       set_input_focus(items[focus_index]);
+                       set_input_focus(items[focus_index].get());
                signal_item_selected.emit(sel_index);
        }
 }
 
 void List::set_selected_item(Widget *item)
 {
-       for(unsigned i=rows[first_row].first; (i<items.size() && items[i]->is_visible()); ++i)
-               if(item==items[i])
+       for(size_t i=rows[first_row].first; (i<items.size() && items[i]->is_visible()); ++i)
+               if(item==items[i].get())
                        return set_selected_index(i);
 }
 
@@ -191,8 +179,8 @@ void List::rebuild_special(const Part &part)
        {
                SetFlag flag(ignore_slider_change);
                reposition_items(true);
-               unsigned old_first_row = first_row;
-               unsigned old_max_scroll = max_scroll;
+               size_t old_first_row = first_row;
+               size_t old_max_scroll = max_scroll;
                check_view_range();
                if(first_row!=old_first_row || max_scroll!=old_max_scroll)
                        reposition_items(false);
@@ -205,7 +193,7 @@ void List::render_special(const Part &part, GL::Renderer &renderer) const
 {
        if(part.get_name()=="items")
        {
-               for(unsigned i=rows[first_row].first; (i<items.size() && items[i]->is_visible()); ++i)
+               for(size_t i=rows[first_row].first; (i<items.size() && items[i]->is_visible()); ++i)
                        items[i]->render(renderer);
        }
        else if(part.get_name()=="slider")
@@ -228,7 +216,7 @@ void List::button_press(int x, int y, unsigned btn)
 {
        if(btn==4 || btn==5)
        {
-               unsigned change = 3;
+               size_t change = 3;
                if(btn==4)
                {
                        change = min(first_row, change);
@@ -305,11 +293,11 @@ void List::touch_motion(int, int y, unsigned finger)
 void List::focus_in()
 {
        Container::focus_in();
-       if(focus_index>=0 && items[focus_index]->is_visible())
-               set_input_focus(items[focus_index]);
+       if(focus_index!=INVALID_INDEX && items[focus_index]->is_visible())
+               set_input_focus(items[focus_index].get());
        else
        {
-               if(sel_index>=0 && items[sel_index]->is_visible())
+               if(sel_index!=INVALID_INDEX && items[sel_index]->is_visible())
                        set_focus_index(sel_index);
                else if(!items.empty())
                        set_focus_index(rows[first_row].first);
@@ -337,7 +325,7 @@ void List::move_focus(Navigation nav, bool select)
 {
        if(nav==NAV_UP && view_mode==GRID)
        {
-               unsigned row = item_index_to_row(focus_index);
+               size_t row = item_index_to_row(focus_index);
                if(row>0)
                        set_focus_index(rows[row-1].first+focus_index-rows[row].first);
                else
@@ -345,7 +333,7 @@ void List::move_focus(Navigation nav, bool select)
        }
        else if(nav==NAV_DOWN && view_mode==GRID)
        {
-               unsigned row = item_index_to_row(focus_index);
+               size_t row = item_index_to_row(focus_index);
                if(row+1<rows.size())
                        set_focus_index(rows[row+1].first+focus_index-rows[row].first);
                else
@@ -358,7 +346,7 @@ void List::move_focus(Navigation nav, bool select)
        }
        else if(nav==NAV_DOWN || (nav==NAV_RIGHT && view_mode==GRID))
        {
-               if(static_cast<unsigned>(focus_index+1)<items.size())
+               if(focus_index+1<items.size())
                        set_focus_index(focus_index+1);
        }
 
@@ -366,14 +354,14 @@ void List::move_focus(Navigation nav, bool select)
                set_selected_index(focus_index);
 }
 
-void List::set_focus_index(int i)
+void List::set_focus_index(size_t i)
 {
        focus_index = i;
-       if(focus_index>=0)
+       if(focus_index!=INVALID_INDEX)
        {
                scroll_to_focus();
                if(state&FOCUS)
-                       set_input_focus(items[focus_index]);
+                       set_input_focus(items[focus_index].get());
        }
 }
 
@@ -400,7 +388,7 @@ void List::reposition_items(bool record_rows)
        unsigned x = 0;
        unsigned y = 0;
        unsigned row_h = 0;
-       for(unsigned i=0; i<items.size(); ++i)
+       for(size_t i=0; i<items.size(); ++i)
        {
                const Geometry &igeom = items[i]->get_geometry();
 
@@ -430,7 +418,7 @@ void List::reposition_items(bool record_rows)
                }
                else
                {
-                       for(unsigned j=rows.back().first; j<=i; ++j)
+                       for(size_t j=rows.back().first; j<=i; ++j)
                                items[j]->set_visible(false);
                        y = 0;
                }
@@ -443,7 +431,7 @@ void List::reposition_items(bool record_rows)
                rows.back().height = row_h;
 }
 
-unsigned List::last_to_first_row(unsigned last) const
+size_t List::last_to_first_row(size_t last) const
 {
        if(!items_part)
                return last;
@@ -452,7 +440,7 @@ unsigned List::last_to_first_row(unsigned last) const
        unsigned view_h = geom.h-min(geom.h, margin.top+margin.bottom);
 
        unsigned items_h = 0;
-       for(unsigned i=last; i<rows.size(); --i)
+       for(size_t i=last; i<rows.size(); --i)
        {
                items_h += rows[i].height;
                if(items_h>view_h)
@@ -462,9 +450,9 @@ unsigned List::last_to_first_row(unsigned last) const
        return 0;
 }
 
-unsigned List::item_index_to_row(unsigned index) const
+size_t List::item_index_to_row(size_t index) const
 {
-       for(unsigned i=0; i+1<rows.size(); ++i)
+       for(size_t i=0; i+1<rows.size(); ++i)
                if(rows[i+1].first>index)
                        return i;
        return rows.size()-1;
@@ -490,10 +478,10 @@ void List::check_view_range()
 
 void List::scroll_to_focus()
 {
-       if(focus_index<0 || items[focus_index]->is_visible())
+       if(focus_index==INVALID_INDEX || items[focus_index]->is_visible())
                return;
 
-       unsigned focus_row = item_index_to_row(static_cast<unsigned>(focus_index));
+       size_t focus_row = item_index_to_row(focus_index);
        if(focus_row<first_row)
                slider.set_value(max_scroll-focus_row);
        else
@@ -504,17 +492,19 @@ void List::slider_value_changed(double value)
 {
        if(max_scroll>0 && !ignore_slider_change)
        {
-               first_row = max_scroll-static_cast<unsigned>(value);
+               first_row = max_scroll-static_cast<size_t>(value);
                mark_rebuild();
        }
 }
 
-void List::adjust_index(int &index, int pos, int change)
+void List::adjust_index(size_t &index, size_t pos, ptrdiff_t change)
 {
-       if(index>pos)
+       if(index==INVALID_INDEX)
+               return;
+       else if(index>pos)
                index += change;
        else if(index==pos)
-               index = (change>0 ? index+change : -1);
+               index = (change>0 ? index+change : INVALID_INDEX);
 }
 
 
@@ -527,47 +517,42 @@ List::DataObserver::DataObserver(List &l):
        list.data->signal_refresh_item.connect(sigc::mem_fun(this, &DataObserver::refresh_item));
 }
 
-void List::DataObserver::item_added(unsigned i)
+void List::DataObserver::item_added(size_t i)
 {
        adjust_index(list.sel_index, i, 1);
        adjust_index(list.focus_index, i, 1);
 
-       Item *item = list.create_item(i);
-       list.items.insert(list.items.begin()+i, item);
+       list.items.insert(list.items.begin()+i, list.create_item(i));
        list.items_changed();
 }
 
-void List::DataObserver::item_removed(unsigned i)
+void List::DataObserver::item_removed(size_t i)
 {
-       bool had_selection = (list.sel_index>=0);
+       bool had_selection = (list.sel_index!=INVALID_INDEX);
        adjust_index(list.sel_index, i, -1);
        adjust_index(list.focus_index, i, -1);
 
-       delete list.items[i];
        list.items.erase(list.items.begin()+i);
        list.items_changed();
 
-       if(had_selection && list.sel_index<0)
+       if(had_selection && list.sel_index==INVALID_INDEX)
                list.signal_selection_cleared.emit();
 }
 
 void List::DataObserver::cleared()
 {
-       list.sel_index = -1;
-       list.focus_index = -1;
-       for(Item *i: list.items)
-               delete i;
+       list.sel_index = INVALID_INDEX;
+       list.focus_index = INVALID_INDEX;
        list.items.clear();
        list.items_changed();
 
        list.signal_selection_cleared.emit();
 }
 
-void List::DataObserver::refresh_item(unsigned i)
+void List::DataObserver::refresh_item(size_t i)
 {
-       delete list.items[i];
-       // Avoid stale pointer while create_item is executing
-       list.items[i] = nullptr;
+       // Destroy the old item before calling create_item
+       list.items[i].reset();
        list.items[i] = list.create_item(i);
        list.items_changed();
 }
@@ -583,7 +568,7 @@ void List::Item::autosize_special(const Part &part, Geometry &ageom) const
        if(part.get_name()=="children")
        {
                const Sides &margin = part.get_margin();
-               for(const Child *c: children)
+               for(const unique_ptr<Child> &c: children)
                {
                        Geometry cgeom;
                        c->widget->autosize(cgeom);
@@ -602,7 +587,7 @@ void List::Item::render_special(const Part &part, GL::Renderer &renderer) const
 {
        if(part.get_name()=="children")
        {
-               for(const Child *c: children)
+               for(const unique_ptr<Child> &c: children)
                        c->widget->render(renderer);
        }
 }
@@ -628,8 +613,8 @@ void List::MultiColumnItem::check_widths(vector<unsigned> &widths) const
        if(widths.size()<children.size())
                widths.resize(children.size(), 0);
 
-       unsigned n = 0;
-       for(const Child *c: children)
+       size_t n = 0;
+       for(const unique_ptr<Child> &c: children)
        {
                Geometry cgeom;
                c->widget->autosize(cgeom);
@@ -650,8 +635,8 @@ void List::MultiColumnItem::set_widths(const vector<unsigned> &widths)
 
        const Sides &margin = part->get_margin();
        int x = margin.left;
-       unsigned n = 0;
-       for(const Child *c: children)
+       size_t n = 0;
+       for(const unique_ptr<Child> &c: children)
        {
                c->widget->set_position(x, margin.bottom);
                x += widths[n++];
@@ -663,26 +648,26 @@ void List::MultiColumnItem::on_style_change()
        if(!style)
                return;
 
-       for(const Child *c: children)
+       for(const unique_ptr<Child> &c: children)
                c->widget->autosize();
 
        vector<unsigned> widths;
        List *list = static_cast<List *>(parent);
-       for(Item *i: list->items)
-               if(i!=this)
-                       if(MultiColumnItem *mci = dynamic_cast<MultiColumnItem *>(i))
+       for(const unique_ptr<Item> &i: list->items)
+               if(i.get()!=this)
+                       if(MultiColumnItem *mci = dynamic_cast<MultiColumnItem *>(i.get()))
                                mci->check_widths(widths);
 
        vector<unsigned> self_widths(widths);
        check_widths(self_widths);
        bool update_all = false;
-       for(unsigned i=0; (!update_all && i<widths.size() && i<self_widths.size()); ++i)
+       for(size_t i=0; (!update_all && i<widths.size() && i<self_widths.size()); ++i)
                update_all = self_widths[i]>widths[i];
 
        if(update_all)
        {
-               for(Item *i: list->items)
-                       if(MultiColumnItem *mci = dynamic_cast<MultiColumnItem *>(i))
+               for(const unique_ptr<Item> &i: list->items)
+                       if(MultiColumnItem *mci = dynamic_cast<MultiColumnItem *>(i.get()))
                                mci->set_widths(self_widths);
        }