]> git.tdb.fi Git - libs/gltk.git/blob - source/list.h
Rework how widget ownership works in Container
[libs/gltk.git] / source / list.h
1 #ifndef MSP_GLTK_LIST_H_
2 #define MSP_GLTK_LIST_H_
3
4 #include <memory>
5 #include <stdexcept>
6 #include <typeinfo>
7 #include <sigc++/signal.h>
8 #include "container.h"
9 #include "label.h"
10 #include "listdata.h"
11 #include "mspgltk_api.h"
12 #include "slider.h"
13
14 namespace Msp {
15 namespace GLtk {
16
17 /**
18 Thrown if a list's item type is incompatible with its data.
19 */
20 class MSPGLTK_API incompatible_data: public std::logic_error
21 {
22 public:
23         incompatible_data(const std::type_info &);
24 };
25
26
27 /**
28 Shows a list of items, allowing the user to select one.  A slider is included
29 to allow scrolling through a long list.
30 */
31 class MSPGLTK_API List: virtual public Widget, private Container
32 {
33 public:
34         enum ViewMode
35         {
36                 LIST,
37                 GRID
38         };
39
40         static constexpr size_t INVALID_INDEX = std::numeric_limits<size_t>::max();
41
42         class Loader: public DataFile::DerivedObjectLoader<List, Widget::Loader>
43         {
44         public:
45                 Loader(List &);
46
47         private:
48                 void item(const std::string &);
49         };
50
51 private:
52         /// This exists to make disconnecting signals easier
53         class DataObserver: public sigc::trackable
54         {
55         private:
56                 List &list;
57
58         public:
59                 DataObserver(List &);
60
61                 void item_added(std::size_t);
62                 void item_removed(std::size_t);
63                 void cleared();
64                 void refresh_item(std::size_t);
65         };
66
67 public:
68         class Item: virtual public Widget, protected Container
69         {
70         protected:
71                 Item();
72
73         public:
74                 const char *get_class() const override { return "listitem"; }
75
76         protected:
77                 void autosize_special(const Part &, Geometry &) const override;
78         public:
79                 void set_active(bool);
80
81                 void render_special(const Part &, GL::Renderer &) const override;
82         };
83
84         class SimpleItem: public Item
85         {
86         protected:
87                 SimpleItem() = default;
88
89                 void on_style_change() override;
90         };
91
92         class MultiColumnItem: public Item
93         {
94         protected:
95                 MultiColumnItem() = default;
96
97                 virtual void check_widths(std::vector<unsigned> &) const;
98                 virtual void set_widths(const std::vector<unsigned> &);
99
100                 void on_style_change() override;
101         };
102
103 private:
104         class BasicItem: public SimpleItem
105         {
106         private:
107                 Label label;
108
109         public:
110                 BasicItem(const std::string &);
111         };
112
113         class ItemFactory
114         {
115         protected:
116                 ItemFactory() = default;
117         public:
118                 virtual ~ItemFactory() = default;
119
120                 virtual void set_data(const ListData &) = 0;
121                 virtual std::unique_ptr<Item> create_item(std::size_t) const = 0;
122         };
123
124         template<typename I>
125         class TypedItemFactory: public ItemFactory
126         {
127         private:
128                 typedef typename I::ValueType ValueType;
129
130                 const ListDataStore<ValueType> *data;
131
132         public:
133                 TypedItemFactory(const ListData &d) { set_data(d); }
134
135                 void set_data(const ListData &d) override
136                 {
137                         if(const ListDataStore<ValueType> *ds = dynamic_cast<const ListDataStore<ValueType> *>(&d))
138                                 data = ds;
139                         else
140                                 throw incompatible_data(typeid(ValueType));
141                 }
142
143                 std::unique_ptr<Item> create_item(std::size_t i) const override
144                 {
145                         return std::make_unique<I>(data->get(i));
146                 }
147         };
148
149         struct Row
150         {
151                 std::size_t first;
152                 unsigned height;
153
154                 Row(std::size_t f): first(f), height(0) { }
155         };
156
157 public:
158         sigc::signal<void, std::size_t> signal_item_selected;
159         sigc::signal<void> signal_selection_cleared;
160
161 private:
162         std::unique_ptr<ListData> own_data;
163         ListData *data = nullptr;
164         std::unique_ptr<DataObserver> observer = nullptr;
165         std::unique_ptr<ItemFactory> item_factory = nullptr;
166         ViewMode view_mode = LIST;
167         std::size_t sel_index = INVALID_INDEX;
168         std::size_t focus_index = INVALID_INDEX;
169         std::size_t first_row = 0;
170         std::size_t max_scroll = 0;
171         unsigned view_rows = 5;
172         unsigned view_columns = 5;
173         const Part *items_part = nullptr;
174         bool ignore_slider_change = false;
175         bool dragging = false;
176         int drag_start_x = 0;
177         int drag_start_y = 0;
178
179         VSlider slider;
180         std::vector<std::unique_ptr<Item>> items;
181         std::vector<Row> rows;
182
183         List(std::unique_ptr<ListData>);
184 public:
185         List();
186         List(ListData &);
187
188         const char *get_class() const override { return "list"; }
189
190 private:
191         void autosize_special(const Part &, Geometry &) const override;
192
193 public:
194         void set_data(ListData &);
195         ListData &get_data() { return *data; }
196         const ListData &get_data() const { return *data; }
197 private:
198         void items_changed();
199
200 public:
201         template<typename I>
202         void set_item_type() { item_factory = std::make_unique<TypedItemFactory<I>>(*data); }
203 private:
204         std::unique_ptr<Item> create_item(std::size_t);
205
206 public:
207         void set_view_mode(ViewMode);
208         void set_view_size(unsigned);
209         void set_view_size(unsigned, unsigned);
210         void set_view_all();
211
212         void set_selected_index(std::size_t);
213         int get_selected_index() const { return sel_index; }
214 private:
215         void set_selected_item(Widget *);
216
217         void rebuild_special(const Part &) override;
218         void render_special(const Part &, GL::Renderer &) const override;
219
220 public:
221         bool key_press(unsigned, unsigned) override;
222         void button_press(int, int, unsigned) override;
223         void touch_press(int, int, unsigned) override;
224         void touch_release(int, int, unsigned) override;
225         void touch_motion(int, int, unsigned) override;
226         void focus_in() override;
227         bool navigate(Navigation) override;
228 private:
229         void on_style_change() override;
230
231         void move_focus(Navigation, bool);
232         void set_focus_index(std::size_t);
233
234         void item_autosize_changed(Item *);
235         void reposition_items(bool);
236         std::size_t last_to_first_row(std::size_t) const;
237         std::size_t item_index_to_row(std::size_t) const;
238         void check_view_range();
239         void scroll_to_focus();
240         void slider_value_changed(double);
241         static void adjust_index(std::size_t &, std::size_t, std::ptrdiff_t);
242 };
243
244 MSPGLTK_API void operator>>(const LexicalConverter &, List::ViewMode &);
245
246 } // namespace GLtk
247 } // namespace Msp
248
249 #endif