xbintsc 0.3.18 → 0.3.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/src/cli/main.js +124 -30
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.js +8 -1
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/state.js +20 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +6 -0
- package/dist/src/codegen/generator/tables.js +38 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/commonjs.d.ts +52 -0
- package/dist/src/driver/bundler/commonjs.js +493 -0
- package/dist/src/driver/bundler/commonjs.js.map +1 -0
- package/dist/src/driver/bundler/graph.d.ts +2 -1
- package/dist/src/driver/bundler/graph.js +32 -3
- package/dist/src/driver/bundler/graph.js.map +1 -1
- package/dist/src/driver/bundler/merge.d.ts +2 -1
- package/dist/src/driver/bundler/merge.js +16 -2
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/bundler/types.d.ts +8 -0
- package/dist/src/driver/compiler.d.ts +8 -1
- package/dist/src/driver/compiler.js +75 -7
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/config.d.ts +53 -0
- package/dist/src/driver/config.js +141 -0
- package/dist/src/driver/config.js.map +1 -0
- package/dist/src/driver/icon.d.ts +62 -0
- package/dist/src/driver/icon.js +151 -0
- package/dist/src/driver/icon.js.map +1 -0
- package/dist/src/driver/index.d.ts +4 -0
- package/dist/src/driver/index.js +4 -0
- package/dist/src/driver/index.js.map +1 -1
- package/dist/src/driver/mac-bundle.d.ts +41 -0
- package/dist/src/driver/mac-bundle.js +123 -0
- package/dist/src/driver/mac-bundle.js.map +1 -0
- package/dist/src/driver/win-icon.d.ts +27 -0
- package/dist/src/driver/win-icon.js +80 -0
- package/dist/src/driver/win-icon.js.map +1 -0
- package/dist/src/extensions/gui/html.d.ts +25 -0
- package/dist/src/extensions/gui/html.js +203 -0
- package/dist/src/extensions/gui/html.js.map +1 -0
- package/dist/src/extensions/gui/index.js +11 -0
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/registry.d.ts +29 -0
- package/dist/src/extensions/registry.js +14 -0
- package/dist/src/extensions/registry.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/lexer/scanner/identifiers.js +4 -12
- package/dist/src/lexer/scanner/identifiers.js.map +1 -1
- package/dist/src/parser/statements.js +17 -4
- package/dist/src/parser/statements.js.map +1 -1
- package/dist/tests/cli/main.test.js +46 -1
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/driver/bundler-resolve.test.d.ts +1 -0
- package/dist/tests/driver/bundler-resolve.test.js +230 -0
- package/dist/tests/driver/bundler-resolve.test.js.map +1 -0
- package/dist/tests/driver/config.test.d.ts +1 -0
- package/dist/tests/driver/config.test.js +124 -0
- package/dist/tests/driver/config.test.js.map +1 -0
- package/dist/tests/driver/icon.test.d.ts +1 -0
- package/dist/tests/driver/icon.test.js +421 -0
- package/dist/tests/driver/icon.test.js.map +1 -0
- package/dist/tests/driver/modules.test.js +177 -0
- package/dist/tests/driver/modules.test.js.map +1 -1
- package/dist/tests/e2e/fetch.test.d.ts +12 -0
- package/dist/tests/e2e/fetch.test.js +272 -0
- package/dist/tests/e2e/fetch.test.js.map +1 -0
- package/dist/tests/e2e/gui-dom.test.d.ts +5 -0
- package/dist/tests/e2e/gui-dom.test.js +102 -0
- package/dist/tests/e2e/gui-dom.test.js.map +1 -0
- package/dist/tests/e2e/gui-events.test.d.ts +5 -0
- package/dist/tests/e2e/gui-events.test.js +60 -0
- package/dist/tests/e2e/gui-events.test.js.map +1 -0
- package/dist/tests/e2e/gui-helpers.d.ts +31 -0
- package/dist/tests/e2e/gui-helpers.js +74 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -0
- package/dist/tests/e2e/gui-layout.test.d.ts +4 -0
- package/dist/tests/e2e/gui-layout.test.js +88 -0
- package/dist/tests/e2e/gui-layout.test.js.map +1 -0
- package/dist/tests/e2e/gui-lifecycle.test.d.ts +5 -0
- package/dist/tests/e2e/gui-lifecycle.test.js +58 -0
- package/dist/tests/e2e/gui-lifecycle.test.js.map +1 -0
- package/dist/tests/e2e/gui-paint.test.d.ts +5 -0
- package/dist/tests/e2e/gui-paint.test.js +111 -0
- package/dist/tests/e2e/gui-paint.test.js.map +1 -0
- package/dist/tests/e2e/gui-script.test.d.ts +5 -0
- package/dist/tests/e2e/gui-script.test.js +78 -0
- package/dist/tests/e2e/gui-script.test.js.map +1 -0
- package/dist/tests/e2e/gui-style.test.d.ts +5 -0
- package/dist/tests/e2e/gui-style.test.js +175 -0
- package/dist/tests/e2e/gui-style.test.js.map +1 -0
- package/dist/tests/e2e/icon.test.d.ts +7 -0
- package/dist/tests/e2e/icon.test.js +80 -0
- package/dist/tests/e2e/icon.test.js.map +1 -0
- package/dist/tests/e2e/npm-modules.test.d.ts +4 -1
- package/dist/tests/e2e/npm-modules.test.js +47 -1
- package/dist/tests/e2e/npm-modules.test.js.map +1 -1
- package/dist/tests/e2e/runtime-coverage-typed-array.test.d.ts +9 -0
- package/dist/tests/e2e/runtime-coverage-typed-array.test.js +140 -0
- package/dist/tests/e2e/runtime-coverage-typed-array.test.js.map +1 -0
- package/dist/tests/extensions/gui-index.test.d.ts +1 -0
- package/dist/tests/extensions/gui-index.test.js +49 -0
- package/dist/tests/extensions/gui-index.test.js.map +1 -0
- package/dist/tests/extensions/gui.test.d.ts +1 -0
- package/dist/tests/extensions/gui.test.js +177 -0
- package/dist/tests/extensions/gui.test.js.map +1 -0
- package/dist/tests/extensions/registry.test.js +36 -0
- package/dist/tests/extensions/registry.test.js.map +1 -1
- package/dist/tests/parser/statements.test.js +11 -0
- package/dist/tests/parser/statements.test.js.map +1 -1
- package/dist/tests/public-api.test.d.ts +1 -0
- package/dist/tests/public-api.test.js +31 -0
- package/dist/tests/public-api.test.js.map +1 -0
- package/package.json +1 -1
- package/runtime/ext_gui/css.cpp +4 -517
- package/runtime/ext_gui/css_internal.h +27 -0
- package/runtime/ext_gui/css_match.cpp +195 -0
- package/runtime/ext_gui/css_selector.cpp +207 -0
- package/runtime/ext_gui/css_sheet.cpp +129 -0
- package/runtime/ext_gui/document.cpp +90 -0
- package/runtime/ext_gui/document.h +23 -0
- package/runtime/ext_gui/dom.cpp +43 -403
- package/runtime/ext_gui/dom.h +7 -0
- package/runtime/ext_gui/dom_api.cpp +164 -0
- package/runtime/ext_gui/dom_api.h +12 -0
- package/runtime/ext_gui/dom_api_document.cpp +96 -0
- package/runtime/ext_gui/dom_api_events.cpp +123 -0
- package/runtime/ext_gui/dom_api_internal.h +135 -0
- package/runtime/ext_gui/dom_api_node.cpp +329 -0
- package/runtime/ext_gui/dom_api_node_read.cpp +294 -0
- package/runtime/ext_gui/dom_api_proto.cpp +130 -0
- package/runtime/ext_gui/dom_api_style.cpp +192 -0
- package/runtime/ext_gui/dom_internal.h +39 -0
- package/runtime/ext_gui/dom_parse.cpp +348 -0
- package/runtime/ext_gui/dom_serialize.cpp +54 -0
- package/runtime/ext_gui/gui.cpp +61 -245
- package/runtime/ext_gui/gui_engine.h +54 -0
- package/runtime/ext_gui/gui_events.cpp +294 -0
- package/runtime/ext_gui/gui_internal.h +29 -0
- package/runtime/ext_gui/layout.cpp +6 -597
- package/runtime/ext_gui/layout_flex.cpp +200 -0
- package/runtime/ext_gui/layout_flow.cpp +191 -0
- package/runtime/ext_gui/layout_flow_internal.h +149 -0
- package/runtime/ext_gui/layout_inline.cpp +181 -0
- package/runtime/ext_gui/layout_internal.h +20 -0
- package/runtime/ext_gui/renderer.cpp +15 -679
- package/runtime/ext_gui/renderer_image.cpp +233 -0
- package/runtime/ext_gui/renderer_internal.h +112 -0
- package/runtime/ext_gui/renderer_shaders.h +108 -0
- package/runtime/ext_gui/renderer_text.cpp +342 -0
- package/runtime/ext_gui/script.cpp +63 -0
- package/runtime/ext_gui/style.cpp +12 -660
- package/runtime/ext_gui/style_internal.h +33 -0
- package/runtime/ext_gui/style_print.cpp +113 -0
- package/runtime/ext_gui/style_ua.cpp +98 -0
- package/runtime/ext_gui/style_values.cpp +468 -0
- package/runtime/ext_gui/window.cpp +60 -0
- package/runtime/rt.h +6 -0
- package/runtime/rt_internal.h +4 -0
- package/runtime/xt_builtins.c +1 -0
- package/runtime/xt_containers/access.inc +32 -1
- package/runtime/xt_fetch/buffer.inc +64 -0
- package/runtime/xt_fetch/headers.inc +312 -0
- package/runtime/xt_fetch/request.inc +386 -0
- package/runtime/xt_fetch/response.inc +107 -0
- package/runtime/xt_fetch/socket.inc +72 -0
- package/runtime/xt_fetch/url.inc +108 -0
- package/runtime/xt_fetch.c +39 -0
- package/runtime/xt_stdlib2/set.inc +1 -1
- package/runtime/xt_typed_array.c +614 -0
- package/src/cli/main.ts +137 -29
- package/src/codegen/generator/calls/access.ts +9 -1
- package/src/codegen/generator/state.ts +20 -0
- package/src/codegen/generator/tables.ts +40 -0
- package/src/driver/bundler/commonjs.ts +553 -0
- package/src/driver/bundler/graph.ts +35 -2
- package/src/driver/bundler/merge.ts +18 -1
- package/src/driver/bundler/types.ts +8 -0
- package/src/driver/compiler.ts +85 -7
- package/src/driver/config.ts +173 -0
- package/src/driver/icon.ts +184 -0
- package/src/driver/index.ts +30 -0
- package/src/driver/mac-bundle.ts +156 -0
- package/src/driver/win-icon.ts +96 -0
- package/src/extensions/gui/html.ts +236 -0
- package/src/extensions/gui/index.ts +11 -0
- package/src/extensions/registry.ts +39 -0
- package/src/index.ts +1 -1
- package/src/lexer/scanner/identifiers.ts +4 -11
- package/src/parser/statements.ts +17 -4
- package/dist/tests/e2e/gui.test.d.ts +0 -12
- package/dist/tests/e2e/gui.test.js +0 -493
- package/dist/tests/e2e/gui.test.js.map +0 -1
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — flex formatting context.
|
|
3
|
+
*
|
|
4
|
+
* Sizes flex items along the main axis (grow/shrink/basis), resolves the cross
|
|
5
|
+
* axis (align-items) and places them (justify-content), reusing the block and
|
|
6
|
+
* inline helpers from `layout_flow.cpp` / `layout_inline.cpp`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include "layout_flow_internal.h"
|
|
10
|
+
|
|
11
|
+
namespace xtgui {
|
|
12
|
+
namespace layout_flow_detail {
|
|
13
|
+
|
|
14
|
+
float Layouter::layoutFlex(LayoutBox *container, float contentWidth, float definiteHeight) const {
|
|
15
|
+
const XtStyle &containerStyle = *container->style;
|
|
16
|
+
bool isRow = containerStyle.flex_direction == FlexDirection::Row ||
|
|
17
|
+
containerStyle.flex_direction == FlexDirection::RowReverse;
|
|
18
|
+
bool reverse = containerStyle.flex_direction == FlexDirection::RowReverse ||
|
|
19
|
+
containerStyle.flex_direction == FlexDirection::ColumnReverse;
|
|
20
|
+
float gap = px(containerStyle.gap, contentWidth, containerStyle.font_size, 0.0f);
|
|
21
|
+
|
|
22
|
+
std::vector<LayoutBox *> items;
|
|
23
|
+
for (const std::unique_ptr<LayoutBox> &child : container->children) {
|
|
24
|
+
if (child->display != Display::None) items.push_back(child.get());
|
|
25
|
+
}
|
|
26
|
+
if (items.empty()) return 0;
|
|
27
|
+
size_t count = items.size();
|
|
28
|
+
|
|
29
|
+
if (isRow) {
|
|
30
|
+
float mainSize = contentWidth;
|
|
31
|
+
std::vector<float> outer(count, 0.0f);
|
|
32
|
+
std::vector<float> mainSizes(count, 0.0f);
|
|
33
|
+
float sumBase = 0;
|
|
34
|
+
for (size_t i = 0; i < count; i++) {
|
|
35
|
+
const XtStyle &style = *items[i]->style;
|
|
36
|
+
float basis;
|
|
37
|
+
if (isAuto(style.flex_basis)) {
|
|
38
|
+
if (!isAuto(style.width)) basis = px(style.width, mainSize, style.font_size, 0.0f);
|
|
39
|
+
else basis = preferredContentWidth(items[i], mainSize);
|
|
40
|
+
} else {
|
|
41
|
+
basis = px(style.flex_basis, mainSize, style.font_size, 0.0f);
|
|
42
|
+
}
|
|
43
|
+
resolveEdges(items[i], mainSize);
|
|
44
|
+
float edges = horizontalEdges(items[i]);
|
|
45
|
+
outer[i] = basis + edges;
|
|
46
|
+
mainSizes[i] = outer[i];
|
|
47
|
+
sumBase += outer[i];
|
|
48
|
+
}
|
|
49
|
+
float free = mainSize - sumBase - gap * (float)(count - 1);
|
|
50
|
+
if (free > 0) {
|
|
51
|
+
float totalGrow = 0;
|
|
52
|
+
for (LayoutBox *item : items) totalGrow += item->style->flex_grow;
|
|
53
|
+
if (totalGrow > 0) {
|
|
54
|
+
for (size_t i = 0; i < count; i++)
|
|
55
|
+
mainSizes[i] += free * items[i]->style->flex_grow / totalGrow;
|
|
56
|
+
}
|
|
57
|
+
} else if (free < 0) {
|
|
58
|
+
float totalShrink = 0;
|
|
59
|
+
for (size_t i = 0; i < count; i++) totalShrink += items[i]->style->flex_shrink * outer[i];
|
|
60
|
+
if (totalShrink > 0) {
|
|
61
|
+
for (size_t i = 0; i < count; i++) {
|
|
62
|
+
mainSizes[i] += free * (items[i]->style->flex_shrink * outer[i]) / totalShrink;
|
|
63
|
+
mainSizes[i] = std::max(0.0f, mainSizes[i]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Cross sizes: lay out once to measure, then apply cross alignment. */
|
|
69
|
+
float crossSize = definiteHeight >= 0 ? definiteHeight : 0;
|
|
70
|
+
std::vector<float> cross(count, 0.0f);
|
|
71
|
+
for (size_t i = 0; i < count; i++) {
|
|
72
|
+
float contentW = mainSizes[i] - horizontalEdges(items[i]);
|
|
73
|
+
if (contentW < 0) contentW = 0;
|
|
74
|
+
layoutBlock(items[i], container->content_x, container->content_y, contentWidth,
|
|
75
|
+
definiteHeight, contentW);
|
|
76
|
+
cross[i] = verticalEdges(items[i]) + items[i]->height;
|
|
77
|
+
if (definiteHeight < 0) crossSize = std::max(crossSize, cross[i]);
|
|
78
|
+
}
|
|
79
|
+
if (containerStyle.align_items == AlignItems::Stretch && definiteHeight >= 0) {
|
|
80
|
+
for (size_t i = 0; i < count; i++) {
|
|
81
|
+
if (isAuto(items[i]->style->height)) {
|
|
82
|
+
float contentH = crossSize - verticalEdges(items[i]);
|
|
83
|
+
if (contentH < 0) contentH = 0;
|
|
84
|
+
float contentW = mainSizes[i] - horizontalEdges(items[i]);
|
|
85
|
+
layoutBlock(items[i], container->content_x, container->content_y, contentWidth,
|
|
86
|
+
definiteHeight, contentW, contentH);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* justify-content: place the main axis. */
|
|
92
|
+
float freeSpace = mainSize - (sumOf(mainSizes) + gap * (float)(count - 1));
|
|
93
|
+
float offset = 0;
|
|
94
|
+
float between = gap;
|
|
95
|
+
applyJustify(containerStyle.justify_content, freeSpace, count, &offset, &between);
|
|
96
|
+
|
|
97
|
+
float cursor = container->content_x + offset;
|
|
98
|
+
for (size_t k = 0; k < count; k++) {
|
|
99
|
+
size_t i = reverse ? (count - 1 - k) : k;
|
|
100
|
+
float crossOffset = crossOffsetFor(containerStyle.align_items, crossSize, cross[i]);
|
|
101
|
+
float dx = cursor - items[i]->x;
|
|
102
|
+
float dy = container->content_y + crossOffset - items[i]->y;
|
|
103
|
+
translate(items[i], dx, dy);
|
|
104
|
+
cursor += mainSizes[i] + between;
|
|
105
|
+
}
|
|
106
|
+
return crossSize;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Column flex. */
|
|
110
|
+
float mainSize = definiteHeight >= 0 ? definiteHeight : -1;
|
|
111
|
+
std::vector<float> outer(count, 0.0f);
|
|
112
|
+
float sumBase = 0;
|
|
113
|
+
for (size_t i = 0; i < count; i++) {
|
|
114
|
+
layoutBlock(items[i], container->content_x, container->content_y, contentWidth, definiteHeight);
|
|
115
|
+
outer[i] = verticalEdges(items[i]) + items[i]->height;
|
|
116
|
+
sumBase += outer[i];
|
|
117
|
+
}
|
|
118
|
+
std::vector<float> mainSizes = outer;
|
|
119
|
+
if (mainSize >= 0) {
|
|
120
|
+
float free = mainSize - sumBase - gap * (float)(count - 1);
|
|
121
|
+
if (free > 0) {
|
|
122
|
+
float totalGrow = 0;
|
|
123
|
+
for (LayoutBox *item : items) totalGrow += item->style->flex_grow;
|
|
124
|
+
if (totalGrow > 0) {
|
|
125
|
+
for (size_t i = 0; i < count; i++) {
|
|
126
|
+
mainSizes[i] += free * items[i]->style->flex_grow / totalGrow;
|
|
127
|
+
float contentH = mainSizes[i] - verticalEdges(items[i]);
|
|
128
|
+
if (contentH < 0) contentH = 0;
|
|
129
|
+
layoutBlock(items[i], container->content_x, container->content_y, contentWidth,
|
|
130
|
+
definiteHeight, -1, contentH);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
mainSize = sumBase + gap * (float)(count - 1);
|
|
136
|
+
}
|
|
137
|
+
float freeSpace = mainSize - (sumOf(mainSizes) + gap * (float)(count - 1));
|
|
138
|
+
float offset = 0;
|
|
139
|
+
float between = gap;
|
|
140
|
+
applyJustify(containerStyle.justify_content, freeSpace, count, &offset, &between);
|
|
141
|
+
|
|
142
|
+
float cursor = container->content_y + offset;
|
|
143
|
+
for (size_t k = 0; k < count; k++) {
|
|
144
|
+
size_t i = reverse ? (count - 1 - k) : k;
|
|
145
|
+
float dx = container->content_x - items[i]->x;
|
|
146
|
+
float dy = cursor - items[i]->y;
|
|
147
|
+
translate(items[i], dx, dy);
|
|
148
|
+
cursor += mainSizes[i] + between;
|
|
149
|
+
}
|
|
150
|
+
return mainSize;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
float Layouter::sumOf(const std::vector<float> &values) {
|
|
154
|
+
float total = 0;
|
|
155
|
+
for (float value : values) total += value;
|
|
156
|
+
return total;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
void Layouter::applyJustify(JustifyContent justify, float freeSpace, size_t count, float *offset,
|
|
160
|
+
float *between) {
|
|
161
|
+
if (count == 0) return;
|
|
162
|
+
if (freeSpace < 0) freeSpace = 0;
|
|
163
|
+
switch (justify) {
|
|
164
|
+
case JustifyContent::FlexEnd:
|
|
165
|
+
*offset = freeSpace;
|
|
166
|
+
break;
|
|
167
|
+
case JustifyContent::Center:
|
|
168
|
+
*offset = freeSpace / 2.0f;
|
|
169
|
+
break;
|
|
170
|
+
case JustifyContent::SpaceBetween:
|
|
171
|
+
if (count > 1) *between += freeSpace / (float)(count - 1);
|
|
172
|
+
break;
|
|
173
|
+
case JustifyContent::SpaceAround:
|
|
174
|
+
*between += freeSpace / (float)count;
|
|
175
|
+
*offset = freeSpace / (2.0f * (float)count);
|
|
176
|
+
break;
|
|
177
|
+
case JustifyContent::SpaceEvenly:
|
|
178
|
+
*between += freeSpace / (float)(count + 1);
|
|
179
|
+
*offset = freeSpace / (float)(count + 1);
|
|
180
|
+
break;
|
|
181
|
+
default:
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
float Layouter::crossOffsetFor(AlignItems align, float crossSize, float itemCross) {
|
|
187
|
+
switch (align) {
|
|
188
|
+
case AlignItems::FlexEnd:
|
|
189
|
+
return crossSize - itemCross;
|
|
190
|
+
case AlignItems::Center:
|
|
191
|
+
return (crossSize - itemCross) / 2.0f;
|
|
192
|
+
case AlignItems::Baseline:
|
|
193
|
+
return crossSize - itemCross;
|
|
194
|
+
default:
|
|
195
|
+
return 0.0f;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
} // namespace layout_flow_detail
|
|
200
|
+
} // namespace xtgui
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — block/flow layout core.
|
|
3
|
+
*
|
|
4
|
+
* The box tree is built in `layout.cpp`; this unit turns that tree into
|
|
5
|
+
* positioned boxes and owns the `Layouter` methods shared by the inline
|
|
6
|
+
* (`layout_inline.cpp`) and flex (`layout_flex.cpp`) formatting contexts.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include "layout_flow_internal.h"
|
|
10
|
+
|
|
11
|
+
namespace xtgui {
|
|
12
|
+
namespace layout_flow_detail {
|
|
13
|
+
|
|
14
|
+
const XtStyle *Layouter::styleOf(const Node *node) const {
|
|
15
|
+
auto it = styles->find(node);
|
|
16
|
+
return it == styles->end() ? nullptr : &it->second;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
float Layouter::px(const Length &length, float base, float em, float fallback) const {
|
|
20
|
+
return toPx(length, base, em, rootFontSize, vw, vh, fallback);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
float Layouter::clampSize(float value, const Length &minLength, const Length &maxLength, float base,
|
|
24
|
+
float em) const {
|
|
25
|
+
if (!isAuto(minLength)) value = std::max(value, px(minLength, base, em, value));
|
|
26
|
+
if (!isAuto(maxLength)) value = std::min(value, px(maxLength, base, em, value));
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
float Layouter::textWidth(const std::string &text, const XtStyle &style) const {
|
|
31
|
+
return xt_text_measure_width(text, specOf(style));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
float Layouter::horizontalEdges(const LayoutBox *box) const {
|
|
35
|
+
return box->margin_left + box->margin_right + box->border_left + box->border_right +
|
|
36
|
+
box->padding_left + box->padding_right;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
float Layouter::verticalEdges(const LayoutBox *box) const {
|
|
40
|
+
return box->margin_top + box->margin_bottom + box->border_top + box->border_bottom +
|
|
41
|
+
box->padding_top + box->padding_bottom;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
float Layouter::preferredContentWidth(const LayoutBox *box, float available) const {
|
|
45
|
+
const XtStyle &style = *box->style;
|
|
46
|
+
if (!isAuto(style.width)) return px(style.width, available, style.font_size, 0.0f);
|
|
47
|
+
if (box->is_image && box->intrinsic_width > 0) return box->intrinsic_width;
|
|
48
|
+
float best = 0;
|
|
49
|
+
for (const std::unique_ptr<LayoutBox> &child : box->children) {
|
|
50
|
+
if (child->display == Display::None) continue;
|
|
51
|
+
if (child->is_text) {
|
|
52
|
+
best = std::max(best, textWidth(child->text, *child->style));
|
|
53
|
+
} else {
|
|
54
|
+
best = std::max(best,
|
|
55
|
+
preferredContentWidth(child.get(), available) + horizontalEdges(child.get()));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return best;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
void Layouter::translate(LayoutBox *box, float dx, float dy) const {
|
|
62
|
+
box->x += dx;
|
|
63
|
+
box->y += dy;
|
|
64
|
+
box->content_x += dx;
|
|
65
|
+
box->content_y += dy;
|
|
66
|
+
for (TextFragment &fragment : box->fragments) {
|
|
67
|
+
fragment.x += dx;
|
|
68
|
+
fragment.y += dy;
|
|
69
|
+
}
|
|
70
|
+
for (std::unique_ptr<LayoutBox> &child : box->children) translate(child.get(), dx, dy);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
void Layouter::resolveEdges(LayoutBox *box, float containingWidth) const {
|
|
74
|
+
const XtStyle &style = *box->style;
|
|
75
|
+
float em = style.font_size;
|
|
76
|
+
box->margin_top = px(style.margin.top, containingWidth, em);
|
|
77
|
+
box->margin_right = px(style.margin.right, containingWidth, em);
|
|
78
|
+
box->margin_bottom = px(style.margin.bottom, containingWidth, em);
|
|
79
|
+
box->margin_left = px(style.margin.left, containingWidth, em);
|
|
80
|
+
box->padding_top = px(style.padding.top, containingWidth, em);
|
|
81
|
+
box->padding_right = px(style.padding.right, containingWidth, em);
|
|
82
|
+
box->padding_bottom = px(style.padding.bottom, containingWidth, em);
|
|
83
|
+
box->padding_left = px(style.padding.left, containingWidth, em);
|
|
84
|
+
if (style.border_style == BorderStyle::None) {
|
|
85
|
+
box->border_top = box->border_right = box->border_bottom = box->border_left = 0;
|
|
86
|
+
} else {
|
|
87
|
+
box->border_top = px(style.border_width.top, containingWidth, em);
|
|
88
|
+
box->border_right = px(style.border_width.right, containingWidth, em);
|
|
89
|
+
box->border_bottom = px(style.border_width.bottom, containingWidth, em);
|
|
90
|
+
box->border_left = px(style.border_width.left, containingWidth, em);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
float Layouter::layoutBlock(LayoutBox *box, float x, float y, float availableWidth,
|
|
95
|
+
float availableHeight, float forcedContentWidth,
|
|
96
|
+
float forcedContentHeight) const {
|
|
97
|
+
const XtStyle &style = *box->style;
|
|
98
|
+
float em = style.font_size;
|
|
99
|
+
resolveEdges(box, availableWidth);
|
|
100
|
+
|
|
101
|
+
float contentWidth;
|
|
102
|
+
if (forcedContentWidth >= 0) {
|
|
103
|
+
contentWidth = forcedContentWidth;
|
|
104
|
+
} else if (isAuto(style.width)) {
|
|
105
|
+
contentWidth = availableWidth - horizontalEdges(box);
|
|
106
|
+
} else {
|
|
107
|
+
contentWidth = px(style.width, availableWidth, em);
|
|
108
|
+
}
|
|
109
|
+
contentWidth = clampSize(contentWidth, style.min_width, style.max_width, availableWidth, em);
|
|
110
|
+
if (contentWidth < 0) contentWidth = 0;
|
|
111
|
+
|
|
112
|
+
box->x = x + box->margin_left;
|
|
113
|
+
box->y = y + box->margin_top;
|
|
114
|
+
box->content_x = box->x + box->border_left + box->padding_left;
|
|
115
|
+
box->content_y = box->y + box->border_top + box->padding_top;
|
|
116
|
+
box->content_width = contentWidth;
|
|
117
|
+
box->width =
|
|
118
|
+
box->border_left + box->padding_left + contentWidth + box->padding_right + box->border_right;
|
|
119
|
+
|
|
120
|
+
float definiteHeight = -1;
|
|
121
|
+
if (!isAuto(style.height) && !(style.height.unit == Unit::Percent && availableHeight < 0)) {
|
|
122
|
+
definiteHeight = px(style.height, availableHeight, em, -1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
float contentHeight;
|
|
126
|
+
if (forcedContentHeight >= 0) {
|
|
127
|
+
layoutChildren(box, contentWidth, availableHeight, forcedContentHeight);
|
|
128
|
+
contentHeight = forcedContentHeight;
|
|
129
|
+
} else {
|
|
130
|
+
contentHeight = layoutChildren(box, contentWidth, availableHeight, definiteHeight);
|
|
131
|
+
if (definiteHeight >= 0) contentHeight = definiteHeight;
|
|
132
|
+
contentHeight = clampSize(contentHeight, style.min_height, style.max_height,
|
|
133
|
+
availableHeight < 0 ? contentHeight : availableHeight, em);
|
|
134
|
+
if (contentHeight < 0) contentHeight = 0;
|
|
135
|
+
}
|
|
136
|
+
/* Replaced elements keep their aspect ratio when only one axis is set. */
|
|
137
|
+
if (box->is_image && forcedContentHeight < 0 && isAuto(style.height) && box->intrinsic_width > 0 &&
|
|
138
|
+
box->intrinsic_height > 0) {
|
|
139
|
+
contentHeight = contentWidth * box->intrinsic_height / box->intrinsic_width;
|
|
140
|
+
}
|
|
141
|
+
box->content_height = contentHeight;
|
|
142
|
+
box->height =
|
|
143
|
+
box->border_top + box->padding_top + contentHeight + box->padding_bottom + box->border_bottom;
|
|
144
|
+
box->baseline = box->height;
|
|
145
|
+
return box->margin_top + box->height + box->margin_bottom;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
float Layouter::layoutChildren(LayoutBox *block, float contentWidth, float availableHeight,
|
|
149
|
+
float definiteHeight) const {
|
|
150
|
+
if (block->style->display == Display::Flex)
|
|
151
|
+
return layoutFlex(block, contentWidth, definiteHeight);
|
|
152
|
+
|
|
153
|
+
float y = block->content_y;
|
|
154
|
+
float startY = y;
|
|
155
|
+
std::vector<LayoutBox *> inlineRun;
|
|
156
|
+
auto flush = [&]() {
|
|
157
|
+
if (inlineRun.empty()) return;
|
|
158
|
+
y += layoutInlineRun(block, inlineRun, contentWidth, y);
|
|
159
|
+
inlineRun.clear();
|
|
160
|
+
};
|
|
161
|
+
for (const std::unique_ptr<LayoutBox> &child : block->children) {
|
|
162
|
+
Display display = child->display;
|
|
163
|
+
if (display == Display::None) continue;
|
|
164
|
+
if (display == Display::Block || display == Display::ListItem || display == Display::Flex) {
|
|
165
|
+
flush();
|
|
166
|
+
y += layoutBlock(child.get(), block->content_x, y, contentWidth, availableHeight);
|
|
167
|
+
} else {
|
|
168
|
+
inlineRun.push_back(child.get());
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
flush();
|
|
172
|
+
return y - startY;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
} // namespace layout_flow_detail
|
|
176
|
+
|
|
177
|
+
float xt_layout_text_width(const std::string &text, const XtStyle &style) {
|
|
178
|
+
return xt_text_measure_width(text, layout_flow_detail::specOf(style));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
void xt_layout_run(LayoutBox *root, const std::unordered_map<const Node *, XtStyle> &styles,
|
|
182
|
+
float viewportWidth, float viewportHeight) {
|
|
183
|
+
layout_flow_detail::Layouter layouter;
|
|
184
|
+
layouter.styles = &styles;
|
|
185
|
+
layouter.tree = nullptr;
|
|
186
|
+
layouter.vw = viewportWidth;
|
|
187
|
+
layouter.vh = viewportHeight;
|
|
188
|
+
layouter.layoutBlock(root, 0, 0, viewportWidth, viewportHeight);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
} // namespace xtgui
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — shared internals for the flow layouter.
|
|
3
|
+
*
|
|
4
|
+
* `Layouter` is split by formatting context across three translation units:
|
|
5
|
+
* - layout_flow.cpp block/flow core and the public entry points
|
|
6
|
+
* - layout_inline.cpp inline formatting context (line breaking, text runs)
|
|
7
|
+
* - layout_flex.cpp flex formatting context
|
|
8
|
+
* The struct and its data members live here so each unit can define methods.
|
|
9
|
+
*/
|
|
10
|
+
#ifndef XT_GUI_LAYOUT_FLOW_INTERNAL_H
|
|
11
|
+
#define XT_GUI_LAYOUT_FLOW_INTERNAL_H
|
|
12
|
+
|
|
13
|
+
#include <algorithm>
|
|
14
|
+
#include <cfloat>
|
|
15
|
+
#include <cstring>
|
|
16
|
+
#include <string>
|
|
17
|
+
#include <unordered_map>
|
|
18
|
+
#include <vector>
|
|
19
|
+
|
|
20
|
+
#include "layout_internal.h"
|
|
21
|
+
#include "text.h"
|
|
22
|
+
|
|
23
|
+
namespace xtgui {
|
|
24
|
+
namespace layout_flow_detail {
|
|
25
|
+
|
|
26
|
+
constexpr float kHuge = 1e18f;
|
|
27
|
+
|
|
28
|
+
struct InlineItem {
|
|
29
|
+
LayoutBox *box = nullptr;
|
|
30
|
+
const XtStyle *style = nullptr;
|
|
31
|
+
std::string text;
|
|
32
|
+
float width = 0;
|
|
33
|
+
float height = 0;
|
|
34
|
+
float ascent = 0;
|
|
35
|
+
bool is_space = false;
|
|
36
|
+
bool atomic = false; // inline-block
|
|
37
|
+
bool force_break = false; // explicit newline (white-space: pre)
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
inline float toPx(const Length &length, float base, float em, float root, float vw, float vh,
|
|
41
|
+
float fallback) {
|
|
42
|
+
switch (length.unit) {
|
|
43
|
+
case Unit::Px:
|
|
44
|
+
case Unit::Number:
|
|
45
|
+
return length.value;
|
|
46
|
+
case Unit::Percent:
|
|
47
|
+
return base * length.value / 100.0f;
|
|
48
|
+
case Unit::Em:
|
|
49
|
+
return em * length.value;
|
|
50
|
+
case Unit::Rem:
|
|
51
|
+
return root * length.value;
|
|
52
|
+
case Unit::Vw:
|
|
53
|
+
return vw * length.value / 100.0f;
|
|
54
|
+
case Unit::Vh:
|
|
55
|
+
return vh * length.value / 100.0f;
|
|
56
|
+
default:
|
|
57
|
+
return fallback;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
inline bool isAuto(const Length &length) {
|
|
62
|
+
return length.unit == Unit::Auto || length.unit == Unit::Invalid || length.unit == Unit::None;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Per-character advance as a fraction of the font size is no longer needed
|
|
66
|
+
* here: text metrics come from the shaping stack (see `text.h`). */
|
|
67
|
+
|
|
68
|
+
inline FontSpec specOf(const XtStyle &style) {
|
|
69
|
+
FontSpec spec;
|
|
70
|
+
spec.family = style.font_family;
|
|
71
|
+
spec.pixel_size = style.font_size;
|
|
72
|
+
spec.weight = style.font_weight;
|
|
73
|
+
spec.italic = style.font_style == FontStyle::Italic;
|
|
74
|
+
return spec;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
inline float lineHeightOf(const XtStyle &style) {
|
|
78
|
+
if (style.line_height.unit == Unit::Number) {
|
|
79
|
+
return style.line_height.value > 0 ? style.line_height.value * style.font_size
|
|
80
|
+
: style.font_size * 1.2f;
|
|
81
|
+
}
|
|
82
|
+
if (style.line_height.unit == Unit::Px) return style.line_height.value;
|
|
83
|
+
float lineHeight = 0;
|
|
84
|
+
xt_text_metrics(specOf(style), nullptr, nullptr, &lineHeight);
|
|
85
|
+
return lineHeight > 0 ? lineHeight : style.font_size * 1.2f;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
inline float ascentOf(const XtStyle &style) {
|
|
89
|
+
float ascent = 0;
|
|
90
|
+
xt_text_metrics(specOf(style), &ascent, nullptr, nullptr);
|
|
91
|
+
return ascent > 0 ? ascent : style.font_size * 0.8f;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
struct Layouter {
|
|
95
|
+
const std::unordered_map<const Node *, XtStyle> *styles = nullptr;
|
|
96
|
+
LayoutTree *tree = nullptr;
|
|
97
|
+
float vw = 0;
|
|
98
|
+
float vh = 0;
|
|
99
|
+
float rootFontSize = 16.0f;
|
|
100
|
+
|
|
101
|
+
const XtStyle *styleOf(const Node *node) const;
|
|
102
|
+
|
|
103
|
+
float px(const Length &length, float base, float em, float fallback = 0.0f) const;
|
|
104
|
+
|
|
105
|
+
float clampSize(float value, const Length &minLength, const Length &maxLength, float base,
|
|
106
|
+
float em) const;
|
|
107
|
+
|
|
108
|
+
float textWidth(const std::string &text, const XtStyle &style) const;
|
|
109
|
+
|
|
110
|
+
float horizontalEdges(const LayoutBox *box) const;
|
|
111
|
+
|
|
112
|
+
float verticalEdges(const LayoutBox *box) const;
|
|
113
|
+
|
|
114
|
+
float preferredContentWidth(const LayoutBox *box, float available) const;
|
|
115
|
+
|
|
116
|
+
void translate(LayoutBox *box, float dx, float dy) const;
|
|
117
|
+
|
|
118
|
+
void resolveEdges(LayoutBox *box, float containingWidth) const;
|
|
119
|
+
|
|
120
|
+
/** Lay out `box` at (x, y) and return its outer height (margins included). */
|
|
121
|
+
float layoutBlock(LayoutBox *box, float x, float y, float availableWidth, float availableHeight,
|
|
122
|
+
float forcedContentWidth = -1, float forcedContentHeight = -1) const;
|
|
123
|
+
|
|
124
|
+
/** Lay out the children of `block`; returns the used content height. */
|
|
125
|
+
float layoutChildren(LayoutBox *block, float contentWidth, float availableHeight,
|
|
126
|
+
float definiteHeight) const;
|
|
127
|
+
|
|
128
|
+
void flattenInline(LayoutBox *box, float contentWidth, std::vector<InlineItem> &out) const;
|
|
129
|
+
|
|
130
|
+
void computeInlineGeometry(LayoutBox *box) const;
|
|
131
|
+
|
|
132
|
+
/** Lay out an inline formatting context; returns its consumed height. */
|
|
133
|
+
float layoutInlineRun(LayoutBox *container, const std::vector<LayoutBox *> &run,
|
|
134
|
+
float contentWidth, float y) const;
|
|
135
|
+
|
|
136
|
+
float layoutFlex(LayoutBox *container, float contentWidth, float definiteHeight) const;
|
|
137
|
+
|
|
138
|
+
static float sumOf(const std::vector<float> &values);
|
|
139
|
+
|
|
140
|
+
static void applyJustify(JustifyContent justify, float freeSpace, size_t count, float *offset,
|
|
141
|
+
float *between);
|
|
142
|
+
|
|
143
|
+
static float crossOffsetFor(AlignItems align, float crossSize, float itemCross);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
} // namespace layout_flow_detail
|
|
147
|
+
} // namespace xtgui
|
|
148
|
+
|
|
149
|
+
#endif /* XT_GUI_LAYOUT_FLOW_INTERNAL_H */
|