xbintsc 0.3.25 → 0.3.46
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 +39 -3
- 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/module.js +10 -0
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +24 -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.js +14 -0
- package/dist/src/driver/bundler/graph.js.map +1 -1
- package/dist/src/driver/bundler/merge.js +14 -0
- 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 +73 -5
- 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/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/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 +109 -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/gc.test.d.ts +1 -0
- package/dist/tests/e2e/gc.test.js +168 -0
- package/dist/tests/e2e/gc.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/harness.d.ts +2 -0
- package/dist/tests/e2e/harness.js +1 -0
- package/dist/tests/e2e/harness.js.map +1 -1
- 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/parser/statements.test.js +11 -0
- package/dist/tests/parser/statements.test.js.map +1 -1
- package/package.json +3 -2
- 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/dom.cpp +7 -403
- package/runtime/ext_gui/dom_api.cpp +63 -1146
- 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 +135 -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 +56 -286
- 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/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 +1 -0
- package/runtime/ext_node/buffer/parts/prototype.inc +1 -0
- package/runtime/ext_node/dgram/dgram.c +1 -0
- package/runtime/ext_node/events/events.c +1 -0
- package/runtime/ext_node/fs/fs_ops.c +10 -25
- package/runtime/ext_node/fs/glob.c +13 -30
- package/runtime/ext_node/fs/promises.c +1 -0
- package/runtime/ext_node/http/parts/prototypes.inc +5 -0
- package/runtime/ext_node/net/parts/prototypes.inc +2 -0
- package/runtime/ext_node/process/process.c +9 -7
- package/runtime/ext_node/stream/stream.c +1 -0
- package/runtime/ext_node/util/util.c +6 -6
- package/runtime/rt.h +16 -0
- package/runtime/rt_internal.h +67 -2
- package/runtime/xt_alloc.c +442 -5
- 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_generator.c +95 -1
- package/runtime/xt_loop.c +35 -1
- package/runtime/xt_promise.c +46 -0
- package/runtime/xt_stdlib2/error.inc +1 -0
- package/runtime/xt_stdlib2/regexp-match.inc +8 -7
- package/runtime/xt_stdlib2/set.inc +1 -1
- package/runtime/xt_symbol.c +2 -0
- package/runtime/xt_typed_array/construction.inc +142 -0
- package/runtime/xt_typed_array/elements.inc +92 -0
- package/runtime/xt_typed_array/methods.inc +329 -0
- package/runtime/xt_typed_array.c +72 -0
- package/runtime/xt_values/number-format.inc +26 -0
- package/scripts/check-file-length.ts +85 -0
- package/src/cli/main.ts +137 -29
- package/src/codegen/generator/calls/access.ts +9 -1
- package/src/codegen/generator/module.ts +10 -0
- package/src/codegen/generator/state.ts +24 -0
- package/src/codegen/generator/tables.ts +40 -0
- package/src/driver/bundler/commonjs.ts +553 -0
- package/src/driver/bundler/graph.ts +14 -0
- package/src/driver/bundler/merge.ts +15 -0
- package/src/driver/bundler/types.ts +8 -0
- package/src/driver/compiler.ts +83 -5
- 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/parser/statements.ts +17 -4
- package/dist/tests/e2e/gui.test.d.ts +0 -12
- package/dist/tests/e2e/gui.test.js +0 -702
- package/dist/tests/e2e/gui.test.js.map +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* xbintsc GUI engine —
|
|
2
|
+
* xbintsc GUI engine — box tree construction and the public `LayoutTree` API.
|
|
3
3
|
*
|
|
4
|
-
* The algorithm
|
|
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 "
|
|
15
|
+
#include "layout_internal.h"
|
|
15
16
|
|
|
16
17
|
#include <algorithm>
|
|
17
|
-
#include <
|
|
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 *> ¤t = 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
|
-
|
|
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());
|