]> git.tdb.fi Git - libs/gltk.git/blob - source/layout.h
Rework how widget ownership works in Container
[libs/gltk.git] / source / layout.h
1 #ifndef MSP_GLTK_LAYOUT_H_
2 #define MSP_GLTK_LAYOUT_H_
3
4 #include <list>
5 #include <set>
6 #include <sigc++/trackable.h>
7 #include <msp/strings/lexicalcast.h>
8 #include "geometry.h"
9
10 namespace Msp {
11 namespace GLtk {
12
13 class Arrangement;
14 class Container;
15 class Widget;
16
17 /**
18 Positions Widgets inside a Container.
19
20 A layout operates on constraints, which are used to form a linear program that
21 is then solved to obtain positions and dimensions that fulfill the constraints.
22 There are three kinds of constraints available: ordering, alignment and
23 dimension matching.
24
25 Ordering constraints specify that the widgets should be placed next to each
26 other along X or Y axis.  These operate on one axis at a time, so a widget
27 could be "right of" another even if they are separated by hundreds of pixels
28 vertically.  The widgets will be separated by a spacing value, which is
29 settable on a per-layout basis.
30
31 Alignment constraints make the corresponding edges of two widgets be on the
32 same line.  These are incompatible with ordering constraints, so only one or
33 the other should be used between any pair of widgets for the same axis.
34
35 Dimension matching constraints force the two widgets to have the same dimension
36 along the relevant axis.
37
38 In addition to constraints, there are some other properties that can be set on
39 widgets to determine how they are laid out.  Gravity affects which edge of the
40 container the widget should be placed at.  This is a relatively weak hint and
41 will be overridden by many other things.  Margins can also be specified to
42 prevent widgets from getting too close to the container's edges.
43
44 Usually widgets are made as small as their content allows.  Setting the expand
45 flag for a widget causes it to use as much space as possible.  If multiple co-
46 dependent widgets have the expand flag set, the results are currently
47 undefined.
48
49 Since specifiyng constraints manually can be quite tedious, an Arrangement
50 interface is provided to automatically arrange widgets.  See classes Row,
51 Column and Grid for some commonly used arrangements.
52 */
53 class Layout
54 {
55 private:
56         enum
57         {
58                 HORIZONTAL = 0,
59                 VERTICAL = 1,
60                 SELF_POS = 2,
61                 SELF_DIM = 4,
62                 SELF_MASK = 6,
63                 TARGET_POS = 8,
64                 TARGET_DIM = 16,
65                 TARGET_MASK = 24,
66                 HALF_DIM = 32,
67                 SPACING = 64,
68                 SLACK = 128
69         };
70
71 public:
72         enum ConstraintType
73         {
74                 ABOVE = VERTICAL|SELF_POS|TARGET_POS|TARGET_DIM|SPACING,
75                 BELOW = VERTICAL|SELF_POS|SELF_DIM|TARGET_POS|SPACING,
76                 RIGHT_OF = HORIZONTAL|SELF_POS|TARGET_POS|TARGET_DIM|SPACING,
77                 LEFT_OF = HORIZONTAL|SELF_POS|SELF_DIM|TARGET_POS|SPACING,
78                 FAR_ABOVE = VERTICAL|SELF_POS|TARGET_POS|TARGET_DIM|SPACING|SLACK,
79                 FAR_BELOW = VERTICAL|SELF_POS|SELF_DIM|TARGET_POS|SPACING|SLACK,
80                 FAR_RIGHT_OF = HORIZONTAL|SELF_POS|TARGET_POS|TARGET_DIM|SPACING|SLACK,
81                 FAR_LEFT_OF = HORIZONTAL|SELF_POS|SELF_DIM|TARGET_POS|SPACING|SLACK,
82                 ALIGN_TOP = VERTICAL|SELF_POS|SELF_DIM|TARGET_POS|TARGET_DIM,
83                 ALIGN_VCENTER = VERTICAL|SELF_POS|SELF_DIM|TARGET_POS|TARGET_DIM|HALF_DIM,
84                 ALIGN_BOTTOM = VERTICAL|SELF_POS|TARGET_POS,
85                 ALIGN_RIGHT = HORIZONTAL|SELF_POS|SELF_DIM|TARGET_POS|TARGET_DIM,
86                 ALIGN_HCENTER = HORIZONTAL|SELF_POS|SELF_DIM|TARGET_POS|TARGET_DIM|HALF_DIM,
87                 ALIGN_LEFT = HORIZONTAL|SELF_POS|TARGET_POS,
88                 COPY_WIDTH = HORIZONTAL|SELF_DIM|TARGET_DIM,
89                 COPY_HEIGHT = VERTICAL|SELF_DIM|TARGET_DIM
90         };
91
92         class Loader: public DataFile::ObjectLoader<Layout>
93         {
94         public:
95                 typedef std::map<std::string, Widget *> WidgetMap;
96
97         private:
98                 const WidgetMap &wdg_map;
99
100         public:
101                 Loader(Layout &, const WidgetMap &);
102
103         private:
104                 void column_spacing(unsigned);
105                 void margin();
106                 void row_spacing(unsigned);
107                 void spacing(unsigned);
108                 void widget(const std::string &);
109         };
110
111 private:
112         class WidgetLoader: public DataFile::Loader
113         {
114         private:
115                 Layout &layout;
116                 Widget &widget;
117                 const Layout::Loader::WidgetMap &wdg_map;
118
119         public:
120                 WidgetLoader(Layout &, Widget &, const Layout::Loader::WidgetMap &);
121
122         private:
123                 void constraint(ConstraintType, const std::string &);
124                 void expand(bool, bool);
125                 void ghost(bool);
126                 void gravity(int, int);
127         };
128
129         struct Slot;
130
131         struct Constraint
132         {
133                 ConstraintType type;
134                 Slot &target;
135                 int spacing;
136
137                 Constraint(ConstraintType, Slot &);
138         };
139
140         struct Packing
141         {
142                 int gravity;
143                 bool expand;
144
145                 Packing();
146         };
147
148         struct Slot: public sigc::trackable
149         {
150                 Layout &layout;
151                 int index;
152                 Widget &widget;
153                 Geometry autosize_geom;
154                 Geometry geom;
155                 std::list<Constraint> constraints;
156                 Packing horiz_pack;
157                 Packing vert_pack;
158                 bool ghost;
159                 bool floating;
160
161                 Slot(Layout &, Widget &);
162
163                 void autosize_changed();
164                 void visibility_changed(bool);
165         };
166
167         enum SolveMode
168         {
169                 UPDATE,
170                 AUTOSIZE
171         };
172
173         class LinearProgram;
174         struct Pointers;
175
176         Container *container;
177         std::list<Slot *> slots;
178         unsigned n_active_slots;
179         unsigned n_slack_vars[2];
180         Sides margin;
181         unsigned row_spacing;
182         unsigned col_spacing;
183         Geometry autosize_geom;
184         std::list<Arrangement *> arrangement_stack;
185
186         static Pointers pointers[2];
187
188 public:
189         Layout();
190         ~Layout();
191
192         void set_container(Container &);
193         void set_margin(const Sides &);
194         const Sides &get_margin() const { return margin; }
195
196         /** Sets the default spacing between widgets in both directions. */
197         void set_spacing(unsigned);
198
199         /** Sets the default vertical spacing between widgets.  Affects the ABOVE
200         and BELOW constraints. */
201         void set_row_spacing(unsigned);
202
203         /** Sets the default horizontal spacing between widgets.  Affects the
204         LEFT_OF and RIGHT_OF constraints. */
205         void set_column_spacing(unsigned);
206
207         unsigned get_row_spacing() const { return row_spacing; }
208         unsigned get_column_spacing() const { return col_spacing; }
209
210         void push_arrangement(Arrangement &);
211         Arrangement *get_arrangement() const;
212         void pop_arrangement(Arrangement &);
213
214         void add_widget(Widget &);
215         void remove_widget(Widget &);
216 private:
217         void update_slot_indices();
218         Slot &get_slot_for_widget(Widget &);
219         static ConstraintType complement(ConstraintType);
220         void create_constraint(Widget &, ConstraintType, Widget &, int);
221
222 public:
223         /** Adds a constraint between two widgets. */
224         void add_constraint(Widget &src, ConstraintType type, Widget &tgt);
225
226         /** Adds a constraint between two widgets, overriding the default spacing.
227         Not all constraint types use a spacing. */
228         void add_constraint(Widget &src, ConstraintType type, Widget &tgt, unsigned);
229
230         void set_gravity(Widget &, int, int);
231         void set_expand(Widget &, bool, bool);
232
233         /// Sets a widget as a ghost, taking up space even if it is hidden.
234         void set_ghost(Widget &, bool);
235
236         void set_floating(Widget &, bool);
237
238         void update();
239         void autosize(Geometry &);
240
241 private:
242         void solve_constraints(int, SolveMode);
243 };
244
245 void operator>>(const LexicalConverter &, Layout::ConstraintType &);
246
247 } // namespace GLtk
248 } // namespace Msp
249
250 #endif