1 /**
2 Copyright: Copyright (c) 2013-2014 Andrey Penechko.
3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module anchovy.gui.layouts.linearlayout;
8 
9 import anchovy.gui;
10 
11 public import anchovy.gui.interfaces.ilayout;
12 
13 alias VerticalLayout = LinearLayout!true;
14 alias HorizontalLayout = LinearLayout!false;
15 
16 //version = debug_linear;
17 
18 class LinearLayout(bool vertical) : ILayout
19 {
20 
21 	override void minimize(Widget root)
22 	{
23 		Widget[] children = root.getPropertyAs!("children", Widget[]);
24 		ivec2 rootSize = root.getPropertyAs!("size", ivec2);
25 		int rootSpacing = root.coercePropertyAs!("spacing", int)(0);
26 		int rootPadding = root.coercePropertyAs!("padding", int)(0);
27 
28 		int minRootWidth = int.min; // Will be max child width. Then padding will be added
29 		int minRootLength = rootPadding * 2;
30 		int childrenLength;
31 		uint numExpandableChildren;
32 
33 		foreach(child; children)
34 		{
35 			ivec2 childSize = child.getPropertyAs!("prefSize", ivec2);
36 			ivec2 childMinSize = child.getPropertyAs!("minSize", ivec2);
37 
38 			childrenLength += max(*sizeLength(childSize), *sizeLength(childMinSize));
39 			minRootWidth = max(max(*sizeWidth(childSize), minRootWidth), *sizeWidth(childMinSize));
40 
41 			if (isExpandableLength(child)) ++numExpandableChildren;
42 		}
43 
44 		minRootLength += (children.length-1) * rootSpacing;
45 		minRootLength += childrenLength;
46 		minRootWidth += rootPadding * 2;
47 
48 		version(debug_linear)
49 		{
50 			writeln("minRootLength ", minRootLength);
51 			writeln("minRootWidth ", minRootWidth);
52 			writeln("rootSize ", rootSize);
53 		}
54 
55 		*sizeWidth(rootSize) = minRootWidth;
56 		*sizeLength(rootSize) = minRootLength;
57 
58 		version(debug_linear) writeln("rootSize ", rootSize);
59 
60 		root.setProperty!("prefSize")(rootSize);
61 		root.setProperty!("numExpandable")(numExpandableChildren);
62 
63 		version(debug_linear) writeln("linear minimize end\n");
64 	}
65 
66 	override void expand(Widget root)
67 	{
68 		Widget[] children = root.getPropertyAs!("children", Widget[]);
69 
70 		int rootSpacing = root.coercePropertyAs!("spacing", int)(0);
71 		int rootPadding = root.coercePropertyAs!("padding", int)(0);
72 
73 		uint numExpandableChildren = root.getPropertyAs!("numExpandable", uint);
74 
75 		ivec2 rootSize = root.getPropertyAs!("size", ivec2);
76 		ivec2 rootPrefSize = root.getPropertyAs!("prefSize", ivec2);
77 
78 		version(debug_linear)
79 		{
80 			writeln("Root: ", root["name"]);
81 			writeln("rootSize ", rootSize);
82 			writeln("rootPrefSize ", rootPrefSize);
83 		}
84 
85 		int maxChildWidth = *sizeWidth(rootSize) - rootPadding * 2;
86 
87 		int extraLength = *sizeLength(rootSize) - *sizeLength(rootPrefSize);
88 		int extraPerWidget = extraLength / cast(int)(numExpandableChildren > 0 ? numExpandableChildren : 1);
89 		extraPerWidget = extraPerWidget > 0 ? extraPerWidget : 0;
90 
91 		version(debug_linear)
92 		{
93 			writeln("numChildren ", children.length);
94 			writeln("numExpandableChildren ", numExpandableChildren);
95 			writeln("extraPerWidget ", extraPerWidget);
96 			writeln("extraLength ", extraLength);
97 			writeln("rootPrefSize ", rootPrefSize);
98 		}
99 
100 		int topOffset = rootPadding - rootSpacing;
101 
102 		foreach(child; children)
103 		{
104 			topOffset += rootSpacing;
105 			static if(vertical)
106 				child.setProperty!("position")(ivec2(rootPadding, topOffset));
107 			else
108 				child.setProperty!("position")(ivec2(topOffset, rootPadding));
109 
110 			ivec2 childSize = child.getPropertyAs!("prefSize", ivec2);
111 			ivec2 childMinSize = child.getPropertyAs!("minSize", ivec2);
112 			childSize = ivec2(max(childSize.x, childMinSize.x), max(childSize.y, childMinSize.y));
113 
114 			if (isExpandableLength(child))
115 			{
116 				version(debug_linear) writeln("	expandable ", child["type"]);
117 				*sizeLength(childSize) += extraPerWidget;
118 			}
119 
120 			if (isExpandableWidth(child))
121 			{
122 				version(debug_linear) writeln("expandable width ", child["type"]);
123 				*sizeWidth(childSize) = maxChildWidth;
124 			}
125 
126 			topOffset += *sizeLength(childSize); // Offset for next child
127 
128 			child.setProperty!("size")(childSize);
129 		}
130 
131 		version(debug_linear) writeln("linear expand end\n");
132 	}
133 
134 	override void onContainerResized(Widget root, ivec2 oldSize, ivec2 newSize)
135 	{
136 		version(debug_linear) writeln("onContainerResized");
137 	}
138 
139 private:
140 
141 	static bool isExpandableWidth(Widget widget)
142 	{
143 		static if (vertical)
144 			return widget.hasProperty!"hexpand";
145 		else
146 			return widget.hasProperty!"vexpand";
147 	}
148 
149 	static bool isExpandableLength(Widget widget)
150 	{
151 		static if (vertical)
152 			return widget.hasProperty!"vexpand";
153 		else
154 			return widget.hasProperty!"hexpand";
155 	}
156 
157 	static pure int* sizeLength(ref ivec2 vector)
158 	{
159 		static if (vertical)
160 			return &(vector.arrayof[1]);
161 		else
162 			return &(vector.arrayof[0]);
163 	}
164 
165 	static pure int* sizeWidth(ref ivec2 vector)
166 	{
167 		static if (vertical)
168 			return &(vector.arrayof[0]);
169 		else
170 			return &(vector.arrayof[1]);
171 	}
172 }