]> git.tdb.fi Git - libs/gltk.git/blob - source/layout.h
Better method of implementing zero gravity
[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                 SPACING = 32,
67                 SLACK = 64
68         };
69
70 public:
71         enum ConstraintType
72         {
73                 ABOVE = VERTICAL|SELF_POS|TARGET_POS|TARGET_DIM|SPACING,
74                 BELOW = VERTICAL|SELF_POS|SELF_DIM|TARGET_POS|SPACING,
75                 RIGHT_OF = HORIZONTAL|SELF_POS|TARGET_POS|TARGET_DIM|SPACING,
76                 LEFT_OF = HORIZONTAL|SELF_POS|SELF_DIM|TARGET_POS|SPACING,
77                 FAR_ABOVE = VERTICAL|SELF_POS|TARGET_POS|TARGET_DIM|SPACING|SLACK,
78                 FAR_BELOW = VERTICAL|SELF_POS|SELF_DIM|TARGET_POS|SPACING|SLACK,
79                 FAR_RIGHT_OF = HORIZONTAL|SELF_POS|TARGET_POS|TARGET_DIM|SPACING|SLACK,
80                 FAR_LEFT_OF = HORIZONTAL|SELF_POS|SELF_DIM|TARGET_POS|SPACING|SLACK,
81                 ALIGN_TOP = VERTICAL|SELF_POS|SELF_DIM|TARGET_POS|TARGET_DIM,
82                 ALIGN_BOTTOM = VERTICAL|SELF_POS|TARGET_POS,
83                 ALIGN_RIGHT = HORIZONTAL|SELF_POS|SELF_DIM|TARGET_POS|TARGET_DIM,
84                 ALIGN_LEFT = HORIZONTAL|SELF_POS|TARGET_POS,
85                 COPY_WIDTH = HORIZONTAL|SELF_DIM|TARGET_DIM,
86                 COPY_HEIGHT = VERTICAL|SELF_DIM|TARGET_DIM
87         };
88
89         class Loader: public DataFile::ObjectLoader<Layout>
90         {
91         public:
92                 typedef std::map<std::string, Widget *> WidgetMap;
93
94         private:
95                 const WidgetMap &wdg_map;
96
97         public:
98                 Loader(Layout &, const WidgetMap &);
99
100         private:
101                 void column_spacing(unsigned);
102                 void margin();
103                 void row_spacing(unsigned);
104                 void spacing(unsigned);
105                 void widget(const std::string &);
106         };
107
108 private:
109         class WidgetLoader: public DataFile::Loader
110         {
111         private:
112                 Layout &layout;
113                 Widget &widget;
114                 const Layout::Loader::WidgetMap &wdg_map;
115
116         public:
117                 WidgetLoader(Layout &, Widget &, const Layout::Loader::WidgetMap &);
118
119         private:
120                 void constraint(ConstraintType, const std::string &);
121                 void expand(bool, bool);
122                 void ghost(bool);
123                 void gravity(int, int);
124         };
125
126         struct Slot;
127
128         struct Constraint
129         {
130                 ConstraintType type;
131                 Slot &target;
132                 int spacing;
133
134                 Constraint(ConstraintType, Slot &);
135         };
136
137         struct Packing
138         {
139                 int gravity;
140                 bool expand;
141
142                 Packing();
143         };
144
145         struct Slot: public sigc::trackable
146         {
147                 Layout &layout;
148                 int index;
149                 Widget &widget;
150                 Geometry autosize_geom;
151                 Geometry geom;
152                 std::list<Constraint> constraints;
153                 Packing horiz_pack;
154                 Packing vert_pack;
155                 bool ghost;
156
157                 Slot(Layout &, Widget &);
158
159                 void autosize_changed();
160                 void visibility_changed(bool);
161         };
162
163         enum SolveMode
164         {
165                 UPDATE,
166                 AUTOSIZE
167         };
168
169         class LinearProgram;
170         struct Pointers;
171
172         Container *container;
173         std::list<Slot *> slots;
174         unsigned n_active_slots;
175         unsigned n_slack_vars[2];
176         Sides margin;
177         unsigned row_spacing;
178         unsigned col_spacing;
179         Geometry autosize_geom;
180         std::list<Arrangement *> arrangement_stack;
181
182         static Pointers pointers[2];
183
184 public:
185         Layout();
186         ~Layout();
187
188         void set_container(Container &);
189         void set_margin(const Sides &);
190         const Sides &get_margin() const { return margin; }
191
192         /** Sets the default spacing between widgets in both directions. */
193         void set_spacing(unsigned);
194
195         /** Sets the default vertical spacing between widgets.  Affects the ABOVE
196         and BELOW constraints. */
197         void set_row_spacing(unsigned);
198
199         /** Sets the default horizontal spacing between widgets.  Affects the
200         LEFT_OF and RIGHT_OF constraints. */
201         void set_column_spacing(unsigned);
202
203         unsigned get_row_spacing() const { return row_spacing; }
204         unsigned get_column_spacing() const { return col_spacing; }
205
206         void push_arrangement(Arrangement &);
207         Arrangement *get_arrangement() const;
208         void pop_arrangement(Arrangement &);
209
210         void add_widget(Widget &);
211         void remove_widget(Widget &);
212 private:
213         void update_slot_indices();
214         Slot &get_slot_for_widget(Widget &);
215         static ConstraintType complement(ConstraintType);
216         void create_constraint(Widget &, ConstraintType, Widget &, int);
217
218 public:
219         /** Adds a constraint between two widgets. */
220         void add_constraint(Widget &src, ConstraintType type, Widget &tgt);
221
222         /** Adds a constraint between two widgets, overriding the default spacing.
223         Not all constraint types use a spacing. */
224         void add_constraint(Widget &src, ConstraintType type, Widget &tgt, unsigned);
225
226         void set_gravity(Widget &, int, int);
227         void set_expand(Widget &, bool, bool);
228
229         /// Sets a widget as a ghost, taking up space even if it is hidden.
230         void set_ghost(Widget &, bool);
231
232         void update();
233         void autosize(Geometry &);
234
235 private:
236         void solve_constraints(int, SolveMode);
237 };
238
239 void operator>>(const LexicalConverter &, Layout::ConstraintType &);
240
241 } // namespace GLtk
242 } // namespace Msp
243
244 #endif