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.
Files changed (194) hide show
  1. package/README.md +33 -0
  2. package/dist/src/cli/main.js +124 -30
  3. package/dist/src/cli/main.js.map +1 -1
  4. package/dist/src/codegen/generator/calls/access.js +8 -1
  5. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  6. package/dist/src/codegen/generator/state.js +20 -0
  7. package/dist/src/codegen/generator/state.js.map +1 -1
  8. package/dist/src/codegen/generator/tables.d.ts +6 -0
  9. package/dist/src/codegen/generator/tables.js +38 -0
  10. package/dist/src/codegen/generator/tables.js.map +1 -1
  11. package/dist/src/driver/bundler/commonjs.d.ts +52 -0
  12. package/dist/src/driver/bundler/commonjs.js +493 -0
  13. package/dist/src/driver/bundler/commonjs.js.map +1 -0
  14. package/dist/src/driver/bundler/graph.d.ts +2 -1
  15. package/dist/src/driver/bundler/graph.js +32 -3
  16. package/dist/src/driver/bundler/graph.js.map +1 -1
  17. package/dist/src/driver/bundler/merge.d.ts +2 -1
  18. package/dist/src/driver/bundler/merge.js +16 -2
  19. package/dist/src/driver/bundler/merge.js.map +1 -1
  20. package/dist/src/driver/bundler/types.d.ts +8 -0
  21. package/dist/src/driver/compiler.d.ts +8 -1
  22. package/dist/src/driver/compiler.js +75 -7
  23. package/dist/src/driver/compiler.js.map +1 -1
  24. package/dist/src/driver/config.d.ts +53 -0
  25. package/dist/src/driver/config.js +141 -0
  26. package/dist/src/driver/config.js.map +1 -0
  27. package/dist/src/driver/icon.d.ts +62 -0
  28. package/dist/src/driver/icon.js +151 -0
  29. package/dist/src/driver/icon.js.map +1 -0
  30. package/dist/src/driver/index.d.ts +4 -0
  31. package/dist/src/driver/index.js +4 -0
  32. package/dist/src/driver/index.js.map +1 -1
  33. package/dist/src/driver/mac-bundle.d.ts +41 -0
  34. package/dist/src/driver/mac-bundle.js +123 -0
  35. package/dist/src/driver/mac-bundle.js.map +1 -0
  36. package/dist/src/driver/win-icon.d.ts +27 -0
  37. package/dist/src/driver/win-icon.js +80 -0
  38. package/dist/src/driver/win-icon.js.map +1 -0
  39. package/dist/src/extensions/gui/html.d.ts +25 -0
  40. package/dist/src/extensions/gui/html.js +203 -0
  41. package/dist/src/extensions/gui/html.js.map +1 -0
  42. package/dist/src/extensions/gui/index.js +11 -0
  43. package/dist/src/extensions/gui/index.js.map +1 -1
  44. package/dist/src/extensions/registry.d.ts +29 -0
  45. package/dist/src/extensions/registry.js +14 -0
  46. package/dist/src/extensions/registry.js.map +1 -1
  47. package/dist/src/index.d.ts +1 -1
  48. package/dist/src/index.js.map +1 -1
  49. package/dist/src/lexer/scanner/identifiers.js +4 -12
  50. package/dist/src/lexer/scanner/identifiers.js.map +1 -1
  51. package/dist/src/parser/statements.js +17 -4
  52. package/dist/src/parser/statements.js.map +1 -1
  53. package/dist/tests/cli/main.test.js +46 -1
  54. package/dist/tests/cli/main.test.js.map +1 -1
  55. package/dist/tests/driver/bundler-resolve.test.d.ts +1 -0
  56. package/dist/tests/driver/bundler-resolve.test.js +230 -0
  57. package/dist/tests/driver/bundler-resolve.test.js.map +1 -0
  58. package/dist/tests/driver/config.test.d.ts +1 -0
  59. package/dist/tests/driver/config.test.js +124 -0
  60. package/dist/tests/driver/config.test.js.map +1 -0
  61. package/dist/tests/driver/icon.test.d.ts +1 -0
  62. package/dist/tests/driver/icon.test.js +421 -0
  63. package/dist/tests/driver/icon.test.js.map +1 -0
  64. package/dist/tests/driver/modules.test.js +177 -0
  65. package/dist/tests/driver/modules.test.js.map +1 -1
  66. package/dist/tests/e2e/fetch.test.d.ts +12 -0
  67. package/dist/tests/e2e/fetch.test.js +272 -0
  68. package/dist/tests/e2e/fetch.test.js.map +1 -0
  69. package/dist/tests/e2e/gui-dom.test.d.ts +5 -0
  70. package/dist/tests/e2e/gui-dom.test.js +102 -0
  71. package/dist/tests/e2e/gui-dom.test.js.map +1 -0
  72. package/dist/tests/e2e/gui-events.test.d.ts +5 -0
  73. package/dist/tests/e2e/gui-events.test.js +60 -0
  74. package/dist/tests/e2e/gui-events.test.js.map +1 -0
  75. package/dist/tests/e2e/gui-helpers.d.ts +31 -0
  76. package/dist/tests/e2e/gui-helpers.js +74 -0
  77. package/dist/tests/e2e/gui-helpers.js.map +1 -0
  78. package/dist/tests/e2e/gui-layout.test.d.ts +4 -0
  79. package/dist/tests/e2e/gui-layout.test.js +88 -0
  80. package/dist/tests/e2e/gui-layout.test.js.map +1 -0
  81. package/dist/tests/e2e/gui-lifecycle.test.d.ts +5 -0
  82. package/dist/tests/e2e/gui-lifecycle.test.js +58 -0
  83. package/dist/tests/e2e/gui-lifecycle.test.js.map +1 -0
  84. package/dist/tests/e2e/gui-paint.test.d.ts +5 -0
  85. package/dist/tests/e2e/gui-paint.test.js +111 -0
  86. package/dist/tests/e2e/gui-paint.test.js.map +1 -0
  87. package/dist/tests/e2e/gui-script.test.d.ts +5 -0
  88. package/dist/tests/e2e/gui-script.test.js +78 -0
  89. package/dist/tests/e2e/gui-script.test.js.map +1 -0
  90. package/dist/tests/e2e/gui-style.test.d.ts +5 -0
  91. package/dist/tests/e2e/gui-style.test.js +175 -0
  92. package/dist/tests/e2e/gui-style.test.js.map +1 -0
  93. package/dist/tests/e2e/icon.test.d.ts +7 -0
  94. package/dist/tests/e2e/icon.test.js +80 -0
  95. package/dist/tests/e2e/icon.test.js.map +1 -0
  96. package/dist/tests/e2e/npm-modules.test.d.ts +4 -1
  97. package/dist/tests/e2e/npm-modules.test.js +47 -1
  98. package/dist/tests/e2e/npm-modules.test.js.map +1 -1
  99. package/dist/tests/e2e/runtime-coverage-typed-array.test.d.ts +9 -0
  100. package/dist/tests/e2e/runtime-coverage-typed-array.test.js +140 -0
  101. package/dist/tests/e2e/runtime-coverage-typed-array.test.js.map +1 -0
  102. package/dist/tests/extensions/gui-index.test.d.ts +1 -0
  103. package/dist/tests/extensions/gui-index.test.js +49 -0
  104. package/dist/tests/extensions/gui-index.test.js.map +1 -0
  105. package/dist/tests/extensions/gui.test.d.ts +1 -0
  106. package/dist/tests/extensions/gui.test.js +177 -0
  107. package/dist/tests/extensions/gui.test.js.map +1 -0
  108. package/dist/tests/extensions/registry.test.js +36 -0
  109. package/dist/tests/extensions/registry.test.js.map +1 -1
  110. package/dist/tests/parser/statements.test.js +11 -0
  111. package/dist/tests/parser/statements.test.js.map +1 -1
  112. package/dist/tests/public-api.test.d.ts +1 -0
  113. package/dist/tests/public-api.test.js +31 -0
  114. package/dist/tests/public-api.test.js.map +1 -0
  115. package/package.json +1 -1
  116. package/runtime/ext_gui/css.cpp +4 -517
  117. package/runtime/ext_gui/css_internal.h +27 -0
  118. package/runtime/ext_gui/css_match.cpp +195 -0
  119. package/runtime/ext_gui/css_selector.cpp +207 -0
  120. package/runtime/ext_gui/css_sheet.cpp +129 -0
  121. package/runtime/ext_gui/document.cpp +90 -0
  122. package/runtime/ext_gui/document.h +23 -0
  123. package/runtime/ext_gui/dom.cpp +43 -403
  124. package/runtime/ext_gui/dom.h +7 -0
  125. package/runtime/ext_gui/dom_api.cpp +164 -0
  126. package/runtime/ext_gui/dom_api.h +12 -0
  127. package/runtime/ext_gui/dom_api_document.cpp +96 -0
  128. package/runtime/ext_gui/dom_api_events.cpp +123 -0
  129. package/runtime/ext_gui/dom_api_internal.h +135 -0
  130. package/runtime/ext_gui/dom_api_node.cpp +329 -0
  131. package/runtime/ext_gui/dom_api_node_read.cpp +294 -0
  132. package/runtime/ext_gui/dom_api_proto.cpp +130 -0
  133. package/runtime/ext_gui/dom_api_style.cpp +192 -0
  134. package/runtime/ext_gui/dom_internal.h +39 -0
  135. package/runtime/ext_gui/dom_parse.cpp +348 -0
  136. package/runtime/ext_gui/dom_serialize.cpp +54 -0
  137. package/runtime/ext_gui/gui.cpp +61 -245
  138. package/runtime/ext_gui/gui_engine.h +54 -0
  139. package/runtime/ext_gui/gui_events.cpp +294 -0
  140. package/runtime/ext_gui/gui_internal.h +29 -0
  141. package/runtime/ext_gui/layout.cpp +6 -597
  142. package/runtime/ext_gui/layout_flex.cpp +200 -0
  143. package/runtime/ext_gui/layout_flow.cpp +191 -0
  144. package/runtime/ext_gui/layout_flow_internal.h +149 -0
  145. package/runtime/ext_gui/layout_inline.cpp +181 -0
  146. package/runtime/ext_gui/layout_internal.h +20 -0
  147. package/runtime/ext_gui/renderer.cpp +15 -679
  148. package/runtime/ext_gui/renderer_image.cpp +233 -0
  149. package/runtime/ext_gui/renderer_internal.h +112 -0
  150. package/runtime/ext_gui/renderer_shaders.h +108 -0
  151. package/runtime/ext_gui/renderer_text.cpp +342 -0
  152. package/runtime/ext_gui/script.cpp +63 -0
  153. package/runtime/ext_gui/style.cpp +12 -660
  154. package/runtime/ext_gui/style_internal.h +33 -0
  155. package/runtime/ext_gui/style_print.cpp +113 -0
  156. package/runtime/ext_gui/style_ua.cpp +98 -0
  157. package/runtime/ext_gui/style_values.cpp +468 -0
  158. package/runtime/ext_gui/window.cpp +60 -0
  159. package/runtime/rt.h +6 -0
  160. package/runtime/rt_internal.h +4 -0
  161. package/runtime/xt_builtins.c +1 -0
  162. package/runtime/xt_containers/access.inc +32 -1
  163. package/runtime/xt_fetch/buffer.inc +64 -0
  164. package/runtime/xt_fetch/headers.inc +312 -0
  165. package/runtime/xt_fetch/request.inc +386 -0
  166. package/runtime/xt_fetch/response.inc +107 -0
  167. package/runtime/xt_fetch/socket.inc +72 -0
  168. package/runtime/xt_fetch/url.inc +108 -0
  169. package/runtime/xt_fetch.c +39 -0
  170. package/runtime/xt_stdlib2/set.inc +1 -1
  171. package/runtime/xt_typed_array.c +614 -0
  172. package/src/cli/main.ts +137 -29
  173. package/src/codegen/generator/calls/access.ts +9 -1
  174. package/src/codegen/generator/state.ts +20 -0
  175. package/src/codegen/generator/tables.ts +40 -0
  176. package/src/driver/bundler/commonjs.ts +553 -0
  177. package/src/driver/bundler/graph.ts +35 -2
  178. package/src/driver/bundler/merge.ts +18 -1
  179. package/src/driver/bundler/types.ts +8 -0
  180. package/src/driver/compiler.ts +85 -7
  181. package/src/driver/config.ts +173 -0
  182. package/src/driver/icon.ts +184 -0
  183. package/src/driver/index.ts +30 -0
  184. package/src/driver/mac-bundle.ts +156 -0
  185. package/src/driver/win-icon.ts +96 -0
  186. package/src/extensions/gui/html.ts +236 -0
  187. package/src/extensions/gui/index.ts +11 -0
  188. package/src/extensions/registry.ts +39 -0
  189. package/src/index.ts +1 -1
  190. package/src/lexer/scanner/identifiers.ts +4 -11
  191. package/src/parser/statements.ts +17 -4
  192. package/dist/tests/e2e/gui.test.d.ts +0 -12
  193. package/dist/tests/e2e/gui.test.js +0 -493
  194. package/dist/tests/e2e/gui.test.js.map +0 -1
@@ -1,7 +1,8 @@
1
1
  /*
2
- * xbintsc GUI engine — layout implementation.
2
+ * xbintsc GUI engine — box tree construction and the public `LayoutTree` API.
3
3
  *
4
- * The algorithm is intentionally a pragmatic subset:
4
+ * The flow algorithm itself lives in `layout_flow.cpp`. The algorithm is
5
+ * intentionally a pragmatic subset:
5
6
  * - Block flow: consecutive inline-level children form an anonymous inline
6
7
  * formatting context; block-level children are stacked vertically.
7
8
  * - Inline flow: greedy, word-based line breaking with `text-align`; text is
@@ -11,599 +12,17 @@
11
12
  * Margins do not collapse yet, and only static positioning is honoured.
12
13
  */
13
14
 
14
- #include "layout.h"
15
+ #include "layout_internal.h"
15
16
 
16
17
  #include <algorithm>
17
- #include <cfloat>
18
+ #include <cstdio>
18
19
  #include <cstring>
19
20
 
20
- #include "text.h"
21
21
  #include "image.h"
22
22
 
23
23
  namespace xtgui {
24
24
  namespace {
25
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
- float toPx(const Length &length, float base, float em, float root, float vw, float vh, float fallback) {
41
- switch (length.unit) {
42
- case Unit::Px:
43
- case Unit::Number:
44
- return length.value;
45
- case Unit::Percent:
46
- return base * length.value / 100.0f;
47
- case Unit::Em:
48
- return em * length.value;
49
- case Unit::Rem:
50
- return root * length.value;
51
- case Unit::Vw:
52
- return vw * length.value / 100.0f;
53
- case Unit::Vh:
54
- return vh * length.value / 100.0f;
55
- default:
56
- return fallback;
57
- }
58
- }
59
-
60
- bool isAuto(const Length &length) {
61
- return length.unit == Unit::Auto || length.unit == Unit::Invalid || length.unit == Unit::None;
62
- }
63
-
64
- /* Per-character advance as a fraction of the font size is no longer needed
65
- * here: text metrics come from the shaping stack (see `text.h`). */
66
-
67
- FontSpec specOf(const XtStyle &style) {
68
- FontSpec spec;
69
- spec.family = style.font_family;
70
- spec.pixel_size = style.font_size;
71
- spec.weight = style.font_weight;
72
- spec.italic = style.font_style == FontStyle::Italic;
73
- return spec;
74
- }
75
-
76
- float lineHeightOf(const XtStyle &style) {
77
- if (style.line_height.unit == Unit::Number) {
78
- return style.line_height.value > 0 ? style.line_height.value * style.font_size : style.font_size * 1.2f;
79
- }
80
- if (style.line_height.unit == Unit::Px) return style.line_height.value;
81
- float lineHeight = 0;
82
- xt_text_metrics(specOf(style), nullptr, nullptr, &lineHeight);
83
- return lineHeight > 0 ? lineHeight : style.font_size * 1.2f;
84
- }
85
-
86
- float ascentOf(const XtStyle &style) {
87
- float ascent = 0;
88
- xt_text_metrics(specOf(style), &ascent, nullptr, nullptr);
89
- return ascent > 0 ? ascent : style.font_size * 0.8f;
90
- }
91
-
92
- struct Layouter {
93
- const std::unordered_map<const Node *, XtStyle> *styles = nullptr;
94
- LayoutTree *tree = nullptr;
95
- float vw = 0;
96
- float vh = 0;
97
- float rootFontSize = 16.0f;
98
-
99
- const XtStyle *styleOf(const Node *node) const {
100
- auto it = styles->find(node);
101
- return it == styles->end() ? nullptr : &it->second;
102
- }
103
-
104
- float px(const Length &length, float base, float em, float fallback = 0.0f) const {
105
- return toPx(length, base, em, rootFontSize, vw, vh, fallback);
106
- }
107
-
108
- float clampSize(float value, const Length &minLength, const Length &maxLength, float base,
109
- float em) const {
110
- if (!isAuto(minLength)) value = std::max(value, px(minLength, base, em, value));
111
- if (!isAuto(maxLength)) value = std::min(value, px(maxLength, base, em, value));
112
- return value;
113
- }
114
-
115
- float textWidth(const std::string &text, const XtStyle &style) const {
116
- return xt_text_measure_width(text, specOf(style));
117
- }
118
-
119
- float horizontalEdges(const LayoutBox *box) const {
120
- return box->margin_left + box->margin_right + box->border_left + box->border_right +
121
- box->padding_left + box->padding_right;
122
- }
123
-
124
- float verticalEdges(const LayoutBox *box) const {
125
- return box->margin_top + box->margin_bottom + box->border_top + box->border_bottom +
126
- box->padding_top + box->padding_bottom;
127
- }
128
-
129
- float preferredContentWidth(const LayoutBox *box, float available) const {
130
- const XtStyle &style = *box->style;
131
- if (!isAuto(style.width)) return px(style.width, available, style.font_size, 0.0f);
132
- if (box->is_image && box->intrinsic_width > 0) return box->intrinsic_width;
133
- float best = 0;
134
- for (const std::unique_ptr<LayoutBox> &child : box->children) {
135
- if (child->display == Display::None) continue;
136
- if (child->is_text) {
137
- best = std::max(best, textWidth(child->text, *child->style));
138
- } else {
139
- best = std::max(best, preferredContentWidth(child.get(), available) + horizontalEdges(child.get()));
140
- }
141
- }
142
- return best;
143
- }
144
-
145
- void translate(LayoutBox *box, float dx, float dy) const {
146
- box->x += dx;
147
- box->y += dy;
148
- box->content_x += dx;
149
- box->content_y += dy;
150
- for (TextFragment &fragment : box->fragments) {
151
- fragment.x += dx;
152
- fragment.y += dy;
153
- }
154
- for (std::unique_ptr<LayoutBox> &child : box->children) translate(child.get(), dx, dy);
155
- }
156
-
157
- void resolveEdges(LayoutBox *box, float containingWidth) const {
158
- const XtStyle &style = *box->style;
159
- float em = style.font_size;
160
- box->margin_top = px(style.margin.top, containingWidth, em);
161
- box->margin_right = px(style.margin.right, containingWidth, em);
162
- box->margin_bottom = px(style.margin.bottom, containingWidth, em);
163
- box->margin_left = px(style.margin.left, containingWidth, em);
164
- box->padding_top = px(style.padding.top, containingWidth, em);
165
- box->padding_right = px(style.padding.right, containingWidth, em);
166
- box->padding_bottom = px(style.padding.bottom, containingWidth, em);
167
- box->padding_left = px(style.padding.left, containingWidth, em);
168
- if (style.border_style == BorderStyle::None) {
169
- box->border_top = box->border_right = box->border_bottom = box->border_left = 0;
170
- } else {
171
- box->border_top = px(style.border_width.top, containingWidth, em);
172
- box->border_right = px(style.border_width.right, containingWidth, em);
173
- box->border_bottom = px(style.border_width.bottom, containingWidth, em);
174
- box->border_left = px(style.border_width.left, containingWidth, em);
175
- }
176
- }
177
-
178
- /** Lay out `box` at (x, y) and return its outer height (margins included). */
179
- float layoutBlock(LayoutBox *box, float x, float y, float availableWidth, float availableHeight,
180
- float forcedContentWidth = -1, float forcedContentHeight = -1) const {
181
- const XtStyle &style = *box->style;
182
- float em = style.font_size;
183
- resolveEdges(box, availableWidth);
184
-
185
- float contentWidth;
186
- if (forcedContentWidth >= 0) {
187
- contentWidth = forcedContentWidth;
188
- } else if (isAuto(style.width)) {
189
- contentWidth = availableWidth - horizontalEdges(box);
190
- } else {
191
- contentWidth = px(style.width, availableWidth, em);
192
- }
193
- contentWidth = clampSize(contentWidth, style.min_width, style.max_width, availableWidth, em);
194
- if (contentWidth < 0) contentWidth = 0;
195
-
196
- box->x = x + box->margin_left;
197
- box->y = y + box->margin_top;
198
- box->content_x = box->x + box->border_left + box->padding_left;
199
- box->content_y = box->y + box->border_top + box->padding_top;
200
- box->content_width = contentWidth;
201
- box->width = box->border_left + box->padding_left + contentWidth + box->padding_right + box->border_right;
202
-
203
- float definiteHeight = -1;
204
- if (!isAuto(style.height) && !(style.height.unit == Unit::Percent && availableHeight < 0)) {
205
- definiteHeight = px(style.height, availableHeight, em, -1);
206
- }
207
-
208
- float contentHeight;
209
- if (forcedContentHeight >= 0) {
210
- layoutChildren(box, contentWidth, availableHeight, forcedContentHeight);
211
- contentHeight = forcedContentHeight;
212
- } else {
213
- contentHeight = layoutChildren(box, contentWidth, availableHeight, definiteHeight);
214
- if (definiteHeight >= 0) contentHeight = definiteHeight;
215
- contentHeight = clampSize(contentHeight, style.min_height, style.max_height,
216
- availableHeight < 0 ? contentHeight : availableHeight, em);
217
- if (contentHeight < 0) contentHeight = 0;
218
- }
219
- /* Replaced elements keep their aspect ratio when only one axis is set. */
220
- if (box->is_image && forcedContentHeight < 0 && isAuto(style.height) &&
221
- box->intrinsic_width > 0 && box->intrinsic_height > 0) {
222
- contentHeight = contentWidth * box->intrinsic_height / box->intrinsic_width;
223
- }
224
- box->content_height = contentHeight;
225
- box->height = box->border_top + box->padding_top + contentHeight + box->padding_bottom + box->border_bottom;
226
- box->baseline = box->height;
227
- return box->margin_top + box->height + box->margin_bottom;
228
- }
229
-
230
- /** Lay out the children of `block`; returns the used content height. */
231
- float layoutChildren(LayoutBox *block, float contentWidth, float availableHeight,
232
- float definiteHeight) const {
233
- if (block->style->display == Display::Flex) return layoutFlex(block, contentWidth, definiteHeight);
234
-
235
- float y = block->content_y;
236
- float startY = y;
237
- std::vector<LayoutBox *> inlineRun;
238
- auto flush = [&]() {
239
- if (inlineRun.empty()) return;
240
- y += layoutInlineRun(block, inlineRun, contentWidth, y);
241
- inlineRun.clear();
242
- };
243
- for (const std::unique_ptr<LayoutBox> &child : block->children) {
244
- Display display = child->display;
245
- if (display == Display::None) continue;
246
- if (display == Display::Block || display == Display::ListItem || display == Display::Flex) {
247
- flush();
248
- y += layoutBlock(child.get(), block->content_x, y, contentWidth, availableHeight);
249
- } else {
250
- inlineRun.push_back(child.get());
251
- }
252
- }
253
- flush();
254
- return y - startY;
255
- }
256
-
257
- void flattenInline(LayoutBox *box, float contentWidth, std::vector<InlineItem> &out) const {
258
- const XtStyle &style = *box->style;
259
- if (box->is_text) {
260
- float height = lineHeightOf(style);
261
- float ascent = ascentOf(style);
262
- const std::string &text = box->text;
263
- size_t i = 0;
264
- while (i < text.size()) {
265
- if (text[i] == '\n') {
266
- InlineItem br;
267
- br.box = box;
268
- br.style = &style;
269
- br.force_break = true;
270
- out.push_back(std::move(br));
271
- i++;
272
- continue;
273
- }
274
- bool space = text[i] == ' ' || text[i] == '\t';
275
- size_t j = i;
276
- while (j < text.size() && text[j] != '\n' && (text[j] == ' ' || text[j] == '\t') == space) j++;
277
- InlineItem item;
278
- item.box = box;
279
- item.style = &style;
280
- item.text = text.substr(i, j - i);
281
- item.is_space = space;
282
- item.width = textWidth(item.text, style);
283
- item.height = height;
284
- item.ascent = ascent;
285
- out.push_back(std::move(item));
286
- i = j;
287
- }
288
- return;
289
- }
290
- if (box->display == Display::InlineBlock) {
291
- float available = contentWidth;
292
- float intrinsic = preferredContentWidth(box, available);
293
- layoutBlock(box, 0, 0, available, -1, intrinsic);
294
- InlineItem item;
295
- item.box = box;
296
- item.style = &style;
297
- item.atomic = true;
298
- item.width = box->margin_left + box->width + box->margin_right;
299
- item.height = box->margin_top + box->height + box->margin_bottom;
300
- item.ascent = item.height;
301
- out.push_back(std::move(item));
302
- return;
303
- }
304
- /* Inline element: flatten its subtree. */
305
- for (const std::unique_ptr<LayoutBox> &child : box->children) {
306
- if (child->display == Display::None) continue;
307
- flattenInline(child.get(), contentWidth, out);
308
- }
309
- }
310
-
311
- void computeInlineGeometry(LayoutBox *box) const {
312
- if (box->is_text) {
313
- if (box->fragments.empty()) return;
314
- float minx = kHuge, miny = kHuge, maxx = -kHuge, maxy = -kHuge;
315
- for (const TextFragment &fragment : box->fragments) {
316
- minx = std::min(minx, fragment.x);
317
- miny = std::min(miny, fragment.y);
318
- maxx = std::max(maxx, fragment.x + fragment.width);
319
- maxy = std::max(maxy, fragment.y + fragment.height);
320
- }
321
- box->x = minx;
322
- box->y = miny;
323
- box->width = maxx - minx;
324
- box->height = maxy - miny;
325
- box->content_x = minx;
326
- box->content_y = miny;
327
- box->content_width = box->width;
328
- box->content_height = box->height;
329
- return;
330
- }
331
- float minx = kHuge, miny = kHuge, maxx = -kHuge, maxy = -kHuge;
332
- bool any = false;
333
- for (const std::unique_ptr<LayoutBox> &child : box->children) {
334
- if (child->display == Display::None) continue;
335
- computeInlineGeometry(child.get());
336
- if (child->is_text && child->fragments.empty()) continue;
337
- if (child->width <= 0 && child->height <= 0 && !child->is_text && child->children.empty()) continue;
338
- any = true;
339
- minx = std::min(minx, child->x);
340
- miny = std::min(miny, child->y);
341
- maxx = std::max(maxx, child->x + child->width);
342
- maxy = std::max(maxy, child->y + child->height);
343
- }
344
- if (!any) return;
345
- box->x = minx;
346
- box->y = miny;
347
- box->width = maxx - minx;
348
- box->height = maxy - miny;
349
- box->content_x = minx;
350
- box->content_y = miny;
351
- box->content_width = box->width;
352
- box->content_height = box->height;
353
- }
354
-
355
- /** Lay out an inline formatting context; returns its consumed height. */
356
- float layoutInlineRun(LayoutBox *container, const std::vector<LayoutBox *> &run, float contentWidth,
357
- float y) const {
358
- std::vector<InlineItem> items;
359
- for (LayoutBox *box : run) flattenInline(box, contentWidth, items);
360
- if (items.empty()) return 0;
361
-
362
- std::vector<std::vector<InlineItem *>> lines;
363
- std::vector<InlineItem *> line;
364
- float lineWidth = 0;
365
- auto pushLine = [&]() {
366
- while (!line.empty() && line.back()->is_space) {
367
- lineWidth -= line.back()->width;
368
- line.pop_back();
369
- }
370
- if (!line.empty()) lines.push_back(line);
371
- line.clear();
372
- lineWidth = 0;
373
- };
374
- for (InlineItem &item : items) {
375
- if (item.force_break) {
376
- pushLine();
377
- continue;
378
- }
379
- if (item.is_space && line.empty()) continue; // no leading space on a line
380
- if (lineWidth + item.width > contentWidth && !line.empty()) pushLine();
381
- if (item.is_space && line.empty()) continue;
382
- line.push_back(&item);
383
- lineWidth += item.width;
384
- }
385
- pushLine();
386
-
387
- const XtStyle &containerStyle = *container->style;
388
- float cursorY = y;
389
- for (size_t index = 0; index < lines.size(); index++) {
390
- std::vector<InlineItem *> &current = lines[index];
391
- float width = 0;
392
- float ascent = 0;
393
- float descent = 0;
394
- for (InlineItem *item : current) {
395
- width += item->width;
396
- ascent = std::max(ascent, item->ascent);
397
- descent = std::max(descent, item->height - item->ascent);
398
- }
399
- float height = ascent + descent;
400
- float x = container->content_x;
401
- if (containerStyle.text_align == TextAlign::Center) {
402
- x += (contentWidth - width) / 2.0f;
403
- } else if (containerStyle.text_align == TextAlign::End) {
404
- x += contentWidth - width;
405
- }
406
- for (InlineItem *item : current) {
407
- if (item->atomic) {
408
- float dx = x + item->box->margin_left - item->box->x;
409
- float dy = cursorY + item->box->margin_top - item->box->y;
410
- translate(item->box, dx, dy);
411
- } else {
412
- item->box->fragments.push_back({x, cursorY, item->width, height, item->text});
413
- }
414
- x += item->width;
415
- }
416
- cursorY += height;
417
- }
418
- for (LayoutBox *box : run) computeInlineGeometry(box);
419
- return cursorY - y;
420
- }
421
-
422
- float layoutFlex(LayoutBox *container, float contentWidth, float definiteHeight) const {
423
- const XtStyle &containerStyle = *container->style;
424
- bool isRow = containerStyle.flex_direction == FlexDirection::Row ||
425
- containerStyle.flex_direction == FlexDirection::RowReverse;
426
- bool reverse = containerStyle.flex_direction == FlexDirection::RowReverse ||
427
- containerStyle.flex_direction == FlexDirection::ColumnReverse;
428
- float gap = px(containerStyle.gap, contentWidth, containerStyle.font_size, 0.0f);
429
-
430
- std::vector<LayoutBox *> items;
431
- for (const std::unique_ptr<LayoutBox> &child : container->children) {
432
- if (child->display != Display::None) items.push_back(child.get());
433
- }
434
- if (items.empty()) return 0;
435
- size_t count = items.size();
436
-
437
- if (isRow) {
438
- float mainSize = contentWidth;
439
- std::vector<float> outer(count, 0.0f);
440
- std::vector<float> mainSizes(count, 0.0f);
441
- float sumBase = 0;
442
- for (size_t i = 0; i < count; i++) {
443
- const XtStyle &style = *items[i]->style;
444
- float basis;
445
- if (isAuto(style.flex_basis)) {
446
- if (!isAuto(style.width)) basis = px(style.width, mainSize, style.font_size, 0.0f);
447
- else basis = preferredContentWidth(items[i], mainSize);
448
- } else {
449
- basis = px(style.flex_basis, mainSize, style.font_size, 0.0f);
450
- }
451
- resolveEdges(items[i], mainSize);
452
- float edges = horizontalEdges(items[i]);
453
- outer[i] = basis + edges;
454
- mainSizes[i] = outer[i];
455
- sumBase += outer[i];
456
- }
457
- float free = mainSize - sumBase - gap * (float)(count - 1);
458
- if (free > 0) {
459
- float totalGrow = 0;
460
- for (LayoutBox *item : items) totalGrow += item->style->flex_grow;
461
- if (totalGrow > 0) {
462
- for (size_t i = 0; i < count; i++) mainSizes[i] += free * items[i]->style->flex_grow / totalGrow;
463
- }
464
- } else if (free < 0) {
465
- float totalShrink = 0;
466
- for (size_t i = 0; i < count; i++) totalShrink += items[i]->style->flex_shrink * outer[i];
467
- if (totalShrink > 0) {
468
- for (size_t i = 0; i < count; i++) {
469
- mainSizes[i] += free * (items[i]->style->flex_shrink * outer[i]) / totalShrink;
470
- mainSizes[i] = std::max(0.0f, mainSizes[i]);
471
- }
472
- }
473
- }
474
-
475
- /* Cross sizes: lay out once to measure, then apply cross alignment. */
476
- float crossSize = definiteHeight >= 0 ? definiteHeight : 0;
477
- std::vector<float> cross(count, 0.0f);
478
- for (size_t i = 0; i < count; i++) {
479
- float contentW = mainSizes[i] - horizontalEdges(items[i]);
480
- if (contentW < 0) contentW = 0;
481
- layoutBlock(items[i], container->content_x, container->content_y, contentWidth, definiteHeight,
482
- contentW);
483
- cross[i] = verticalEdges(items[i]) + items[i]->height;
484
- if (definiteHeight < 0) crossSize = std::max(crossSize, cross[i]);
485
- }
486
- if (containerStyle.align_items == AlignItems::Stretch && definiteHeight >= 0) {
487
- for (size_t i = 0; i < count; i++) {
488
- if (isAuto(items[i]->style->height)) {
489
- float contentH = crossSize - verticalEdges(items[i]);
490
- if (contentH < 0) contentH = 0;
491
- float contentW = mainSizes[i] - horizontalEdges(items[i]);
492
- layoutBlock(items[i], container->content_x, container->content_y, contentWidth, definiteHeight,
493
- contentW, contentH);
494
- }
495
- }
496
- }
497
-
498
- /* justify-content: place the main axis. */
499
- float freeSpace = mainSize - (sumOf(mainSizes) + gap * (float)(count - 1));
500
- float offset = 0;
501
- float between = gap;
502
- applyJustify(containerStyle.justify_content, freeSpace, count, &offset, &between);
503
-
504
- float cursor = container->content_x + offset;
505
- for (size_t k = 0; k < count; k++) {
506
- size_t i = reverse ? (count - 1 - k) : k;
507
- float crossOffset = crossOffsetFor(containerStyle.align_items, crossSize, cross[i]);
508
- float dx = cursor - items[i]->x;
509
- float dy = container->content_y + crossOffset - items[i]->y;
510
- translate(items[i], dx, dy);
511
- cursor += mainSizes[i] + between;
512
- }
513
- return crossSize;
514
- }
515
-
516
- /* Column flex. */
517
- float mainSize = definiteHeight >= 0 ? definiteHeight : -1;
518
- std::vector<float> outer(count, 0.0f);
519
- float sumBase = 0;
520
- for (size_t i = 0; i < count; i++) {
521
- layoutBlock(items[i], container->content_x, container->content_y, contentWidth, definiteHeight);
522
- outer[i] = verticalEdges(items[i]) + items[i]->height;
523
- sumBase += outer[i];
524
- }
525
- std::vector<float> mainSizes = outer;
526
- if (mainSize >= 0) {
527
- float free = mainSize - sumBase - gap * (float)(count - 1);
528
- if (free > 0) {
529
- float totalGrow = 0;
530
- for (LayoutBox *item : items) totalGrow += item->style->flex_grow;
531
- if (totalGrow > 0) {
532
- for (size_t i = 0; i < count; i++) {
533
- mainSizes[i] += free * items[i]->style->flex_grow / totalGrow;
534
- float contentH = mainSizes[i] - verticalEdges(items[i]);
535
- if (contentH < 0) contentH = 0;
536
- layoutBlock(items[i], container->content_x, container->content_y, contentWidth, definiteHeight, -1,
537
- contentH);
538
- }
539
- }
540
- }
541
- } else {
542
- mainSize = sumBase + gap * (float)(count - 1);
543
- }
544
- float freeSpace = mainSize - (sumOf(mainSizes) + gap * (float)(count - 1));
545
- float offset = 0;
546
- float between = gap;
547
- applyJustify(containerStyle.justify_content, freeSpace, count, &offset, &between);
548
-
549
- float cursor = container->content_y + offset;
550
- for (size_t k = 0; k < count; k++) {
551
- size_t i = reverse ? (count - 1 - k) : k;
552
- float dx = container->content_x - items[i]->x;
553
- float dy = cursor - items[i]->y;
554
- translate(items[i], dx, dy);
555
- cursor += mainSizes[i] + between;
556
- }
557
- return mainSize;
558
- }
559
-
560
- static float sumOf(const std::vector<float> &values) {
561
- float total = 0;
562
- for (float value : values) total += value;
563
- return total;
564
- }
565
-
566
- static void applyJustify(JustifyContent justify, float freeSpace, size_t count, float *offset,
567
- float *between) {
568
- if (count == 0) return;
569
- if (freeSpace < 0) freeSpace = 0;
570
- switch (justify) {
571
- case JustifyContent::FlexEnd:
572
- *offset = freeSpace;
573
- break;
574
- case JustifyContent::Center:
575
- *offset = freeSpace / 2.0f;
576
- break;
577
- case JustifyContent::SpaceBetween:
578
- if (count > 1) *between += freeSpace / (float)(count - 1);
579
- break;
580
- case JustifyContent::SpaceAround:
581
- *between += freeSpace / (float)count;
582
- *offset = freeSpace / (2.0f * (float)count);
583
- break;
584
- case JustifyContent::SpaceEvenly:
585
- *between += freeSpace / (float)(count + 1);
586
- *offset = freeSpace / (float)(count + 1);
587
- break;
588
- default:
589
- break;
590
- }
591
- }
592
-
593
- static float crossOffsetFor(AlignItems align, float crossSize, float itemCross) {
594
- switch (align) {
595
- case AlignItems::FlexEnd:
596
- return crossSize - itemCross;
597
- case AlignItems::Center:
598
- return (crossSize - itemCross) / 2.0f;
599
- case AlignItems::Baseline:
600
- return crossSize - itemCross;
601
- default:
602
- return 0.0f;
603
- }
604
- }
605
- };
606
-
607
26
  std::unique_ptr<LayoutBox> buildBoxTree(const Node *node,
608
27
  const std::unordered_map<const Node *, XtStyle> &styles,
609
28
  const XtStyle *inherited) {
@@ -666,10 +85,6 @@ std::string dumpBox(const LayoutBox *box, int depth) {
666
85
 
667
86
  } // namespace
668
87
 
669
- float xt_layout_text_width(const std::string &text, const XtStyle &style) {
670
- return xt_text_measure_width(text, specOf(style));
671
- }
672
-
673
88
  void LayoutTree::compute(const Node *root, const std::unordered_map<const Node *, XtStyle> &styles,
674
89
  float viewportWidth, float viewportHeight) {
675
90
  root_.reset();
@@ -677,12 +92,6 @@ void LayoutTree::compute(const Node *root, const std::unordered_map<const Node *
677
92
  root_style_ = xt_style_initial();
678
93
  root_style_.display = Display::Block;
679
94
 
680
- Layouter layouter;
681
- layouter.styles = &styles;
682
- layouter.tree = this;
683
- layouter.vw = viewportWidth;
684
- layouter.vh = viewportHeight;
685
-
686
95
  root_ = std::make_unique<LayoutBox>();
687
96
  root_->node = root;
688
97
  root_->style = &root_style_;
@@ -704,7 +113,7 @@ void LayoutTree::compute(const Node *root, const std::unordered_map<const Node *
704
113
  }
705
114
  }
706
115
  }
707
- layouter.layoutBlock(root_.get(), 0, 0, viewportWidth, viewportHeight);
116
+ xt_layout_run(root_.get(), styles, viewportWidth, viewportHeight);
708
117
 
709
118
  std::vector<const LayoutBox *> stack;
710
119
  stack.push_back(root_.get());