xbintsc 0.3.15 → 0.3.16
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/dist/src/cli/main.js +4 -4
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/state.js +8 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.js +8 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/graph.d.ts +8 -0
- package/dist/src/driver/bundler/graph.js +85 -0
- package/dist/src/driver/bundler/graph.js.map +1 -0
- package/dist/src/driver/bundler/merge.d.ts +11 -0
- package/dist/src/driver/bundler/merge.js +288 -0
- package/dist/src/driver/bundler/merge.js.map +1 -0
- package/dist/src/driver/bundler/resolve.d.ts +12 -0
- package/dist/src/driver/bundler/resolve.js +246 -0
- package/dist/src/driver/bundler/resolve.js.map +1 -0
- package/dist/src/driver/bundler/symbols.d.ts +15 -0
- package/dist/src/driver/bundler/symbols.js +51 -0
- package/dist/src/driver/bundler/symbols.js.map +1 -0
- package/dist/src/driver/bundler/types.d.ts +36 -0
- package/dist/src/driver/bundler/types.js +5 -0
- package/dist/src/driver/bundler/types.js.map +1 -0
- package/dist/src/driver/modules.d.ts +9 -16
- package/dist/src/driver/modules.js +8 -644
- package/dist/src/driver/modules.js.map +1 -1
- package/dist/src/extensions/catalog.js +2 -1
- package/dist/src/extensions/catalog.js.map +1 -1
- package/dist/src/extensions/gui/index.d.ts +24 -0
- package/dist/src/extensions/gui/index.js +101 -0
- package/dist/src/extensions/gui/index.js.map +1 -0
- package/dist/tests/e2e/compile-basics.test.d.ts +4 -0
- package/dist/tests/e2e/compile-basics.test.js +134 -0
- package/dist/tests/e2e/compile-basics.test.js.map +1 -0
- package/dist/tests/e2e/compile-builtins.test.d.ts +4 -0
- package/dist/tests/e2e/compile-builtins.test.js +191 -0
- package/dist/tests/e2e/compile-builtins.test.js.map +1 -0
- package/dist/tests/e2e/compile-control-flow.test.d.ts +4 -0
- package/dist/tests/e2e/compile-control-flow.test.js +190 -0
- package/dist/tests/e2e/compile-control-flow.test.js.map +1 -0
- package/dist/tests/e2e/compile-modules.test.d.ts +4 -0
- package/dist/tests/e2e/compile-modules.test.js +48 -0
- package/dist/tests/e2e/compile-modules.test.js.map +1 -0
- package/dist/tests/e2e/compile-objects-classes.test.d.ts +4 -0
- package/dist/tests/e2e/compile-objects-classes.test.js +362 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -0
- package/dist/tests/e2e/differential-helpers.d.ts +12 -0
- package/dist/tests/e2e/differential-helpers.js +16 -0
- package/dist/tests/e2e/differential-helpers.js.map +1 -0
- package/dist/tests/e2e/differential-language.test.d.ts +4 -0
- package/dist/tests/e2e/{differential.test.js → differential-language.test.js} +5 -287
- package/dist/tests/e2e/differential-language.test.js.map +1 -0
- package/dist/tests/e2e/differential-symbols.test.d.ts +4 -0
- package/dist/tests/e2e/differential-symbols.test.js +92 -0
- package/dist/tests/e2e/differential-symbols.test.js.map +1 -0
- package/dist/tests/e2e/differential-values.test.d.ts +4 -0
- package/dist/tests/e2e/differential-values.test.js +197 -0
- package/dist/tests/e2e/differential-values.test.js.map +1 -0
- package/dist/tests/e2e/gui.test.d.ts +12 -0
- package/dist/tests/e2e/gui.test.js +493 -0
- package/dist/tests/e2e/gui.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-buffer.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-buffer.test.js +97 -0
- package/dist/tests/e2e/runtime-coverage-buffer.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js +149 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-node-modules.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-node-modules.test.js +177 -0
- package/dist/tests/e2e/runtime-coverage-node-modules.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-streams.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-streams.test.js +161 -0
- package/dist/tests/e2e/runtime-coverage-streams.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-system.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-system.test.js +91 -0
- package/dist/tests/e2e/runtime-coverage-system.test.js.map +1 -0
- package/dist/tests/e2e/timers.test.d.ts +7 -0
- package/dist/tests/e2e/timers.test.js +63 -0
- package/dist/tests/e2e/timers.test.js.map +1 -0
- package/package.json +2 -1
- package/runtime/ext_gui/css.cpp +743 -0
- package/runtime/ext_gui/css.h +98 -0
- package/runtime/ext_gui/document.cpp +264 -0
- package/runtime/ext_gui/document.h +89 -0
- package/runtime/ext_gui/dom.cpp +472 -0
- package/runtime/ext_gui/dom.h +71 -0
- package/runtime/ext_gui/gui.cpp +457 -0
- package/runtime/ext_gui/gui_engine.h +81 -0
- package/runtime/ext_gui/image.cpp +125 -0
- package/runtime/ext_gui/image.h +42 -0
- package/runtime/ext_gui/layout.cpp +748 -0
- package/runtime/ext_gui/layout.h +93 -0
- package/runtime/ext_gui/paint.cpp +137 -0
- package/runtime/ext_gui/paint.h +75 -0
- package/runtime/ext_gui/renderer.cpp +959 -0
- package/runtime/ext_gui/renderer.h +68 -0
- package/runtime/ext_gui/stb_image.h +7988 -0
- package/runtime/ext_gui/style.cpp +771 -0
- package/runtime/ext_gui/style.h +117 -0
- package/runtime/ext_gui/text.cpp +378 -0
- package/runtime/ext_gui/text.h +78 -0
- package/runtime/ext_gui/window.cpp +405 -0
- package/runtime/ext_node/fs/fs_common.h +13 -527
- package/runtime/ext_node/fs/parts/buffer-bridge.inc +6 -0
- package/runtime/ext_node/fs/parts/builtins.inc +69 -0
- package/runtime/ext_node/fs/parts/dir.inc +47 -0
- package/runtime/ext_node/fs/parts/encoding.inc +186 -0
- package/runtime/ext_node/fs/parts/errors.inc +66 -0
- package/runtime/ext_node/fs/parts/flags.inc +39 -0
- package/runtime/ext_node/fs/parts/platform.inc +86 -0
- package/runtime/ext_node/fs/parts/promisify.inc +15 -0
- package/runtime/ext_node/fs/parts/time.inc +9 -0
- package/runtime/rt.h +33 -0
- package/runtime/xt_builtins.c +4 -0
- package/runtime/xt_containers/access.inc +168 -0
- package/runtime/xt_containers/arrays.inc +108 -0
- package/runtime/xt_containers/boxes.inc +11 -0
- package/runtime/xt_containers/objects.inc +326 -0
- package/runtime/xt_containers.c +8 -618
- package/runtime/xt_io/base64.inc +87 -0
- package/runtime/xt_io/console.inc +119 -0
- package/runtime/xt_io/exception.inc +142 -0
- package/runtime/xt_io/inspect.inc +212 -0
- package/runtime/xt_io.c +8 -553
- package/runtime/xt_loop.c +262 -24
- package/runtime/xt_regex/api.inc +58 -0
- package/runtime/xt_regex/helpers.inc +68 -0
- package/runtime/xt_regex/matcher.inc +248 -0
- package/runtime/xt_regex/parser.inc +236 -0
- package/runtime/xt_regex/types.inc +58 -0
- package/runtime/xt_regex.h +9 -672
- package/runtime/xt_values/arithmetic.inc +150 -0
- package/runtime/xt_values/comparison.inc +112 -0
- package/runtime/xt_values/constructors.inc +41 -0
- package/runtime/xt_values/conversions.inc +115 -0
- package/runtime/xt_values/number-format.inc +180 -0
- package/runtime/xt_values/strings.inc +22 -0
- package/runtime/xt_values.c +10 -626
- package/scripts/build-gui.ts +317 -0
- package/src/cli/main.ts +4 -3
- package/src/codegen/generator/state.ts +8 -0
- package/src/codegen/generator/tables.ts +8 -0
- package/src/driver/bundler/graph.ts +95 -0
- package/src/driver/bundler/merge.ts +312 -0
- package/src/driver/bundler/resolve.ts +240 -0
- package/src/driver/bundler/symbols.ts +55 -0
- package/src/driver/bundler/types.ts +37 -0
- package/src/driver/modules.ts +9 -693
- package/src/extensions/catalog.ts +2 -1
- package/src/extensions/gui/index.ts +107 -0
- package/dist/tests/e2e/compile.test.d.ts +0 -8
- package/dist/tests/e2e/compile.test.js +0 -893
- package/dist/tests/e2e/compile.test.js.map +0 -1
- package/dist/tests/e2e/differential.test.d.ts +0 -7
- package/dist/tests/e2e/differential.test.js.map +0 -1
- package/dist/tests/e2e/runtime-coverage.test.d.ts +0 -11
- package/dist/tests/e2e/runtime-coverage.test.js +0 -646
- package/dist/tests/e2e/runtime-coverage.test.js.map +0 -1
|
@@ -0,0 +1,771 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — cascade, inheritance and computed style.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
#include "style.h"
|
|
6
|
+
|
|
7
|
+
#include <algorithm>
|
|
8
|
+
#include <climits>
|
|
9
|
+
#include <cmath>
|
|
10
|
+
#include <cstdio>
|
|
11
|
+
#include <cstdlib>
|
|
12
|
+
#include <unordered_map>
|
|
13
|
+
#include <vector>
|
|
14
|
+
|
|
15
|
+
namespace xtgui {
|
|
16
|
+
|
|
17
|
+
XtStyle xt_style_initial() { return XtStyle(); }
|
|
18
|
+
|
|
19
|
+
/* -- small helpers -------------------------------------------------------- */
|
|
20
|
+
|
|
21
|
+
static void setEdge(Edges *edges, const std::string &side, Length value) {
|
|
22
|
+
if (side == "top") edges->top = value;
|
|
23
|
+
else if (side == "right") edges->right = value;
|
|
24
|
+
else if (side == "bottom") edges->bottom = value;
|
|
25
|
+
else if (side == "left") edges->left = value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static bool parseEdgeLengths(const std::string &value, Edges *edges) {
|
|
29
|
+
std::vector<std::string> parts = xt_css_split_whitespace(value);
|
|
30
|
+
if (parts.empty() || parts.size() > 4) return false;
|
|
31
|
+
Length values[4];
|
|
32
|
+
for (size_t i = 0; i < parts.size(); i++) {
|
|
33
|
+
Length length = xt_css_parse_length(parts[i]);
|
|
34
|
+
if (length.unit == Unit::Invalid) {
|
|
35
|
+
std::string keyword = xt_css_lower(parts[i]);
|
|
36
|
+
if (keyword == "thin") length = {1.0f, Unit::Px};
|
|
37
|
+
else if (keyword == "medium") length = {3.0f, Unit::Px};
|
|
38
|
+
else if (keyword == "thick") length = {5.0f, Unit::Px};
|
|
39
|
+
else return false;
|
|
40
|
+
}
|
|
41
|
+
values[i] = length;
|
|
42
|
+
}
|
|
43
|
+
switch (parts.size()) {
|
|
44
|
+
case 1:
|
|
45
|
+
edges->set(values[0]);
|
|
46
|
+
break;
|
|
47
|
+
case 2:
|
|
48
|
+
edges->top = edges->bottom = values[0];
|
|
49
|
+
edges->right = edges->left = values[1];
|
|
50
|
+
break;
|
|
51
|
+
case 3:
|
|
52
|
+
edges->top = values[0];
|
|
53
|
+
edges->right = edges->left = values[1];
|
|
54
|
+
edges->bottom = values[2];
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
edges->top = values[0];
|
|
58
|
+
edges->right = values[1];
|
|
59
|
+
edges->bottom = values[2];
|
|
60
|
+
edges->left = values[3];
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static bool assignLength(Length *target, const std::string &value) {
|
|
67
|
+
Length length = xt_css_parse_length(value);
|
|
68
|
+
if (length.unit == Unit::Invalid) return false;
|
|
69
|
+
*target = length;
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static bool assignColor(Color *target, bool *has, const std::string &value) {
|
|
74
|
+
Color color = xt_css_parse_color(value);
|
|
75
|
+
if (!color.valid) return false;
|
|
76
|
+
*target = color;
|
|
77
|
+
*has = true;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static float resolveAbsolute(const Length &length, float base, float em, float root, float vw, float vh) {
|
|
82
|
+
switch (length.unit) {
|
|
83
|
+
case Unit::Px:
|
|
84
|
+
case Unit::Number:
|
|
85
|
+
return length.value;
|
|
86
|
+
case Unit::Percent:
|
|
87
|
+
return base * length.value / 100.0f;
|
|
88
|
+
case Unit::Em:
|
|
89
|
+
return em * length.value;
|
|
90
|
+
case Unit::Rem:
|
|
91
|
+
return root * length.value;
|
|
92
|
+
case Unit::Vw:
|
|
93
|
+
return vw * length.value / 100.0f;
|
|
94
|
+
case Unit::Vh:
|
|
95
|
+
return vh * length.value / 100.0f;
|
|
96
|
+
default:
|
|
97
|
+
return 0.0f;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* -- user-agent stylesheet ------------------------------------------------ */
|
|
102
|
+
|
|
103
|
+
static bool isBlockTag(const std::string &tag) {
|
|
104
|
+
static const std::unordered_map<std::string, bool> tags = {
|
|
105
|
+
{"address", true}, {"article", true}, {"aside", true}, {"blockquote", true},
|
|
106
|
+
{"dd", true}, {"div", true}, {"dl", true}, {"dt", true},
|
|
107
|
+
{"fieldset", true}, {"figcaption", true}, {"figure", true}, {"footer", true},
|
|
108
|
+
{"form", true}, {"h1", true}, {"h2", true}, {"h3", true},
|
|
109
|
+
{"h4", true}, {"h5", true}, {"h6", true}, {"header", true},
|
|
110
|
+
{"hr", true}, {"li", true}, {"main", true}, {"nav", true},
|
|
111
|
+
{"ol", true}, {"p", true}, {"pre", true}, {"section", true},
|
|
112
|
+
{"table", true}, {"tbody", true}, {"td", true}, {"tfoot", true},
|
|
113
|
+
{"th", true}, {"thead", true}, {"tr", true}, {"ul", true},
|
|
114
|
+
};
|
|
115
|
+
return tags.count(tag) != 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static void applyUserAgentStyle(const Node *node, XtStyle *style) {
|
|
119
|
+
const std::string &tag = node->tag;
|
|
120
|
+
if (tag == "html" || tag == "body") style->display = Display::Block;
|
|
121
|
+
if (isBlockTag(tag)) style->display = Display::Block;
|
|
122
|
+
if (tag == "head" || tag == "style" || tag == "script" || tag == "title" || tag == "meta" ||
|
|
123
|
+
tag == "link" || tag == "base" || tag == "template") {
|
|
124
|
+
style->display = Display::None;
|
|
125
|
+
}
|
|
126
|
+
if (tag == "br") style->display = Display::Block;
|
|
127
|
+
if (tag == "button" || tag == "input" || tag == "select" || tag == "textarea") {
|
|
128
|
+
style->display = Display::InlineBlock;
|
|
129
|
+
}
|
|
130
|
+
if (tag == "img") style->display = Display::InlineBlock;
|
|
131
|
+
if (tag == "body") style->margin.set({8.0f, Unit::Px});
|
|
132
|
+
if (tag == "p") style->margin.top = style->margin.bottom = {16.0f, Unit::Px};
|
|
133
|
+
if (tag == "blockquote") {
|
|
134
|
+
style->margin.top = style->margin.bottom = {16.0f, Unit::Px};
|
|
135
|
+
style->margin.left = style->margin.right = {40.0f, Unit::Px};
|
|
136
|
+
}
|
|
137
|
+
if (tag == "h1") {
|
|
138
|
+
style->font_size = 32.0f;
|
|
139
|
+
style->font_weight = 700;
|
|
140
|
+
style->margin.top = style->margin.bottom = {21.44f, Unit::Px};
|
|
141
|
+
} else if (tag == "h2") {
|
|
142
|
+
style->font_size = 24.0f;
|
|
143
|
+
style->font_weight = 700;
|
|
144
|
+
style->margin.top = style->margin.bottom = {19.92f, Unit::Px};
|
|
145
|
+
} else if (tag == "h3") {
|
|
146
|
+
style->font_size = 18.72f;
|
|
147
|
+
style->font_weight = 700;
|
|
148
|
+
style->margin.top = style->margin.bottom = {18.72f, Unit::Px};
|
|
149
|
+
} else if (tag == "h4") {
|
|
150
|
+
style->font_size = 16.0f;
|
|
151
|
+
style->font_weight = 700;
|
|
152
|
+
style->margin.top = style->margin.bottom = {21.28f, Unit::Px};
|
|
153
|
+
} else if (tag == "h5") {
|
|
154
|
+
style->font_size = 13.28f;
|
|
155
|
+
style->font_weight = 700;
|
|
156
|
+
style->margin.top = style->margin.bottom = {22.18f, Unit::Px};
|
|
157
|
+
} else if (tag == "h6") {
|
|
158
|
+
style->font_size = 10.72f;
|
|
159
|
+
style->font_weight = 700;
|
|
160
|
+
style->margin.top = style->margin.bottom = {24.97f, Unit::Px};
|
|
161
|
+
}
|
|
162
|
+
if (tag == "strong" || tag == "b") style->font_weight = 700;
|
|
163
|
+
if (tag == "dt") style->font_weight = 700;
|
|
164
|
+
if (tag == "em" || tag == "i") style->font_style = FontStyle::Italic;
|
|
165
|
+
if (tag == "small") style->font_size = 13.333f;
|
|
166
|
+
if (tag == "a") {
|
|
167
|
+
style->color = {0.0f, 0.0f, 0.933f, 1.0f};
|
|
168
|
+
}
|
|
169
|
+
if (tag == "ul" || tag == "ol") {
|
|
170
|
+
style->padding.left = {40.0f, Unit::Px};
|
|
171
|
+
style->margin.top = style->margin.bottom = {16.0f, Unit::Px};
|
|
172
|
+
}
|
|
173
|
+
if (tag == "pre" || tag == "code" || tag == "kbd" || tag == "samp") {
|
|
174
|
+
style->font_family = "monospace";
|
|
175
|
+
}
|
|
176
|
+
if (tag == "pre") style->white_space = WhiteSpace::Pre;
|
|
177
|
+
if (tag == "hr") {
|
|
178
|
+
style->border_width.top = {1.0f, Unit::Px};
|
|
179
|
+
style->border_style = BorderStyle::Solid;
|
|
180
|
+
style->border_color = {0.5f, 0.5f, 0.5f, 1.0f};
|
|
181
|
+
style->has_border_color = true;
|
|
182
|
+
style->margin.top = style->margin.bottom = {8.0f, Unit::Px};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/* -- declaration application ---------------------------------------------- */
|
|
187
|
+
|
|
188
|
+
static bool parseEnum(const std::string &value, const char *const *names, int count, int *out) {
|
|
189
|
+
std::string text = xt_css_lower(xt_css_trim(value));
|
|
190
|
+
for (int i = 0; i < count; i++) {
|
|
191
|
+
if (text == names[i]) {
|
|
192
|
+
*out = i;
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* -- transitions ---------------------------------------------------------- */
|
|
200
|
+
|
|
201
|
+
static bool parseTimeSeconds(const std::string &text, float *out) {
|
|
202
|
+
std::string token = xt_css_trim(text);
|
|
203
|
+
if (token.empty()) return false;
|
|
204
|
+
const char *begin = token.c_str();
|
|
205
|
+
char *end = nullptr;
|
|
206
|
+
double value = std::strtod(begin, &end);
|
|
207
|
+
if (end == begin) return false;
|
|
208
|
+
std::string unit = xt_css_lower(xt_css_trim(std::string(end)));
|
|
209
|
+
if (unit == "ms") {
|
|
210
|
+
*out = (float)(value / 1000.0);
|
|
211
|
+
} else if (unit.empty() || unit == "s") {
|
|
212
|
+
*out = (float)value;
|
|
213
|
+
} else {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
static bool parseTimingFunction(const std::string &text, TimingFunction *out) {
|
|
220
|
+
std::string token = xt_css_lower(xt_css_trim(text));
|
|
221
|
+
if (token == "linear") {
|
|
222
|
+
*out = TimingFunction::Linear;
|
|
223
|
+
} else if (token == "ease") {
|
|
224
|
+
*out = TimingFunction::Ease;
|
|
225
|
+
} else if (token == "ease-in") {
|
|
226
|
+
*out = TimingFunction::EaseIn;
|
|
227
|
+
} else if (token == "ease-out") {
|
|
228
|
+
*out = TimingFunction::EaseOut;
|
|
229
|
+
} else if (token == "ease-in-out") {
|
|
230
|
+
*out = TimingFunction::EaseInOut;
|
|
231
|
+
} else {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
static std::vector<std::string> splitCommas(const std::string &text) {
|
|
238
|
+
std::vector<std::string> parts;
|
|
239
|
+
size_t start = 0;
|
|
240
|
+
for (size_t i = 0; i <= text.size(); i++) {
|
|
241
|
+
if (i == text.size() || text[i] == ',') {
|
|
242
|
+
std::string part = xt_css_trim(text.substr(start, i - start));
|
|
243
|
+
if (!part.empty()) parts.push_back(part);
|
|
244
|
+
start = i + 1;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return parts;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static void parseTransitionShorthand(const std::string &value, std::vector<TransitionSpec> *out) {
|
|
251
|
+
out->clear();
|
|
252
|
+
for (const std::string &group : splitCommas(value)) {
|
|
253
|
+
std::vector<std::string> tokens = xt_css_split_whitespace(group);
|
|
254
|
+
if (tokens.empty()) continue;
|
|
255
|
+
TransitionSpec spec;
|
|
256
|
+
int time_seen = 0;
|
|
257
|
+
for (const std::string &token : tokens) {
|
|
258
|
+
float seconds = 0;
|
|
259
|
+
TimingFunction timing = TimingFunction::Ease;
|
|
260
|
+
if (parseTimeSeconds(token, &seconds)) {
|
|
261
|
+
if (time_seen == 0) {
|
|
262
|
+
spec.duration = seconds;
|
|
263
|
+
} else {
|
|
264
|
+
spec.delay = seconds;
|
|
265
|
+
}
|
|
266
|
+
time_seen++;
|
|
267
|
+
} else if (parseTimingFunction(token, &timing)) {
|
|
268
|
+
spec.timing = timing;
|
|
269
|
+
} else {
|
|
270
|
+
spec.property = xt_css_lower(token);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
out->push_back(spec);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
struct ApplyContext {
|
|
278
|
+
float rootFontSize = 16.0f;
|
|
279
|
+
float parentFontSize = 16.0f;
|
|
280
|
+
float viewportWidth = 0.0f;
|
|
281
|
+
float viewportHeight = 0.0f;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
static void applyDeclaration(XtStyle *style, const std::string &property, const std::string &rawValue,
|
|
285
|
+
const ApplyContext &context) {
|
|
286
|
+
std::string value = xt_css_trim(rawValue);
|
|
287
|
+
|
|
288
|
+
if (property == "display") {
|
|
289
|
+
static const char *const names[] = {"none", "block", "inline", "inline-block", "flex", "list-item"};
|
|
290
|
+
int index = 0;
|
|
291
|
+
if (parseEnum(value, names, 6, &index)) style->display = (Display)index;
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (property == "position") {
|
|
295
|
+
static const char *const names[] = {"static", "relative", "absolute", "fixed"};
|
|
296
|
+
int index = 0;
|
|
297
|
+
if (parseEnum(value, names, 4, &index)) style->position = (Position)index;
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (property == "width") { assignLength(&style->width, value); return; }
|
|
301
|
+
if (property == "height") { assignLength(&style->height, value); return; }
|
|
302
|
+
if (property == "min-width") { assignLength(&style->min_width, value); return; }
|
|
303
|
+
if (property == "min-height") { assignLength(&style->min_height, value); return; }
|
|
304
|
+
if (property == "max-width") { assignLength(&style->max_width, value); return; }
|
|
305
|
+
if (property == "max-height") { assignLength(&style->max_height, value); return; }
|
|
306
|
+
if (property == "top") { assignLength(&style->top, value); return; }
|
|
307
|
+
if (property == "right") { assignLength(&style->right, value); return; }
|
|
308
|
+
if (property == "bottom") { assignLength(&style->bottom, value); return; }
|
|
309
|
+
if (property == "left") { assignLength(&style->left, value); return; }
|
|
310
|
+
|
|
311
|
+
if (property == "margin") { parseEdgeLengths(value, &style->margin); return; }
|
|
312
|
+
if (property == "padding") { parseEdgeLengths(value, &style->padding); return; }
|
|
313
|
+
if (property == "margin-top") { setEdge(&style->margin, "top", xt_css_parse_length(value)); return; }
|
|
314
|
+
if (property == "margin-right") { setEdge(&style->margin, "right", xt_css_parse_length(value)); return; }
|
|
315
|
+
if (property == "margin-bottom") { setEdge(&style->margin, "bottom", xt_css_parse_length(value)); return; }
|
|
316
|
+
if (property == "margin-left") { setEdge(&style->margin, "left", xt_css_parse_length(value)); return; }
|
|
317
|
+
if (property == "padding-top") { setEdge(&style->padding, "top", xt_css_parse_length(value)); return; }
|
|
318
|
+
if (property == "padding-right") { setEdge(&style->padding, "right", xt_css_parse_length(value)); return; }
|
|
319
|
+
if (property == "padding-bottom") { setEdge(&style->padding, "bottom", xt_css_parse_length(value)); return; }
|
|
320
|
+
if (property == "padding-left") { setEdge(&style->padding, "left", xt_css_parse_length(value)); return; }
|
|
321
|
+
|
|
322
|
+
if (property == "border-width") { parseEdgeLengths(value, &style->border_width); return; }
|
|
323
|
+
if (property == "border-top-width") { setEdge(&style->border_width, "top", xt_css_parse_length(value)); return; }
|
|
324
|
+
if (property == "border-right-width") { setEdge(&style->border_width, "right", xt_css_parse_length(value)); return; }
|
|
325
|
+
if (property == "border-bottom-width") { setEdge(&style->border_width, "bottom", xt_css_parse_length(value)); return; }
|
|
326
|
+
if (property == "border-left-width") { setEdge(&style->border_width, "left", xt_css_parse_length(value)); return; }
|
|
327
|
+
if (property == "border-radius") {
|
|
328
|
+
Length length = xt_css_parse_length(value);
|
|
329
|
+
if (length.unit == Unit::Px || length.unit == Unit::Number) style->border_radius = length.value;
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (property == "border-style" || property == "border-top-style" || property == "border-right-style" ||
|
|
333
|
+
property == "border-bottom-style" || property == "border-left-style") {
|
|
334
|
+
static const char *const names[] = {"none", "solid", "dashed", "dotted"};
|
|
335
|
+
int index = 0;
|
|
336
|
+
if (parseEnum(value, names, 4, &index)) style->border_style = (BorderStyle)index;
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (property == "border-color" || property == "border-top-color" || property == "border-right-color" ||
|
|
340
|
+
property == "border-bottom-color" || property == "border-left-color") {
|
|
341
|
+
assignColor(&style->border_color, &style->has_border_color, value);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (property == "border" || property == "border-top" || property == "border-right" ||
|
|
345
|
+
property == "border-bottom" || property == "border-left") {
|
|
346
|
+
for (const std::string &token : xt_css_split_whitespace(value)) {
|
|
347
|
+
Length length = xt_css_parse_length(token);
|
|
348
|
+
if (length.unit == Unit::Px || length.unit == Unit::Number) {
|
|
349
|
+
if (property == "border") style->border_width.set(length);
|
|
350
|
+
else setEdge(&style->border_width, property.substr(7), length);
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
static const char *const names[] = {"none", "solid", "dashed", "dotted"};
|
|
354
|
+
int index = 0;
|
|
355
|
+
if (parseEnum(token, names, 4, &index)) {
|
|
356
|
+
style->border_style = (BorderStyle)index;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
Color color = xt_css_parse_color(token);
|
|
360
|
+
if (color.valid) {
|
|
361
|
+
style->border_color = color;
|
|
362
|
+
style->has_border_color = true;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (property == "color") {
|
|
369
|
+
Color color = xt_css_parse_color(value);
|
|
370
|
+
if (color.valid) style->color = color;
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (property == "background-color") { assignColor(&style->background, &style->has_background, value); return; }
|
|
374
|
+
if (property == "background") {
|
|
375
|
+
Color color = xt_css_parse_color(value);
|
|
376
|
+
if (color.valid) {
|
|
377
|
+
style->background = color;
|
|
378
|
+
style->has_background = true;
|
|
379
|
+
}
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (property == "font-size") {
|
|
384
|
+
Length length = xt_css_parse_length(value);
|
|
385
|
+
if (length.unit == Unit::Invalid) return;
|
|
386
|
+
/* `em`/`%` resolve against the *parent* font size, never the element's own
|
|
387
|
+
* (possibly UA-set) size. */
|
|
388
|
+
style->font_size = resolveAbsolute(length, context.parentFontSize, context.parentFontSize,
|
|
389
|
+
context.rootFontSize, context.viewportWidth, context.viewportHeight);
|
|
390
|
+
if (style->font_size <= 0.0f) style->font_size = 16.0f;
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (property == "font-weight") {
|
|
394
|
+
std::string text = xt_css_lower(value);
|
|
395
|
+
if (text == "normal") style->font_weight = 400;
|
|
396
|
+
else if (text == "bold") style->font_weight = 700;
|
|
397
|
+
else if (text == "lighter") style->font_weight = 100;
|
|
398
|
+
else if (text == "bolder") style->font_weight = 700;
|
|
399
|
+
else {
|
|
400
|
+
float number = 0;
|
|
401
|
+
if (xt_css_parse_number(text, &number)) style->font_weight = (int)number;
|
|
402
|
+
}
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (property == "font-family") {
|
|
406
|
+
style->font_family = value;
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (property == "font-style") {
|
|
410
|
+
style->font_style = (xt_css_lower(value) == "italic") ? FontStyle::Italic : FontStyle::Normal;
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
if (property == "line-height") {
|
|
414
|
+
std::string text = xt_css_lower(value);
|
|
415
|
+
if (text == "normal") {
|
|
416
|
+
style->line_height = {};
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
Length length = xt_css_parse_length(text);
|
|
420
|
+
if (length.unit == Unit::Invalid) return;
|
|
421
|
+
/* Relative units are resolved in `finalizeStyle`, once the element's own
|
|
422
|
+
* font size is settled. */
|
|
423
|
+
style->line_height = length;
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (property == "text-align") {
|
|
427
|
+
std::string text = xt_css_lower(value);
|
|
428
|
+
if (text == "left" || text == "start") style->text_align = TextAlign::Start;
|
|
429
|
+
else if (text == "right" || text == "end") style->text_align = TextAlign::End;
|
|
430
|
+
else if (text == "center") style->text_align = TextAlign::Center;
|
|
431
|
+
else if (text == "justify") style->text_align = TextAlign::Justify;
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (property == "white-space") {
|
|
435
|
+
static const char *const names[] = {"normal", "nowrap", "pre", "pre-wrap", "pre-line"};
|
|
436
|
+
int index = 0;
|
|
437
|
+
if (parseEnum(value, names, 5, &index)) style->white_space = (WhiteSpace)index;
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (property == "flex-direction") {
|
|
442
|
+
static const char *const names[] = {"row", "row-reverse", "column", "column-reverse"};
|
|
443
|
+
int index = 0;
|
|
444
|
+
if (parseEnum(value, names, 4, &index)) style->flex_direction = (FlexDirection)index;
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (property == "justify-content") {
|
|
448
|
+
static const char *const names[] = {"flex-start", "flex-end", "center", "space-between",
|
|
449
|
+
"space-around", "space-evenly"};
|
|
450
|
+
int index = 0;
|
|
451
|
+
if (parseEnum(value, names, 6, &index)) style->justify_content = (JustifyContent)index;
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if (property == "align-items") {
|
|
455
|
+
static const char *const names[] = {"stretch", "flex-start", "flex-end", "center", "baseline"};
|
|
456
|
+
int index = 0;
|
|
457
|
+
if (parseEnum(value, names, 5, &index)) style->align_items = (AlignItems)index;
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (property == "gap" || property == "row-gap" || property == "column-gap") {
|
|
461
|
+
assignLength(&style->gap, value);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
if (property == "flex-grow") {
|
|
465
|
+
float number = 0;
|
|
466
|
+
if (xt_css_parse_number(value, &number)) style->flex_grow = number;
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (property == "flex-shrink") {
|
|
470
|
+
float number = 0;
|
|
471
|
+
if (xt_css_parse_number(value, &number)) style->flex_shrink = number;
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
if (property == "flex-basis") { assignLength(&style->flex_basis, value); return; }
|
|
475
|
+
if (property == "flex") {
|
|
476
|
+
std::string text = xt_css_lower(value);
|
|
477
|
+
if (text == "none") {
|
|
478
|
+
style->flex_grow = 0;
|
|
479
|
+
style->flex_shrink = 0;
|
|
480
|
+
style->flex_basis = {0.0f, Unit::Auto};
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
if (text == "auto") {
|
|
484
|
+
style->flex_grow = 1;
|
|
485
|
+
style->flex_shrink = 1;
|
|
486
|
+
style->flex_basis = {0.0f, Unit::Auto};
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
if (text == "initial") {
|
|
490
|
+
style->flex_grow = 0;
|
|
491
|
+
style->flex_shrink = 1;
|
|
492
|
+
style->flex_basis = {0.0f, Unit::Auto};
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
std::vector<std::string> parts = xt_css_split_whitespace(text);
|
|
496
|
+
style->flex_grow = 0;
|
|
497
|
+
style->flex_shrink = 1;
|
|
498
|
+
style->flex_basis = {0.0f, Unit::Auto};
|
|
499
|
+
size_t index = 0;
|
|
500
|
+
float number = 0;
|
|
501
|
+
if (index < parts.size() && xt_css_parse_number(parts[index], &number)) {
|
|
502
|
+
style->flex_grow = number;
|
|
503
|
+
index++;
|
|
504
|
+
if (index < parts.size() && xt_css_parse_number(parts[index], &number)) {
|
|
505
|
+
style->flex_shrink = number;
|
|
506
|
+
index++;
|
|
507
|
+
}
|
|
508
|
+
if (index >= parts.size()) style->flex_basis = {0.0f, Unit::Px};
|
|
509
|
+
}
|
|
510
|
+
if (index < parts.size()) assignLength(&style->flex_basis, parts[index]);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (property == "opacity") {
|
|
515
|
+
float number = 0;
|
|
516
|
+
if (xt_css_parse_number(value, &number)) style->opacity = number < 0 ? 0 : (number > 1 ? 1 : number);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
if (property == "overflow" || property == "overflow-x" || property == "overflow-y") {
|
|
520
|
+
static const char *const names[] = {"visible", "hidden", "scroll", "auto"};
|
|
521
|
+
int index = 0;
|
|
522
|
+
if (parseEnum(value, names, 4, &index)) style->overflow = (Overflow)index;
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (property == "z-index") {
|
|
526
|
+
float number = 0;
|
|
527
|
+
if (xt_css_parse_number(value, &number)) {
|
|
528
|
+
style->z_index = (int)number;
|
|
529
|
+
style->has_z_index = true;
|
|
530
|
+
}
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
if (property == "transition") {
|
|
534
|
+
parseTransitionShorthand(value, &style->transitions);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
if (property == "transition-property") {
|
|
538
|
+
std::vector<std::string> parts = splitCommas(value);
|
|
539
|
+
style->transitions.resize(parts.size());
|
|
540
|
+
for (size_t i = 0; i < parts.size(); i++) style->transitions[i].property = xt_css_lower(parts[i]);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (property == "transition-duration" || property == "transition-delay") {
|
|
544
|
+
std::vector<std::string> parts = splitCommas(value);
|
|
545
|
+
style->transitions.resize(parts.size());
|
|
546
|
+
for (size_t i = 0; i < parts.size(); i++) {
|
|
547
|
+
float seconds = 0;
|
|
548
|
+
if (!parseTimeSeconds(parts[i], &seconds)) continue;
|
|
549
|
+
if (property == "transition-duration") style->transitions[i].duration = seconds;
|
|
550
|
+
else style->transitions[i].delay = seconds;
|
|
551
|
+
}
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
if (property == "transition-timing-function") {
|
|
555
|
+
std::vector<std::string> parts = splitCommas(value);
|
|
556
|
+
style->transitions.resize(parts.size());
|
|
557
|
+
for (size_t i = 0; i < parts.size(); i++) {
|
|
558
|
+
TimingFunction timing = TimingFunction::Ease;
|
|
559
|
+
if (parseTimingFunction(parts[i], &timing)) style->transitions[i].timing = timing;
|
|
560
|
+
}
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/* -- cascade -------------------------------------------------------------- */
|
|
566
|
+
|
|
567
|
+
namespace {
|
|
568
|
+
|
|
569
|
+
struct Candidate {
|
|
570
|
+
int important = 0;
|
|
571
|
+
int specificity = 0;
|
|
572
|
+
int order = 0;
|
|
573
|
+
const Declaration *declaration = nullptr;
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
bool candidateBefore(const Candidate &a, const Candidate &b) {
|
|
577
|
+
if (a.important != b.important) return a.important < b.important;
|
|
578
|
+
if (a.specificity != b.specificity) return a.specificity < b.specificity;
|
|
579
|
+
return a.order < b.order;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
static void finalizeStyle(XtStyle *style, const ApplyContext &context) {
|
|
583
|
+
switch (style->line_height.unit) {
|
|
584
|
+
case Unit::Em:
|
|
585
|
+
case Unit::Percent:
|
|
586
|
+
case Unit::Rem:
|
|
587
|
+
case Unit::Vw:
|
|
588
|
+
case Unit::Vh:
|
|
589
|
+
style->line_height.value = resolveAbsolute(style->line_height, style->font_size, style->font_size,
|
|
590
|
+
context.rootFontSize, context.viewportWidth,
|
|
591
|
+
context.viewportHeight);
|
|
592
|
+
style->line_height.unit = Unit::Px;
|
|
593
|
+
break;
|
|
594
|
+
default:
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
void computeNode(const Node *node, const XtStyle *parent, const StyleSheet &sheet,
|
|
600
|
+
std::unordered_map<const Node *, XtStyle> &out, const ApplyContext &context,
|
|
601
|
+
const MatchState &state) {
|
|
602
|
+
const XtStyle *childParent = parent;
|
|
603
|
+
if (node->isElement()) {
|
|
604
|
+
XtStyle style = xt_style_initial();
|
|
605
|
+
if (parent != nullptr) {
|
|
606
|
+
/* Inherited properties start from the parent; everything else keeps its
|
|
607
|
+
* initial value until a declaration overrides it. */
|
|
608
|
+
style.color = parent->color;
|
|
609
|
+
style.font_size = parent->font_size;
|
|
610
|
+
style.font_weight = parent->font_weight;
|
|
611
|
+
style.font_family = parent->font_family;
|
|
612
|
+
style.font_style = parent->font_style;
|
|
613
|
+
style.line_height = parent->line_height;
|
|
614
|
+
style.text_align = parent->text_align;
|
|
615
|
+
style.white_space = parent->white_space;
|
|
616
|
+
} else {
|
|
617
|
+
style.color = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
618
|
+
}
|
|
619
|
+
applyUserAgentStyle(node, &style);
|
|
620
|
+
|
|
621
|
+
ApplyContext nodeContext = context;
|
|
622
|
+
nodeContext.parentFontSize = (parent != nullptr) ? parent->font_size : context.rootFontSize;
|
|
623
|
+
|
|
624
|
+
std::vector<Candidate> candidates;
|
|
625
|
+
for (const Rule &rule : sheet.rules) {
|
|
626
|
+
for (const ComplexSelector &selector : rule.selectors) {
|
|
627
|
+
if (!xt_css_match(node, selector, state)) continue;
|
|
628
|
+
for (const Declaration &declaration : rule.declarations) {
|
|
629
|
+
candidates.push_back({declaration.important ? 1 : 0, selector.specificity, rule.order, &declaration});
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
const std::string *inlineStyle = node->attr("style");
|
|
634
|
+
std::vector<Declaration> inlineDeclarations;
|
|
635
|
+
if (inlineStyle != nullptr) {
|
|
636
|
+
inlineDeclarations = xt_css_parse_declarations(*inlineStyle);
|
|
637
|
+
for (const Declaration &declaration : inlineDeclarations) {
|
|
638
|
+
candidates.push_back({declaration.important ? 1 : 0, 1 << 28, INT_MAX, &declaration});
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
std::stable_sort(candidates.begin(), candidates.end(), candidateBefore);
|
|
642
|
+
for (const Candidate &candidate : candidates) {
|
|
643
|
+
applyDeclaration(&style, candidate.declaration->property, candidate.declaration->value, nodeContext);
|
|
644
|
+
}
|
|
645
|
+
finalizeStyle(&style, nodeContext);
|
|
646
|
+
|
|
647
|
+
out[node] = style;
|
|
648
|
+
childParent = &out[node];
|
|
649
|
+
}
|
|
650
|
+
for (const std::unique_ptr<Node> &child : node->children) {
|
|
651
|
+
computeNode(child.get(), childParent, sheet, out, context, state);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
} // namespace
|
|
656
|
+
|
|
657
|
+
void xt_style_compute(const Node *root, const StyleSheet &sheet,
|
|
658
|
+
std::unordered_map<const Node *, XtStyle> &out, float viewportWidth,
|
|
659
|
+
float viewportHeight, const MatchState &state) {
|
|
660
|
+
out.clear();
|
|
661
|
+
ApplyContext context;
|
|
662
|
+
context.viewportWidth = viewportWidth;
|
|
663
|
+
context.viewportHeight = viewportHeight;
|
|
664
|
+
computeNode(root, nullptr, sheet, out, context, state);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/* -- diagnostics ---------------------------------------------------------- */
|
|
668
|
+
|
|
669
|
+
static std::string formatNumber(float number) {
|
|
670
|
+
char buffer[64];
|
|
671
|
+
if (number == (float)(long)number) {
|
|
672
|
+
snprintf(buffer, sizeof(buffer), "%ld", (long)number);
|
|
673
|
+
} else {
|
|
674
|
+
snprintf(buffer, sizeof(buffer), "%.4g", number);
|
|
675
|
+
}
|
|
676
|
+
return buffer;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
static std::string formatLength(const Length &length) {
|
|
680
|
+
switch (length.unit) {
|
|
681
|
+
case Unit::Auto:
|
|
682
|
+
return "auto";
|
|
683
|
+
case Unit::None:
|
|
684
|
+
return "none";
|
|
685
|
+
case Unit::Number:
|
|
686
|
+
return formatNumber(length.value);
|
|
687
|
+
case Unit::Px:
|
|
688
|
+
return formatNumber(length.value) + "px";
|
|
689
|
+
case Unit::Percent:
|
|
690
|
+
return formatNumber(length.value) + "%";
|
|
691
|
+
case Unit::Em:
|
|
692
|
+
return formatNumber(length.value) + "em";
|
|
693
|
+
case Unit::Rem:
|
|
694
|
+
return formatNumber(length.value) + "rem";
|
|
695
|
+
case Unit::Vw:
|
|
696
|
+
return formatNumber(length.value) + "vw";
|
|
697
|
+
case Unit::Vh:
|
|
698
|
+
return formatNumber(length.value) + "vh";
|
|
699
|
+
default:
|
|
700
|
+
return "";
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
static std::string formatColor(const Color &color) {
|
|
705
|
+
if (color.a >= 1.0f) {
|
|
706
|
+
char buffer[64];
|
|
707
|
+
snprintf(buffer, sizeof(buffer), "rgb(%ld, %ld, %ld)", (long)(color.r * 255.0f + 0.5f),
|
|
708
|
+
(long)(color.g * 255.0f + 0.5f), (long)(color.b * 255.0f + 0.5f));
|
|
709
|
+
return buffer;
|
|
710
|
+
}
|
|
711
|
+
char buffer[96];
|
|
712
|
+
snprintf(buffer, sizeof(buffer), "rgba(%ld, %ld, %ld, %.2f)", (long)(color.r * 255.0f + 0.5f),
|
|
713
|
+
(long)(color.g * 255.0f + 0.5f), (long)(color.b * 255.0f + 0.5f), color.a);
|
|
714
|
+
return buffer;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
std::string xt_style_property_to_string(const XtStyle &style, const std::string &rawProperty) {
|
|
718
|
+
std::string property = xt_css_lower(xt_css_trim(rawProperty));
|
|
719
|
+
static const char *const displayNames[] = {"none", "block", "inline", "inline-block", "flex", "list-item"};
|
|
720
|
+
static const char *const positionNames[] = {"static", "relative", "absolute", "fixed"};
|
|
721
|
+
static const char *const flexNames[] = {"row", "row-reverse", "column", "column-reverse"};
|
|
722
|
+
static const char *const justifyNames[] = {"flex-start", "flex-end", "center", "space-between",
|
|
723
|
+
"space-around", "space-evenly"};
|
|
724
|
+
static const char *const alignNames[] = {"stretch", "flex-start", "flex-end", "center", "baseline"};
|
|
725
|
+
static const char *const alignTextNames[] = {"start", "center", "end", "justify"};
|
|
726
|
+
|
|
727
|
+
if (property == "display") return displayNames[(int)style.display];
|
|
728
|
+
if (property == "position") return positionNames[(int)style.position];
|
|
729
|
+
if (property == "color") return formatColor(style.color);
|
|
730
|
+
if (property == "background-color" || property == "background") {
|
|
731
|
+
return style.has_background ? formatColor(style.background) : "rgba(0, 0, 0, 0)";
|
|
732
|
+
}
|
|
733
|
+
if (property == "width") return formatLength(style.width);
|
|
734
|
+
if (property == "height") return formatLength(style.height);
|
|
735
|
+
if (property == "min-width") return formatLength(style.min_width);
|
|
736
|
+
if (property == "min-height") return formatLength(style.min_height);
|
|
737
|
+
if (property == "max-width") return formatLength(style.max_width);
|
|
738
|
+
if (property == "max-height") return formatLength(style.max_height);
|
|
739
|
+
if (property == "top") return formatLength(style.top);
|
|
740
|
+
if (property == "right") return formatLength(style.right);
|
|
741
|
+
if (property == "bottom") return formatLength(style.bottom);
|
|
742
|
+
if (property == "left") return formatLength(style.left);
|
|
743
|
+
if (property == "margin-top") return formatLength(style.margin.top);
|
|
744
|
+
if (property == "margin-right") return formatLength(style.margin.right);
|
|
745
|
+
if (property == "margin-bottom") return formatLength(style.margin.bottom);
|
|
746
|
+
if (property == "margin-left") return formatLength(style.margin.left);
|
|
747
|
+
if (property == "padding-top") return formatLength(style.padding.top);
|
|
748
|
+
if (property == "padding-right") return formatLength(style.padding.right);
|
|
749
|
+
if (property == "padding-bottom") return formatLength(style.padding.bottom);
|
|
750
|
+
if (property == "padding-left") return formatLength(style.padding.left);
|
|
751
|
+
if (property == "font-size") return formatNumber(style.font_size) + "px";
|
|
752
|
+
if (property == "font-weight") return formatNumber((float)style.font_weight);
|
|
753
|
+
if (property == "font-family") return style.font_family;
|
|
754
|
+
if (property == "line-height") {
|
|
755
|
+
return style.line_height.unit == Unit::Auto ? "normal" : formatLength(style.line_height);
|
|
756
|
+
}
|
|
757
|
+
if (property == "text-align") return alignTextNames[(int)style.text_align];
|
|
758
|
+
if (property == "flex-direction") return flexNames[(int)style.flex_direction];
|
|
759
|
+
if (property == "justify-content") return justifyNames[(int)style.justify_content];
|
|
760
|
+
if (property == "align-items") return alignNames[(int)style.align_items];
|
|
761
|
+
if (property == "gap") return formatLength(style.gap);
|
|
762
|
+
if (property == "flex-grow") return formatNumber(style.flex_grow);
|
|
763
|
+
if (property == "flex-shrink") return formatNumber(style.flex_shrink);
|
|
764
|
+
if (property == "flex-basis") return formatLength(style.flex_basis);
|
|
765
|
+
if (property == "border-radius") return formatNumber(style.border_radius) + "px";
|
|
766
|
+
if (property == "opacity") return formatNumber(style.opacity);
|
|
767
|
+
if (property == "z-index") return style.has_z_index ? formatNumber((float)style.z_index) : "auto";
|
|
768
|
+
return "";
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
} // namespace xtgui
|