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