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