1 /** 2 Grimoire 3 Copyright (c) 2017 Enalye 4 5 This software is provided 'as-is', without any express or implied warranty. 6 In no event will the authors be held liable for any damages arising 7 from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute 11 it freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; 14 you must not claim that you wrote the original software. 15 If you use this software in a product, an acknowledgment 16 in the product documentation would be appreciated but 17 is not required. 18 19 2. Altered source versions must be plainly marked as such, 20 and must not be misrepresented as being the original software. 21 22 3. This notice may not be removed or altered from any source distribution. 23 */ 24 25 module ui.layout; 26 27 import std.conv: to; 28 import std.algorithm.comparison: max; 29 30 import render.window; 31 import core.all; 32 import ui.widget; 33 34 import common.all; 35 36 class VLayout: WidgetGroup { 37 private { 38 Vec2f _spacing = Vec2f.zero; 39 uint _capacity; 40 } 41 42 @property { 43 Vec2f spacing() const { return _spacing; } 44 Vec2f spacing(Vec2f newPadding) { _spacing = newPadding; resize(); return _spacing; } 45 46 uint capacity() const { return _capacity; } 47 uint capacity(uint newCapacity) { _capacity = newCapacity; resize(); return _capacity; } 48 } 49 50 this() {} 51 52 this(Vec2f newSize) { 53 size = newSize; 54 } 55 56 override void addChild(Widget widget) { 57 super.addChild(widget); 58 resize(); 59 } 60 61 override void onSize() { 62 resize(); 63 } 64 65 protected void resize() { 66 if(!_children.length) 67 return; 68 Vec2f childSize = Vec2f(_size.x, _size.y / (_capacity != 0u ? _capacity : _children.length)); 69 Vec2f origin = (_isFrame ? Vec2f.zero : _position) - _size / 2 + childSize / 2f; 70 foreach(uint id, Widget widget; _children) { 71 widget.position = origin + Vec2f(0f, childSize.y * to!float(id)); 72 widget.size = childSize - _spacing; 73 } 74 } 75 } 76 77 class HLayout: WidgetGroup { 78 private { 79 Vec2f _spacing = Vec2f.zero; 80 uint _capacity; 81 } 82 83 @property { 84 Vec2f spacing() const { return _spacing; } 85 Vec2f spacing(Vec2f newPadding) { _spacing = newPadding; resize(); return _spacing; } 86 87 uint capacity() const { return _capacity; } 88 uint capacity(uint newCapacity) { _capacity = newCapacity; resize(); return _capacity; } 89 } 90 91 this() {} 92 93 this(Vec2f newSize) { 94 size = newSize; 95 } 96 97 override void addChild(Widget widget) { 98 super.addChild(widget); 99 resize(); 100 } 101 102 override void onSize() { 103 resize(); 104 } 105 106 protected void resize() { 107 if(!_children.length) 108 return; 109 Vec2f childSize = Vec2f(_size.x / (_capacity != 0u ? _capacity : _children.length), _size.y); 110 Vec2f origin = (_isFrame ? Vec2f.zero : _position) - _size / 2 + childSize / 2f; 111 foreach(uint id, Widget widget; _children) { 112 widget.position = origin + Vec2f(childSize.x * to!float(id), 0f); 113 widget.size = childSize - _spacing; 114 } 115 } 116 } 117 118 class GridLayout: WidgetGroup { 119 private { 120 Vec2f _spacing = Vec2f.zero; 121 Vec2u _capacity; 122 } 123 124 @property { 125 Vec2f spacing() const { return _spacing; } 126 Vec2f spacing(Vec2f newPadding) { _spacing = newPadding; resize(); return _spacing; } 127 128 Vec2u capacity() const { return _capacity; } 129 Vec2u capacity(Vec2u newCapacity) { _capacity = newCapacity; resize(); return _capacity; } 130 } 131 132 this() {} 133 134 this(Vec2f newSize) { 135 size = newSize; 136 } 137 138 override void addChild(Widget widget) { 139 super.addChild(widget); 140 resize(); 141 } 142 143 override void onSize() { 144 resize(); 145 } 146 147 protected void resize() { 148 if(!_children.length || _capacity.x == 0u) 149 return; 150 151 int yCapacity = _capacity.y; 152 if(yCapacity == 0u) 153 yCapacity = (to!int(_children.length) / _capacity.x) + 1; 154 155 Vec2f childSize = Vec2f(_size.x / _capacity.x, _size.y / yCapacity); 156 Vec2f origin = (_isFrame ? Vec2f.zero : _position) - _size / 2 + childSize / 2f; 157 foreach(uint id, Widget widget; _children) { 158 Vec2u coords = Vec2u(id % _capacity.x, id / _capacity.x); 159 widget.position = origin + Vec2f(childSize.x * coords.x, childSize.y * coords.y); 160 widget.size = childSize - _spacing; 161 } 162 } 163 } 164 165 class VContainer: WidgetGroup { 166 protected { 167 Vec2f _spacing = Vec2f.zero; 168 } 169 170 @property { 171 Vec2f spacing() const { return _spacing; } 172 Vec2f spacing(Vec2f newPadding) { _spacing = newPadding; resize(); return _spacing; } 173 } 174 175 this() {} 176 177 override void addChild(Widget widget) { 178 super.addChild(widget); 179 resize(); 180 } 181 182 override void onPosition() { 183 resize(); 184 } 185 186 override void onSize() { 187 resize(); 188 } 189 190 override void onAnchor() { 191 resize(); 192 } 193 194 protected void resize() { 195 if(!_children.length) { 196 _size = Vec2f.zero; 197 return; 198 } 199 200 Vec2f totalSize = Vec2f.zero; 201 foreach(Widget widget; _children) { 202 totalSize.y += widget.size.y + _spacing.y; 203 totalSize.x = max(totalSize.x, widget.size.x); 204 } 205 _size = totalSize + Vec2f(_spacing.x * 2f, _spacing.y); 206 Vec2f currentPosition = _position - (_size * _anchor) + _spacing; 207 foreach(Widget widget; _children) { 208 widget.position = currentPosition + widget.size / 2f; 209 currentPosition = currentPosition + Vec2f(0f, widget.size.y + _spacing.y); 210 } 211 } 212 } 213 214 class HContainer: WidgetGroup { 215 protected { 216 Vec2f _spacing = Vec2f.zero; 217 } 218 219 @property { 220 Vec2f spacing() const { return _spacing; } 221 Vec2f spacing(Vec2f newPadding) { _spacing = newPadding; resize(); return _spacing; } 222 } 223 224 this() {} 225 226 override void addChild(Widget widget) { 227 super.addChild(widget); 228 resize(); 229 } 230 231 override void onPosition() { 232 resize(); 233 } 234 235 override void onSize() { 236 resize(); 237 } 238 239 override void onAnchor() { 240 resize(); 241 } 242 243 protected void resize() { 244 if(!_children.length) { 245 _size = Vec2f.zero; 246 return; 247 } 248 249 Vec2f totalSize = Vec2f.zero; 250 foreach(Widget widget; _children) { 251 totalSize.y = max(totalSize.y, widget.size.y); 252 totalSize.x += widget.size.x + _spacing.x; 253 } 254 _size = totalSize + Vec2f(_spacing.x, _spacing.y * 2f); 255 Vec2f currentPosition = _position - (_size * _anchor) + _spacing; 256 foreach(Widget widget; _children) { 257 widget.position = currentPosition + widget.size / 2f; 258 currentPosition = currentPosition + Vec2f(widget.size.x + _spacing.x, 0f); 259 } 260 } 261 } 262 263 class AnchoredLayout: WidgetGroup { 264 private { 265 Vec2f[] _childrenPositions, _childrenSizes; 266 } 267 268 this() {} 269 270 this(Vec2f newSize) { 271 size = newSize; 272 } 273 274 override void addChild(Widget widget) { 275 super.addChild(widget); 276 _childrenPositions ~= Vec2f.half; 277 _childrenSizes ~= Vec2f.one; 278 resize(); 279 } 280 281 override void onPosition() { 282 resize(); 283 } 284 285 override void onSize() { 286 resize(); 287 } 288 289 void addChild(Widget widget, Vec2f position, Vec2f size) { 290 super.addChild(widget); 291 _childrenPositions ~= position; 292 _childrenSizes ~= size; 293 resize(); 294 } 295 296 override void removeChildren() { 297 super.removeChildren(); 298 _childrenPositions.length = 0L; 299 _childrenSizes.length = 0L; 300 } 301 302 protected void resize() { 303 if(!_children.length) 304 return; 305 Vec2f origin = (_isFrame ? Vec2f.zero : _position) - _size / 2; 306 foreach(uint id, Widget widget; _children) { 307 widget.position = origin + _size * _childrenPositions[id]; 308 widget.size = _size * _childrenSizes[id]; 309 } 310 } 311 } 312 313 class LogLayout: WidgetGroup { 314 this() {} 315 316 override void addChild(Widget widget) { 317 super.addChild(widget); 318 resize(); 319 } 320 321 override void onSize() { 322 resize(); 323 } 324 325 protected void resize() { 326 if(!_children.length) 327 return; 328 329 Vec2f totalSize = Vec2f.zero; 330 foreach(Widget widget; _children) { 331 totalSize.y += widget.size.y; 332 totalSize.x = max(totalSize.x, widget.size.x); 333 } 334 _size = totalSize; 335 Vec2f currentPosition = _position - _size / 2f; 336 foreach(Widget widget; _children) { 337 widget.position = currentPosition + widget.size / 2f; 338 currentPosition = currentPosition + Vec2f(0f, widget.size.y); 339 } 340 } 341 }