1// Aseprite UI Library
2// Copyright (C) 2018-2022 Igara Studio S.A.
3// Copyright (C) 2001-2017 David Capello
4//
5// This file is released under the terms of the MIT license.
6// Read LICENSE.txt for more information.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "gfx/size.h"
13#include "ui/box.h"
14#include "ui/message.h"
15#include "ui/resize_event.h"
16#include "ui/size_hint_event.h"
17#include "ui/theme.h"
18
19#include <algorithm>
20
21namespace ui {
22
23using namespace gfx;
24
25Box::Box(int align)
26 : Widget(kBoxWidget)
27{
28 enableFlags(IGNORE_MOUSE);
29 setAlign(align);
30 initTheme();
31}
32
33void Box::onSizeHint(SizeHintEvent& ev)
34{
35#define ADD_CHILD_SIZE(w, h) { \
36 if (align() & HOMOGENEOUS) \
37 prefSize.w = std::max(prefSize.w, childSize.w); \
38 else \
39 prefSize.w += childSize.w; \
40 prefSize.h = std::max(prefSize.h, childSize.h); \
41 }
42
43#define FINAL_ADJUSTMENT(w) { \
44 if (align() & HOMOGENEOUS) \
45 prefSize.w *= visibleChildren; \
46 prefSize.w += childSpacing() * (visibleChildren-1); \
47 }
48
49 int visibleChildren = 0;
50 for (auto child : children()) {
51 if (!child->hasFlags(HIDDEN))
52 ++visibleChildren;
53 }
54
55 Size prefSize(0, 0);
56 for (auto child : children()) {
57 if (child->hasFlags(HIDDEN))
58 continue;
59
60 Size childSize = child->sizeHint();
61 if (align() & HORIZONTAL) {
62 ADD_CHILD_SIZE(w, h);
63 }
64 else {
65 ADD_CHILD_SIZE(h, w);
66 }
67 }
68
69 if (visibleChildren > 0) {
70 if (align() & HORIZONTAL) {
71 FINAL_ADJUSTMENT(w);
72 }
73 else {
74 FINAL_ADJUSTMENT(h);
75 }
76 }
77
78 prefSize.w += border().width();
79 prefSize.h += border().height();
80
81 ev.setSizeHint(prefSize);
82}
83
84void Box::onResize(ResizeEvent& ev)
85{
86#define LAYOUT_CHILDREN(x, y, w, h) { \
87 availExtraSize = availSize.w - prefSize.w; \
88 availSize.w -= childSpacing() * (visibleChildren-1); \
89 if (align() & HOMOGENEOUS) \
90 homogeneousSize = availSize.w / visibleChildren; \
91 \
92 Rect defChildPos(childrenBounds()); \
93 int i = 0, j = 0; \
94 for (auto child : children()) { \
95 if (child->hasFlags(HIDDEN)) \
96 continue; \
97 \
98 int size = 0; \
99 \
100 if (align() & HOMOGENEOUS) { \
101 if (i < visibleChildren-1) \
102 size = homogeneousSize; \
103 else \
104 size = availSize.w; \
105 } \
106 else { \
107 size = child->sizeHint().w; \
108 \
109 if (child->isExpansive()) { \
110 int extraSize = (availExtraSize / (expansiveChildren-j)); \
111 size += extraSize; \
112 availExtraSize -= extraSize; \
113 if (++j == expansiveChildren) \
114 size += availExtraSize; \
115 } \
116 } \
117 \
118 Rect childPos = defChildPos; \
119 childPos.w = size = std::clamp(size, child->minSize().w, child->maxSize().w); \
120 childPos.h = std::clamp(childPos.h, child->minSize().h, child->maxSize().h); \
121 child->setBounds(childPos); \
122 \
123 defChildPos.x += size + childSpacing(); \
124 availSize.w -= size; \
125 ++i; \
126 } \
127 }
128
129 setBoundsQuietly(ev.bounds());
130
131 int visibleChildren = 0;
132 int expansiveChildren = 0;
133 for (auto child : children()) {
134 if (!child->hasFlags(HIDDEN)) {
135 ++visibleChildren;
136 if (child->isExpansive())
137 ++expansiveChildren;
138 }
139 }
140
141 if (visibleChildren > 0) {
142 Size prefSize(sizeHint());
143 Size availSize(childrenBounds().size());
144 int homogeneousSize = 0;
145 int availExtraSize = 0;
146
147 prefSize.w -= border().width();
148 prefSize.h -= border().height();
149
150 if (align() & HORIZONTAL) {
151 LAYOUT_CHILDREN(x, y, w, h);
152 }
153 else {
154 LAYOUT_CHILDREN(y, x, h, w);
155 }
156 }
157}
158
159} // namespace ui
160