1/*
2 NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>.
3 The widget drawing code is based on the NanoVG demo application
4 by Mikko Mononen.
5
6 All rights reserved. Use of this source code is governed by a
7 BSD-style license that can be found in the LICENSE.txt file.
8*/
9/**
10 * \file nanogui/layout.h
11 *
12 * \brief A collection of useful layout managers. The \ref nanogui::GridLayout
13 * was contributed by Christian Schueller.
14 */
15
16#pragma once
17
18#include <nanogui/object.h>
19#include <cstdio>
20#include <unordered_map>
21
22NAMESPACE_BEGIN(nanogui)
23
24/// The different kinds of alignments a layout can perform.
25enum class Alignment : uint8_t {
26 Minimum = 0, ///< Take only as much space as is required.
27 Middle, ///< Center align.
28 Maximum, ///< Take as much space as is allowed.
29 Fill ///< Fill according to preferred sizes.
30};
31
32/// The direction of data flow for a layout.
33enum class Orientation {
34 Horizontal = 0, ///< Layout expands on horizontal axis.
35 Vertical ///< Layout expands on vertical axis.
36};
37
38/**
39 * \class Layout layout.h nanogui/layout.h
40 *
41 * \brief Basic interface of a layout engine.
42 */
43class NANOGUI_EXPORT Layout : public Object {
44public:
45 /**
46 * Performs any and all resizing applicable.
47 *
48 * \param ctx
49 * The ``NanoVG`` context being used for drawing.
50 *
51 * \param widget
52 * The Widget this layout is controlling sizing for.
53 */
54 virtual void performLayout(NVGcontext *ctx, Widget *widget) const = 0;
55
56 /**
57 * The preferred size for this layout.
58 *
59 * \param ctx
60 * The ``NanoVG`` context being used for drawing.
61 *
62 * \param widget
63 * The Widget this layout's preferred size is considering.
64 *
65 * \return
66 * The preferred size, accounting for things such as spacing, padding
67 * for icons, etc.
68 */
69 virtual Vector2i preferredSize(NVGcontext *ctx, const Widget *widget) const = 0;
70
71protected:
72 /// Default destructor (exists for inheritance).
73 virtual ~Layout() { }
74};
75
76/**
77 * \class BoxLayout layout.h nanogui/layout.h
78 *
79 * \brief Simple horizontal/vertical box layout
80 *
81 * This widget stacks up a bunch of widgets horizontally or vertically. It adds
82 * margins around the entire container and a custom spacing between adjacent
83 * widgets.
84 */
85class NANOGUI_EXPORT BoxLayout : public Layout {
86public:
87 /**
88 * \brief Construct a box layout which packs widgets in the given \c Orientation
89 *
90 * \param orientation
91 * The Orientation this BoxLayout expands along
92 *
93 * \param alignment
94 * Widget alignment perpendicular to the chosen orientation
95 *
96 * \param margin
97 * Margin around the layout container
98 *
99 * \param spacing
100 * Extra spacing placed between widgets
101 */
102 BoxLayout(Orientation orientation, Alignment alignment = Alignment::Middle,
103 int margin = 0, int spacing = 0);
104
105 /// The Orientation this BoxLayout is using.
106 Orientation orientation() const { return mOrientation; }
107
108 /// Sets the Orientation of this BoxLayout.
109 void setOrientation(Orientation orientation) { mOrientation = orientation; }
110
111 /// The Alignment of this BoxLayout.
112 Alignment alignment() const { return mAlignment; }
113
114 /// Sets the Alignment of this BoxLayout.
115 void setAlignment(Alignment alignment) { mAlignment = alignment; }
116
117 /// The margin of this BoxLayout.
118 int margin() const { return mMargin; }
119
120 /// Sets the margin of this BoxLayout.
121 void setMargin(int margin) { mMargin = margin; }
122
123 /// The spacing this BoxLayout is using to pad in between widgets.
124 int spacing() const { return mSpacing; }
125
126 /// Sets the spacing of this BoxLayout.
127 void setSpacing(int spacing) { mSpacing = spacing; }
128
129 /* Implementation of the layout interface */
130 /// See \ref Layout::preferredSize.
131 virtual Vector2i preferredSize(NVGcontext *ctx, const Widget *widget) const override;
132
133 /// See \ref Layout::performLayout.
134 virtual void performLayout(NVGcontext *ctx, Widget *widget) const override;
135
136protected:
137 /// The Orientation of this BoxLayout.
138 Orientation mOrientation;
139
140 /// The Alignment of this BoxLayout.
141 Alignment mAlignment;
142
143 /// The margin of this BoxLayout.
144 int mMargin;
145
146 /// The spacing between widgets of this BoxLayout.
147 int mSpacing;
148};
149
150/**
151 * \class GroupLayout layout.h nanogui/layout.h
152 *
153 * \brief Special layout for widgets grouped by labels.
154 *
155 * This widget resembles a box layout in that it arranges a set of widgets
156 * vertically. All widgets are indented on the horizontal axis except for
157 * \ref Label widgets, which are not indented.
158 *
159 * This creates a pleasing layout where a number of widgets are grouped
160 * under some high-level heading.
161 */
162class NANOGUI_EXPORT GroupLayout : public Layout {
163public:
164 /**
165 * Creates a GroupLayout.
166 *
167 * \param margin
168 * The margin around the widgets added.
169 *
170 * \param spacing
171 * The spacing between widgets added.
172 *
173 * \param groupSpacing
174 * The spacing between groups (groups are defined by each Label added).
175 *
176 * \param groupIndent
177 * The amount to indent widgets in a group (underneath a Label).
178 */
179 GroupLayout(int margin = 15, int spacing = 6, int groupSpacing = 14,
180 int groupIndent = 20)
181 : mMargin(margin), mSpacing(spacing), mGroupSpacing(groupSpacing),
182 mGroupIndent(groupIndent) {}
183
184 /// The margin of this GroupLayout.
185 int margin() const { return mMargin; }
186
187 /// Sets the margin of this GroupLayout.
188 void setMargin(int margin) { mMargin = margin; }
189
190 /// The spacing between widgets of this GroupLayout.
191 int spacing() const { return mSpacing; }
192
193 /// Sets the spacing between widgets of this GroupLayout.
194 void setSpacing(int spacing) { mSpacing = spacing; }
195
196 /// The indent of widgets in a group (underneath a Label) of this GroupLayout.
197 int groupIndent() const { return mGroupIndent; }
198
199 /// Sets the indent of widgets in a group (underneath a Label) of this GroupLayout.
200 void setGroupIndent(int groupIndent) { mGroupIndent = groupIndent; }
201
202 /// The spacing between groups of this GroupLayout.
203 int groupSpacing() const { return mGroupSpacing; }
204
205 /// Sets the spacing between groups of this GroupLayout.
206 void setGroupSpacing(int groupSpacing) { mGroupSpacing = groupSpacing; }
207
208 /* Implementation of the layout interface */
209 /// See \ref Layout::preferredSize.
210 virtual Vector2i preferredSize(NVGcontext *ctx, const Widget *widget) const override;
211
212 /// See \ref Layout::performLayout.
213 virtual void performLayout(NVGcontext *ctx, Widget *widget) const override;
214
215protected:
216 /// The margin of this GroupLayout.
217 int mMargin;
218
219 /// The spacing between widgets of this GroupLayout.
220 int mSpacing;
221
222 /// The spacing between groups of this GroupLayout.
223 int mGroupSpacing;
224
225 /// The indent amount of a group under its defining Label of this GroupLayout.
226 int mGroupIndent;
227};
228
229/**
230 * \class GridLayout layout.h nanogui/layout.h
231 *
232 * \brief Grid layout.
233 *
234 * Widgets are arranged in a grid that has a fixed grid resolution \c resolution
235 * along one of the axes. The layout orientation indicates the fixed dimension;
236 * widgets are also appended on this axis. The spacing between items can be
237 * specified per axis. The horizontal/vertical alignment can be specified per
238 * row and column.
239 */
240class NANOGUI_EXPORT GridLayout : public Layout {
241public:
242 /**
243 * Create a 2-column grid layout by default.
244 *
245 * \param orientation
246 * The fixed dimension of this GridLayout.
247 *
248 * \param resolution
249 * The number of rows or columns in the grid (depending on the Orientation).
250 *
251 * \param alignment
252 * How widgets should be aligned within each grid cell.
253 *
254 * \param margin
255 * The amount of spacing to add around the border of the grid.
256 *
257 * \param spacing
258 * The amount of spacing between widgets added to the grid.
259 */
260 GridLayout(Orientation orientation = Orientation::Horizontal, int resolution = 2,
261 Alignment alignment = Alignment::Middle,
262 int margin = 0, int spacing = 0)
263 : mOrientation(orientation), mResolution(resolution), mMargin(margin) {
264 mDefaultAlignment[0] = mDefaultAlignment[1] = alignment;
265 mSpacing = Vector2i::Constant(spacing);
266 }
267
268 /// The Orientation of this GridLayout.
269 Orientation orientation() const { return mOrientation; }
270
271 /// Sets the Orientation of this GridLayout.
272 void setOrientation(Orientation orientation) {
273 mOrientation = orientation;
274 }
275
276 /// The number of rows or columns (depending on the Orientation) of this GridLayout.
277 int resolution() const { return mResolution; }
278
279 /// Sets the number of rows or columns (depending on the Orientation) of this GridLayout.
280 void setResolution(int resolution) { mResolution = resolution; }
281
282 /// The spacing at the specified axis (row or column number, depending on the Orientation).
283 int spacing(int axis) const { return mSpacing[axis]; }
284
285 /// Sets the spacing for a specific axis.
286 void setSpacing(int axis, int spacing) { mSpacing[axis] = spacing; }
287
288 /// Sets the spacing for all axes.
289 void setSpacing(int spacing) { mSpacing[0] = mSpacing[1] = spacing; }
290
291 /// The margin around this GridLayout.
292 int margin() const { return mMargin; }
293
294 /// Sets the margin of this GridLayout.
295 void setMargin(int margin) { mMargin = margin; }
296
297 /**
298 * The Alignment of the specified axis (row or column number, depending on
299 * the Orientation) at the specified index of that row or column.
300 */
301 Alignment alignment(int axis, int item) const {
302 if (item < (int) mAlignment[axis].size())
303 return mAlignment[axis][item];
304 else
305 return mDefaultAlignment[axis];
306 }
307
308 /// Sets the Alignment of the columns.
309 void setColAlignment(Alignment value) { mDefaultAlignment[0] = value; }
310
311 /// Sets the Alignment of the rows.
312 void setRowAlignment(Alignment value) { mDefaultAlignment[1] = value; }
313
314 /// Use this to set variable Alignment for columns.
315 void setColAlignment(const std::vector<Alignment> &value) { mAlignment[0] = value; }
316
317 /// Use this to set variable Alignment for rows.
318 void setRowAlignment(const std::vector<Alignment> &value) { mAlignment[1] = value; }
319
320 /* Implementation of the layout interface */
321 /// See \ref Layout::preferredSize.
322 virtual Vector2i preferredSize(NVGcontext *ctx, const Widget *widget) const override;
323
324 /// See \ref Layout::performLayout.
325 virtual void performLayout(NVGcontext *ctx, Widget *widget) const override;
326
327protected:
328 /// Compute the maximum row and column sizes
329 void computeLayout(NVGcontext *ctx, const Widget *widget,
330 std::vector<int> *grid) const;
331
332protected:
333 /// The Orientation defining this GridLayout.
334 Orientation mOrientation;
335
336 /// The default Alignment for this GridLayout.
337 Alignment mDefaultAlignment[2];
338
339 /// The actual Alignment being used.
340 std::vector<Alignment> mAlignment[2];
341
342 /// The number of rows or columns before starting a new one, depending on the Orientation.
343 int mResolution;
344
345 /// The spacing used for each dimension.
346 Vector2i mSpacing;
347
348 /// The margin around this GridLayout.
349 int mMargin;
350public:
351 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
352};
353
354/**
355 * \class AdvancedGridLayout layout.h nanogui/layout.h
356 *
357 * \brief Advanced Grid layout.
358 *
359 * The is a fancier grid layout with support for items that span multiple rows
360 * or columns, and per-widget alignment flags. Each row and column additionally
361 * stores a stretch factor that controls how additional space is redistributed.
362 * The downside of this flexibility is that a layout anchor data structure must
363 * be provided for each widget.
364 *
365 * An example:
366 *
367 * \rst
368 * .. code-block:: cpp
369 *
370 * using AdvancedGridLayout::Anchor;
371 * Label *label = new Label(window, "A label");
372 * // Add a centered label at grid position (1, 5), which spans two horizontal cells
373 * layout->setAnchor(label, Anchor(1, 5, 2, 1, Alignment::Middle, Alignment::Middle));
374 *
375 * \endrst
376 *
377 * The grid is initialized with user-specified column and row size vectors
378 * (which can be expanded later on if desired). If a size value of zero is
379 * specified for a column or row, the size is set to the maximum preferred size
380 * of any widgets contained in the same row or column. Any remaining space is
381 * redistributed according to the row and column stretch factors.
382 *
383 * The high level usage somewhat resembles the classic HIG layout:
384 *
385 * - https://web.archive.org/web/20070813221705/http://www.autel.cz/dmi/tutorial.html
386 * - https://github.com/jaapgeurts/higlayout
387 */
388class NANOGUI_EXPORT AdvancedGridLayout : public Layout {
389public:
390 /**
391 * \struct Anchor layout.h nanogui/layout.h
392 *
393 * \brief Helper struct to coordinate anchor points for the layout.
394 */
395 struct Anchor {
396 uint8_t pos[2]; ///< The ``(x, y)`` position.
397 uint8_t size[2]; ///< The ``(x, y)`` size.
398 Alignment align[2];///< The ``(x, y)`` Alignment.
399
400 /// Creates a ``0`` Anchor.
401 Anchor() { }
402
403 /// Create an Anchor at position ``(x, y)`` with specified Alignment.
404 Anchor(int x, int y, Alignment horiz = Alignment::Fill,
405 Alignment vert = Alignment::Fill) {
406 pos[0] = (uint8_t) x; pos[1] = (uint8_t) y;
407 size[0] = size[1] = 1;
408 align[0] = horiz; align[1] = vert;
409 }
410
411 /// Create an Anchor at position ``(x, y)`` of size ``(w, h)`` with specified alignments.
412 Anchor(int x, int y, int w, int h,
413 Alignment horiz = Alignment::Fill,
414 Alignment vert = Alignment::Fill) {
415 pos[0] = (uint8_t) x; pos[1] = (uint8_t) y;
416 size[0] = (uint8_t) w; size[1] = (uint8_t) h;
417 align[0] = horiz; align[1] = vert;
418 }
419
420 /// Allows for printing out Anchor position, size, and alignment.
421 operator std::string() const {
422 char buf[50];
423 std::snprintf(buf, 50, "Format[pos=(%i, %i), size=(%i, %i), align=(%i, %i)]",
424 pos[0], pos[1], size[0], size[1], (int) align[0], (int) align[1]);
425 return buf;
426 }
427 };
428
429 /// Creates an AdvancedGridLayout with specified columns, rows, and margin.
430 AdvancedGridLayout(const std::vector<int> &cols = {}, const std::vector<int> &rows = {}, int margin = 0);
431
432 /// The margin of this AdvancedGridLayout.
433 int margin() const { return mMargin; }
434
435 /// Sets the margin of this AdvancedGridLayout.
436 void setMargin(int margin) { mMargin = margin; }
437
438 /// Return the number of cols
439 int colCount() const { return (int) mCols.size(); }
440
441 /// Return the number of rows
442 int rowCount() const { return (int) mRows.size(); }
443
444 /// Append a row of the given size (and stretch factor)
445 void appendRow(int size, float stretch = 0.f) { mRows.push_back(size); mRowStretch.push_back(stretch); };
446
447 /// Append a column of the given size (and stretch factor)
448 void appendCol(int size, float stretch = 0.f) { mCols.push_back(size); mColStretch.push_back(stretch); };
449
450 /// Set the stretch factor of a given row
451 void setRowStretch(int index, float stretch) { mRowStretch.at(index) = stretch; }
452
453 /// Set the stretch factor of a given column
454 void setColStretch(int index, float stretch) { mColStretch.at(index) = stretch; }
455
456 /// Specify the anchor data structure for a given widget
457 void setAnchor(const Widget *widget, const Anchor &anchor) { mAnchor[widget] = anchor; }
458
459 /// Retrieve the anchor data structure for a given widget
460 Anchor anchor(const Widget *widget) const {
461 auto it = mAnchor.find(widget);
462 if (it == mAnchor.end())
463 throw std::runtime_error("Widget was not registered with the grid layout!");
464 return it->second;
465 }
466
467 /* Implementation of the layout interface */
468 /// See \ref Layout::preferredSize.
469 virtual Vector2i preferredSize(NVGcontext *ctx, const Widget *widget) const override;
470
471 /// See \ref Layout::performLayout.
472 virtual void performLayout(NVGcontext *ctx, Widget *widget) const override;
473
474protected:
475 /// Computes the layout
476 void computeLayout(NVGcontext *ctx, const Widget *widget,
477 std::vector<int> *grid) const;
478
479protected:
480 /// The columns of this AdvancedGridLayout.
481 std::vector<int> mCols;
482
483 /// The rows of this AdvancedGridLayout.
484 std::vector<int> mRows;
485
486 /// The stretch for each column of this AdvancedGridLayout.
487 std::vector<float> mColStretch;
488
489 /// The stretch for each row of this AdvancedGridLayout.
490 std::vector<float> mRowStretch;
491
492 /// The mapping of widgets to their specified anchor points.
493 std::unordered_map<const Widget *, Anchor> mAnchor;
494
495 /// The margin around this AdvancedGridLayout.
496 int mMargin;
497};
498
499NAMESPACE_END(nanogui)
500