1 ///
2 module nanogui.layout;
3 
4 import std.container.array : Array;
5 import std.algorithm : max;
6 
7 import nanogui.window : Window;
8 import nanogui.common;
9 import nanogui.widget;
10 
11 /*
12 	NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>.
13 	The widget drawing code is based on the NanoVG demo application
14 	by Mikko Mononen.
15 
16 	All rights reserved. Use of this source code is governed by a
17 	BSD-style license that can be found in the LICENSE.txt file.
18 */
19 /**
20  * A collection of useful layout managers.  The \ref nanogui.GridLayout
21  *        was contributed by Christian Schueller.
22  */
23 
24 /// The different kinds of alignments a layout can perform.
25 enum Alignment : ubyte
26 {
27 	Minimum = 0, /// Take only as much space as is required.
28 	Middle,      /// Center align.
29 	Maximum,     /// Take as much space as is allowed.
30 	Fill         /// Fill according to preferred sizes.
31 }
32 
33 /// The direction of data flow for a layout.
34 enum Orientation
35 {
36 	Horizontal = 0, /// Layout expands on horizontal axis.
37 	Vertical        /// Layout expands on vertical axis.
38 }
39 
40 /**
41  * interface Layout
42  *
43  * Basic interface of a layout engine.
44  */
45 interface Layout
46 {
47 public:
48 	/**
49 	 * Performs any and all resizing applicable.
50 	 *
51 	 * Params:
52 	 *     nvg    = The `NanoVG` context being used for drawing.
53 	 *     widget = The Widget this layout is controlling sizing for.
54 	 */
55 	void performLayout(NVGContext nvg, Widget widget) const;
56 
57 	/**
58 	 * The preferred size for this layout.
59 	 *
60 	 * Params:
61 	 *     nvg    = The `NanoVG` context being used for drawing.
62 	 *     widget = The Widget this layout's preferred size is considering.
63 	 *
64 	 * Returns:
65 	 *     The preferred size, accounting for things such as spacing, padding
66 	 *     for icons, etc.
67 	 */
68 	Vector2i preferredSize(NVGContext nvg, const Widget widget, const Widget skipped = null) const;
69 }
70 
71 /**
72  * Simple horizontal/vertical box layout
73  *
74  * This widget stacks up a bunch of widgets horizontally or vertically. It adds
75  * margins around the entire container and a custom spacing between adjacent
76  * widgets.
77  */
78 class BoxLayout : Layout
79 {
80 public:
81 	/**
82 	 * Construct a box layout which packs widgets in the given `Orientation`
83 	 *
84 	 * Params:
85 	 *     orientation = The Orientation this BoxLayout expands along
86 	 *     alignment   = Widget alignment perpendicular to the chosen orientation
87 	 *     margin      = Margin around the layout container
88 	 *     spacing     = Extra spacing placed between widgets
89 	 */
90 	this(Orientation orientation, Alignment alignment = Alignment.Middle,
91 			  int margin = 0, int spacing = 0)
92 	{
93 		mOrientation = orientation;
94 		mAlignment   = alignment;
95 		mMargin      = margin;
96 		mSpacing     = spacing;
97 	}
98 
99 	/// The Orientation this BoxLayout is using.
100 	final Orientation orientation() const { return mOrientation; }
101 
102 	/// Sets the Orientation of this BoxLayout.
103 	final void setOrientation(Orientation orientation) { mOrientation = orientation; }
104 
105 	/// The Alignment of this BoxLayout.
106 	final Alignment alignment() const { return mAlignment; }
107 
108 	/// Sets the Alignment of this BoxLayout.
109 	final void setAlignment(Alignment alignment) { mAlignment = alignment; }
110 
111 	/// The margin of this BoxLayout.
112 	final int margin() const { return mMargin; }
113 
114 	/// Sets the margin of this BoxLayout.
115 	final void setMargin(int margin) { mMargin = margin; }
116 
117 	/// The spacing this BoxLayout is using to pad in between widgets.
118 	final int spacing() const { return mSpacing; }
119 
120 	/// Sets the spacing of this BoxLayout.
121 	final void setSpacing(int spacing) { mSpacing = spacing; }
122 
123 	/// Implementation of the layout interface
124 	/// See `Layout.preferredSize`.
125 	override Vector2i preferredSize(NVGContext nvg, const Widget widget, const Widget skipped = null) const
126 	{
127 		Vector2i size = Vector2i(2*mMargin, 2*mMargin);
128 
129 		int yOffset = 0;
130 		auto window = cast(Window) widget;
131 		if (window && window.title().length) {
132 			if (mOrientation == Orientation.Vertical)
133 				size[1] += widget.theme.mWindowHeaderHeight - mMargin/2;
134 			else
135 				yOffset = widget.theme.mWindowHeaderHeight;
136 		}
137 
138 		bool first = true;
139 		int axis1 = cast(int) mOrientation;
140 		int axis2 = (cast(int) mOrientation + 1)%2;
141 		foreach (w; widget.children)
142 		{
143 			if (!w.visible || w is skipped)
144 				continue;
145 			if (first)
146 				first = false;
147 			else
148 				size[axis1] += mSpacing;
149 
150 			Vector2i ps = w.preferredSize(nvg);
151 			Vector2i fs = w.fixedSize();
152 			auto targetSize = Vector2i(
153 				fs[0] ? fs[0] : ps[0],
154 				fs[1] ? fs[1] : ps[1]
155 			);
156 
157 			size[axis1] += targetSize[axis1];
158 			size[axis2] = max(size[axis2], targetSize[axis2] + 2*mMargin);
159 			first = false;
160 		}
161 		return size + Vector2i(0, yOffset);
162 	}
163 
164 	/// See `Layout.performLayout`.
165 	override void performLayout(NVGContext nvg, Widget widget) const
166 	{
167 		Vector2i fs_w = widget.fixedSize();
168 		auto containerSize = Vector2i(
169 			fs_w[0] ? fs_w[0] : widget.width,
170 			fs_w[1] ? fs_w[1] : widget.height
171 		);
172 
173 		int axis1 = cast(int) mOrientation;
174 		int axis2 = (cast(int) mOrientation + 1)%2;
175 		int position = mMargin;
176 		int yOffset = 0;
177 
178 		import nanogui.window : Window;
179 		auto window = cast(const Window)(widget);
180 		if (window && window.title.length)
181 		{
182 			if (mOrientation == Orientation.Vertical)
183 			{
184 				position += widget.theme.mWindowHeaderHeight - mMargin/2;
185 			}
186 			else
187 			{
188 				yOffset = widget.theme.mWindowHeaderHeight;
189 				containerSize[1] -= yOffset;
190 			}
191 		}
192 
193 		bool first = true;
194 		foreach(w; widget.children) {
195 			if (!w.visible)
196 				continue;
197 			if (first)
198 				first = false;
199 			else
200 				position += mSpacing;
201 
202 			Vector2i ps = w.preferredSize(nvg), fs = w.fixedSize();
203 			auto targetSize = Vector2i(
204 				fs[0] ? fs[0] : ps[0],
205 				fs[1] ? fs[1] : ps[1]
206 			);
207 			auto pos = Vector2i(0, yOffset);
208 
209 			pos[axis1] = position;
210 
211 			final switch (mAlignment)
212 			{
213 				case Alignment.Minimum:
214 					pos[axis2] += mMargin;
215 					break;
216 				case Alignment.Middle:
217 					pos[axis2] += (containerSize[axis2] - targetSize[axis2]) / 2;
218 					break;
219 				case Alignment.Maximum:
220 					pos[axis2] += containerSize[axis2] - targetSize[axis2] - mMargin * 2;
221 					break;
222 				case Alignment.Fill:
223 					pos[axis2] += mMargin;
224 					targetSize[axis2] = fs[axis2] ? fs[axis2] : (containerSize[axis2] - mMargin * 2);
225 					break;
226 			}
227 
228 			w.position(pos);
229 			w.size(targetSize);
230 			w.performLayout(nvg);
231 			position += targetSize[axis1];
232 		}
233 	}
234 
235 protected:
236 	/// The Orientation of this BoxLayout.
237 	Orientation mOrientation;
238 
239 	/// The Alignment of this BoxLayout.
240 	Alignment mAlignment;
241 
242 	/// The margin of this BoxLayout.
243 	int mMargin;
244 
245 	/// The spacing between widgets of this BoxLayout.
246 	int mSpacing;
247 }
248 
249 /**
250  * Special layout for widgets grouped by labels.
251  *
252  * This widget resembles a box layout in that it arranges a set of widgets
253  * vertically. All widgets are indented on the horizontal axis except for
254  * `Label` widgets, which are not indented.
255  *
256  * This creates a pleasing layout where a number of widgets are grouped
257  * under some high-level heading.
258  */
259 class GroupLayout : Layout
260 {
261 public:
262 	/**
263 	 * Creates a GroupLayout.
264 	 *
265 	 * Params:
266 	 *     margin       = The margin around the widgets added.
267 	 *     spacing      = The spacing between widgets added.
268 	 *     groupSpacing = The spacing between groups (groups are defined by each Label added).
269 	 *     groupIndent  = The amount to indent widgets in a group (underneath a Label).
270 	 */
271 	this(int margin = 15, int spacing = 6, int groupSpacing = 14,
272 				int groupIndent = 20)
273 	{
274 		mMargin       = margin;
275 		mSpacing      = spacing;
276 		mGroupSpacing = groupSpacing;
277 		mGroupIndent  = groupIndent;
278 	}
279 
280 	/// The margin of this GroupLayout.
281 	final int margin() const { return mMargin; }
282 
283 	/// Sets the margin of this GroupLayout.
284 	final void margin(int margin) { mMargin = margin; }
285 
286 	/// The spacing between widgets of this GroupLayout.
287 	final int spacing() const { return mSpacing; }
288 
289 	/// Sets the spacing between widgets of this GroupLayout.
290 	final void spacing(int spacing) { mSpacing = spacing; }
291 
292 	/// The indent of widgets in a group (underneath a Label) of this GroupLayout.
293 	final int groupIndent() const { return mGroupIndent; }
294 
295 	/// Sets the indent of widgets in a group (underneath a Label) of this GroupLayout.
296 	final void groupIndent(int groupIndent) { mGroupIndent = groupIndent; }
297 
298 	/// The spacing between groups of this GroupLayout.
299 	final int groupSpacing() const { return mGroupSpacing; }
300 
301 	/// Sets the spacing between groups of this GroupLayout.
302 	final void groupSpacing(int groupSpacing) { mGroupSpacing = groupSpacing; }
303 
304 	/// Implementation of the layout interface
305 	/// See `Layout.preferredSize`.
306 	override Vector2i preferredSize(NVGContext nvg, const Widget widget, const Widget skipped = null) const
307 	{
308 		int height = mMargin, width = 2*mMargin;
309 
310 		import nanogui.window : Window;
311 		auto window = cast(const Window) widget;
312 		if (window && window.title.length)
313 			height += widget.theme.mWindowHeaderHeight - mMargin/2;
314 
315 		bool first = true, indent = false;
316 		foreach (c; widget.children) {
317 			if (!c.visible || c is skipped)
318 				continue;
319 			import nanogui.label : Label;
320 			auto label = cast(const Label) c;
321 			if (!first)
322 				height += (label is null) ? mSpacing : mGroupSpacing;
323 			first = false;
324 
325 			Vector2i ps = c.preferredSize(nvg), fs = c.fixedSize();
326 			auto targetSize = Vector2i(
327 				fs[0] ? fs[0] : ps[0],
328 				fs[1] ? fs[1] : ps[1]
329 			);
330 
331 			bool indentCur = indent && label is null;
332 			height += targetSize.y;
333 
334 			width = max(width, targetSize.x + 2*mMargin + (indentCur ? mGroupIndent : 0));
335 
336 			if (label)
337 				indent = label.caption().length != 0;
338 		}
339 		height += mMargin;
340 		return Vector2i(width, height);
341 	}
342 
343 	/// See `Layout.performLayout`.
344 	override void performLayout(NVGContext nvg, Widget widget) const
345 	{
346 		int height = mMargin, availableWidth =
347 			(widget.fixedWidth() ? widget.fixedWidth() : widget.width()) - 2*mMargin;
348 
349 		const Window window = cast(const Window) widget;
350 		if (window && window.title.length)
351 			height += widget.theme.mWindowHeaderHeight - mMargin/2;
352 
353 		bool first = true, indent = false;
354 		foreach (c; widget.children) {
355 			if (!c.visible)
356 				continue;
357 			import nanogui.label : Label;
358 			const Label label = cast(const Label) c;
359 			if (!first)
360 				height += (label is null) ? mSpacing : mGroupSpacing;
361 			first = false;
362 
363 			bool indentCur = indent && label is null;
364 			Vector2i ps = Vector2i(availableWidth - (indentCur ? mGroupIndent : 0),
365 								   c.preferredSize(nvg).y);
366 			Vector2i fs = c.fixedSize();
367 
368 			auto targetSize = Vector2i(
369 				fs[0] ? fs[0] : ps[0],
370 				fs[1] ? fs[1] : ps[1]
371 			);
372 
373 			c.position = Vector2i(mMargin + (indentCur ? mGroupIndent : 0), height);
374 			c.size = targetSize;
375 			c.performLayout(nvg);
376 
377 			height += targetSize.y;
378 
379 			if (label)
380 				indent = label.caption != "";
381 		}
382 	}
383 
384 protected:
385 	/// The margin of this GroupLayout.
386 	int mMargin;
387 
388 	/// The spacing between widgets of this GroupLayout.
389 	int mSpacing;
390 
391 	/// The spacing between groups of this GroupLayout.
392 	int mGroupSpacing;
393 
394 	/// The indent amount of a group under its defining Label of this GroupLayout.
395 	int mGroupIndent;
396 }
397 
398 /**
399  * Grid layout.
400  *
401  * Widgets are arranged in a grid that has a fixed grid resolution `resolution`
402  * along one of the axes. The layout orientation indicates the fixed dimension;
403  * widgets are also appended on this axis. The spacing between items can be
404  * specified per axis. The horizontal/vertical alignment can be specified per
405  * row and column.
406  */
407 class GridLayout : Layout
408 {
409 public:
410 	/**
411 	 * Create a 2-column grid layout by default.
412 	 *
413 	 * Params:
414 	 *     orientation = The fixed dimension of this GridLayout.
415 	 *     resolution  = The number of rows or columns in the grid (depending on the Orientation).
416 	 *     alignment   = How widgets should be aligned within each grid cell.
417 	 *     margin      = The amount of spacing to add around the border of the grid.
418 	 *     spacing     = The amount of spacing between widgets added to the grid.
419 	 */
420 	this(Orientation orientation = Orientation.Horizontal, int resolution = 2,
421 			   Alignment alignment = Alignment.Middle,
422 			   int margin = 0, int spacing = 0)
423 	{
424 		mOrientation = orientation;
425 		mResolution  = resolution;
426 		mMargin      = margin;
427 		mSpacing     = Vector2i(spacing);
428 
429 		mDefaultAlignment[0] = mDefaultAlignment[1] = alignment;
430 	}
431 
432 	/// The Orientation of this GridLayout.
433 	final Orientation orientation() const { return mOrientation; }
434 
435 	/// Sets the Orientation of this GridLayout.
436 	final void orientation(Orientation orientation) {
437 		mOrientation = orientation;
438 	}
439 
440 	/// The number of rows or columns (depending on the Orientation) of this GridLayout.
441 	final int resolution() const { return mResolution; }
442 
443 	/// Sets the number of rows or columns (depending on the Orientation) of this GridLayout.
444 	final void resolution(int resolution) { mResolution = resolution; }
445 
446 	/// The spacing at the specified axis (row or column number, depending on the Orientation).
447 	final int spacing(int axis) const { return mSpacing[axis]; }
448 
449 	/// Sets the spacing for a specific axis.
450 	final void spacing(int axis, int spacing) { mSpacing[axis] = spacing; }
451 
452 	/// Sets the spacing for all axes.
453 	final void spacing(int spacing) { mSpacing[0] = mSpacing[1] = spacing; }
454 
455 	/// The margin around this GridLayout.
456 	final int margin() const { return mMargin; }
457 
458 	/// Sets the margin of this GridLayout.
459 	final void margin(int margin) { mMargin = margin; }
460 
461 	/**
462 	 * The Alignment of the specified axis (row or column number, depending on
463 	 * the Orientation) at the specified index of that row or column.
464 	 */
465 	final Alignment alignment(int axis, int item) const
466 	{
467 		if (item < cast(int) mAlignment[axis].length)
468 			return mAlignment[axis][item];
469 		else
470 			return mDefaultAlignment[axis];
471 	}
472 
473 	/// Sets the Alignment of the columns.
474 	final void colAlignment(Alignment value) { mDefaultAlignment[0] = value; }
475 
476 	/// Sets the Alignment of the rows.
477 	final void rowAlignment(Alignment value) { mDefaultAlignment[1] = value; }
478 
479 	/// Use this to set variable Alignment for columns.
480 	final void colAlignment(Array!Alignment value) { mAlignment[0] = value; }
481 
482 	/// Use this to set variable Alignment for rows.
483 	final void rowAlignment(Array!Alignment value) { mAlignment[1] = value; }
484 
485 	/// Implementation of the layout interface
486 	/// See `Layout.preferredSize`.
487 	override Vector2i preferredSize(NVGContext nvg, const Widget widget, const Widget skipped = null) const
488 	{
489 		/* Compute minimum row / column sizes */
490 		import std.algorithm : sum;
491 		Array!(int)[2] grid;
492 		computeLayout(nvg, widget, grid, skipped);
493 
494 		auto size = Vector2i(
495 			2*mMargin + sum(grid[0][])
496 			 + max(cast(int) grid[0].length - 1, 0) * mSpacing[0],
497 			2*mMargin + sum(grid[1][])
498 			 + max(cast(int) grid[1].length - 1, 0) * mSpacing[1]
499 		);
500 
501 		const Window window = cast(const Window) widget;
502 		if (window && window.title.length)
503 			size[1] += widget.theme.mWindowHeaderHeight - mMargin/2;
504 
505 		return size;
506 	}
507 
508 	/// See `Layout.performLayout`.
509 	override void performLayout(NVGContext nvg, Widget widget) const
510 	{
511 		Vector2i fs_w = widget.fixedSize;
512 		auto containerSize = Vector2i(
513 			fs_w[0] ? fs_w[0] : widget.width,
514 			fs_w[1] ? fs_w[1] : widget.height
515 		);
516 
517 		/* Compute minimum row / column sizes */
518 		Array!(int)[2] grid;
519 		computeLayout(nvg, widget, grid, null);
520 		if (grid[1].length == 0)
521 			return;
522 		int[2] dim = [ cast(int) grid[0].length, cast(int) grid[1].length ];
523 
524 		Vector2i extra;
525 		const Window window = cast(const Window) widget;
526 		if (window && window.title.length)
527 			extra[1] += widget.theme.mWindowHeaderHeight - mMargin / 2;
528 
529 		/* Strech to size provided by \c widget */
530 		for (int i = 0; i < 2; i++) {
531 			int gridSize = 2 * mMargin + extra[i];
532 			foreach (int s; grid[i]) {
533 				gridSize += s;
534 				if (i+1 < dim[i])
535 					gridSize += mSpacing[i];
536 			}
537 
538 			if (gridSize < containerSize[i]) {
539 				/* Re-distribute remaining space evenly */
540 				int gap = containerSize[i] - gridSize;
541 				int g = gap / dim[i];
542 				int rest = gap - g * dim[i];
543 				for (int j = 0; j < dim[i]; ++j)
544 					grid[i][j] += g;
545 				for (int j = 0; rest > 0 && j < dim[i]; --rest, ++j)
546 					grid[i][j] += 1;
547 			}
548 		}
549 
550 		int axis1 = cast(int) mOrientation, axis2 = (axis1 + 1) % 2;
551 		Vector2i start = Vector2i(mMargin, mMargin) + extra;
552 
553 		size_t numChildren = widget.children.length;
554 		size_t child = 0;
555 
556 		Vector2i pos = start;
557 		for (int i2 = 0; i2 < dim[axis2]; i2++) {
558 			pos[axis1] = start[axis1];
559 			for (int i1 = 0; i1 < dim[axis1]; i1++) {
560 				Widget w;
561 				do {
562 					if (child >= numChildren)
563 						return;
564 					w = widget.children()[child++];
565 				} while (!w.visible());
566 
567 				Vector2i ps = w.preferredSize(nvg);
568 				Vector2i fs = w.fixedSize();
569 				auto targetSize = Vector2i(
570 					fs[0] ? fs[0] : ps[0],
571 					fs[1] ? fs[1] : ps[1]
572 				);
573 
574 				auto itemPos = Vector2i(pos);
575 				for (int j = 0; j < 2; j++) {
576 					int axis = (axis1 + j) % 2;
577 					int item = j == 0 ? i1 : i2;
578 					Alignment algn = alignment(axis, item);
579 
580 					final switch (algn) {
581 						case Alignment.Minimum:
582 							break;
583 						case Alignment.Middle:
584 							itemPos[axis] += (grid[axis][item] - targetSize[axis]) / 2;
585 							break;
586 						case Alignment.Maximum:
587 							itemPos[axis] += grid[axis][item] - targetSize[axis];
588 							break;
589 						case Alignment.Fill:
590 							targetSize[axis] = fs[axis] ? fs[axis] : grid[axis][item];
591 							break;
592 					}
593 				}
594 				w.position(itemPos);
595 				w.size(targetSize);
596 				w.performLayout(nvg);
597 				pos[axis1] += grid[axis1][i1] + mSpacing[axis1];
598 			}
599 			pos[axis2] += grid[axis2][i2] + mSpacing[axis2];
600 		}
601 	}
602 
603 protected:
604 	/// Compute the maximum row and column sizes
605 	void computeLayout(NVGContext nvg, const Widget widget,
606 					   ref Array!(int)[2] grid, const Widget skipped) const
607 	{
608 		int axis1 = cast(int) mOrientation, axis2 = (axis1 + 1) % 2;
609 		size_t numChildren = widget.children.length, visibleChildren = 0;
610 		foreach (w; widget.children)
611 			visibleChildren += w.visible ? 1 : 0;
612 
613 		Vector2i dim;
614 		dim[axis1] = mResolution;
615 		dim[axis2] = cast(int) ((visibleChildren + mResolution - 1) / mResolution);
616 
617 		grid[axis1].clear(); grid[axis1].length = dim[axis1]; grid[axis1][] = 0;
618 		grid[axis2].clear(); grid[axis2].length = dim[axis2]; grid[axis2][] = 0;
619 
620 		size_t child;
621 		for (int i2 = 0; i2 < dim[axis2]; i2++) {
622 			for (int i1 = 0; i1 < dim[axis1]; i1++) {
623 				import std.typecons : Rebindable;
624 				Rebindable!(const Widget) w;
625 				do {
626 					if (child >= numChildren)
627 						return;
628 					w = widget.children[child++];
629 				} while (!w.visible || w is skipped);
630 
631 				Vector2i ps = w.preferredSize(nvg);
632 				Vector2i fs = w.fixedSize();
633 				auto targetSize = Vector2i(
634 					fs[0] ? fs[0] : ps[0],
635 					fs[1] ? fs[1] : ps[1]
636 				);
637 
638 				grid[axis1][i1] = max(grid[axis1][i1], targetSize[axis1]);
639 				grid[axis2][i2] = max(grid[axis2][i2], targetSize[axis2]);
640 			}
641 		}
642 	}
643 
644 	/// The Orientation defining this GridLayout.
645 	Orientation mOrientation;
646 
647 	/// The default Alignment for this GridLayout.
648 	Alignment[2] mDefaultAlignment;
649 
650 	/// The actual Alignment being used.
651 	Array!(Alignment)[2] mAlignment;
652 
653 	/// The number of rows or columns before starting a new one, depending on the Orientation.
654 	int mResolution;
655 
656 	/// The spacing used for each dimension.
657 	Vector2i mSpacing;
658 
659 	/// The margin around this GridLayout.
660 	int mMargin;
661 }
662 
663 ///**
664 // * \class AdvancedGridLayout layout.h nanogui/layout.h
665 // *
666 // * \brief Advanced Grid layout.
667 // *
668 // * The is a fancier grid layout with support for items that span multiple rows
669 // * or columns, and per-widget alignment flags. Each row and column additionally
670 // * stores a stretch factor that controls how additional space is redistributed.
671 // * The downside of this flexibility is that a layout anchor data structure must
672 // * be provided for each widget.
673 // *
674 // * An example:
675 // *
676 // * \rst
677 // * .. code-block. cpp
678 // *
679 // *    using AdvancedGridLayout.Anchor;
680 // *    Label *label = new Label(window, "A label");
681 // *    // Add a centered label at grid position (1, 5), which spans two horizontal cells
682 // *    layout.setAnchor(label, Anchor(1, 5, 2, 1, Alignment.Middle, Alignment.Middle));
683 // *
684 // * \endrst
685 // *
686 // * The grid is initialized with user-specified column and row size vectors
687 // * (which can be expanded later on if desired). If a size value of zero is
688 // * specified for a column or row, the size is set to the maximum preferred size
689 // * of any widgets contained in the same row or column. Any remaining space is
690 // * redistributed according to the row and column stretch factors.
691 // *
692 // * The high level usage somewhat resembles the classic HIG layout:
693 // *
694 // * - https://web.archive.org/web/20070813221705/http://www.autel.cz/dmi/tutorial.html
695 // * - https://github.com/jaapgeurts/higlayout
696 // */
697 //class NANOGUI_EXPORT AdvancedGridLayout : public Layout {
698 //public:
699 //    /**
700 //     * \struct Anchor layout.h nanogui/layout.h
701 //     *
702 //     * \brief Helper struct to coordinate anchor points for the layout.
703 //     */
704 //    struct Anchor {
705 //        uint8_t pos[2];    ///< The ``(x, y)`` position.
706 //        uint8_t size[2];   ///< The ``(x, y)`` size.
707 //        Alignment align[2];///< The ``(x, y)`` Alignment.
708 
709 //        /// Creates a ``0`` Anchor.
710 //        Anchor() { }
711 
712 //        /// Create an Anchor at position ``(x, y)`` with specified Alignment.
713 //        Anchor(int x, int y, Alignment horiz = Alignment.Fill,
714 //              Alignment vert = Alignment.Fill) {
715 //            pos[0] = (uint8_t) x; pos[1] = (uint8_t) y;
716 //            size[0] = size[1] = 1;
717 //            align[0] = horiz; align[1] = vert;
718 //        }
719 
720 //        /// Create an Anchor at position ``(x, y)`` of size ``(w, h)`` with specified alignments.
721 //        Anchor(int x, int y, int w, int h,
722 //              Alignment horiz = Alignment.Fill,
723 //              Alignment vert = Alignment.Fill) {
724 //            pos[0] = (uint8_t) x; pos[1] = (uint8_t) y;
725 //            size[0] = (uint8_t) w; size[1] = (uint8_t) h;
726 //            align[0] = horiz; align[1] = vert;
727 //        }
728 
729 //        /// Allows for printing out Anchor position, size, and alignment.
730 //        operator std.string() const {
731 //            char buf[50];
732 //            NANOGUI_SNPRINTF(buf, 50, "Format[pos=(%i, %i), size=(%i, %i), align=(%i, %i)]",
733 //                pos[0], pos[1], size[0], size[1], (int) align[0], (int) align[1]);
734 //            return buf;
735 //        }
736 //    };
737 
738 //    /// Creates an AdvancedGridLayout with specified columns, rows, and margin.
739 //    AdvancedGridLayout(const std.vector<int> &cols = {}, const std.vector<int> &rows = {}, int margin = 0);
740 
741 //    /// The margin of this AdvancedGridLayout.
742 //    int margin() const { return mMargin; }
743 
744 //    /// Sets the margin of this AdvancedGridLayout.
745 //    void setMargin(int margin) { mMargin = margin; }
746 
747 //    /// Return the number of cols
748 //    int colCount() const { return (int) mCols.size(); }
749 
750 //    /// Return the number of rows
751 //    int rowCount() const { return (int) mRows.size(); }
752 
753 //    /// Append a row of the given size (and stretch factor)
754 //    void appendRow(int size, float stretch = 0.f) { mRows.push_back(size); mRowStretch.push_back(stretch); };
755 
756 //    /// Append a column of the given size (and stretch factor)
757 //    void appendCol(int size, float stretch = 0.f) { mCols.push_back(size); mColStretch.push_back(stretch); };
758 
759 //    /// Set the stretch factor of a given row
760 //    void setRowStretch(int index, float stretch) { mRowStretch.at(index) = stretch; }
761 
762 //    /// Set the stretch factor of a given column
763 //    void setColStretch(int index, float stretch) { mColStretch.at(index) = stretch; }
764 
765 //    /// Specify the anchor data structure for a given widget
766 //    void setAnchor(const Widget *widget, const Anchor &anchor) { mAnchor[widget] = anchor; }
767 
768 //    /// Retrieve the anchor data structure for a given widget
769 //    Anchor anchor(const Widget *widget) const {
770 //        auto it = mAnchor.find(widget);
771 //        if (it == mAnchor.end())
772 //            throw std.runtime_error("Widget was not registered with the grid layout!");
773 //        return it.second;
774 //    }
775 
776 //    /* Implementation of the layout interface */
777 //    /// See \ref Layout.preferredSize.
778 //    virtual Vector2i preferredSize(NVGContext *nvg, const Widget *widget) const override;
779 
780 //    /// See \ref Layout.performLayout.
781 //    virtual void performLayout(NVGContext *nvg, Widget *widget) const override;
782 
783 //protected:
784 //    /// Computes the layout
785 //    void computeLayout(NVGContext *nvg, const Widget *widget,
786 //                       std.vector<int> *grid) const;
787 
788 //protected:
789 //    /// The columns of this AdvancedGridLayout.
790 //    std.vector<int> mCols;
791 
792 //    /// The rows of this AdvancedGridLayout.
793 //    std.vector<int> mRows;
794 
795 //    /// The stretch for each column of this AdvancedGridLayout.
796 //    std.vector<float> mColStretch;
797 
798 //    /// The stretch for each row of this AdvancedGridLayout.
799 //    std.vector<float> mRowStretch;
800 
801 //    /// The mapping of widgets to their specified anchor points.
802 //    std.unordered_map<const Widget *, Anchor> mAnchor;
803 
804 //    /// The margin around this AdvancedGridLayout.
805 //    int mMargin;
806 //}