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
@@ -0,0 +1,468 @@
1
+ /*
2
+ * xbintsc GUI engine — CSS declaration application (value parsing and how each
3
+ * property maps onto `XtStyle`).
4
+ */
5
+
6
+ #include "style_internal.h"
7
+
8
+ #include <cstdlib>
9
+ #include <vector>
10
+
11
+ namespace xtgui {
12
+
13
+ /* -- small helpers -------------------------------------------------------- */
14
+
15
+ static void setEdge(Edges *edges, const std::string &side, Length value) {
16
+ if (side == "top") edges->top = value;
17
+ else if (side == "right") edges->right = value;
18
+ else if (side == "bottom") edges->bottom = value;
19
+ else if (side == "left") edges->left = value;
20
+ }
21
+
22
+ static bool parseEdgeLengths(const std::string &value, Edges *edges) {
23
+ std::vector<std::string> parts = xt_css_split_whitespace(value);
24
+ if (parts.empty() || parts.size() > 4) return false;
25
+ Length values[4];
26
+ for (size_t i = 0; i < parts.size(); i++) {
27
+ Length length = xt_css_parse_length(parts[i]);
28
+ if (length.unit == Unit::Invalid) {
29
+ std::string keyword = xt_css_lower(parts[i]);
30
+ if (keyword == "thin") length = {1.0f, Unit::Px};
31
+ else if (keyword == "medium") length = {3.0f, Unit::Px};
32
+ else if (keyword == "thick") length = {5.0f, Unit::Px};
33
+ else return false;
34
+ }
35
+ values[i] = length;
36
+ }
37
+ switch (parts.size()) {
38
+ case 1:
39
+ edges->set(values[0]);
40
+ break;
41
+ case 2:
42
+ edges->top = edges->bottom = values[0];
43
+ edges->right = edges->left = values[1];
44
+ break;
45
+ case 3:
46
+ edges->top = values[0];
47
+ edges->right = edges->left = values[1];
48
+ edges->bottom = values[2];
49
+ break;
50
+ default:
51
+ edges->top = values[0];
52
+ edges->right = values[1];
53
+ edges->bottom = values[2];
54
+ edges->left = values[3];
55
+ break;
56
+ }
57
+ return true;
58
+ }
59
+
60
+ static bool assignLength(Length *target, const std::string &value) {
61
+ Length length = xt_css_parse_length(value);
62
+ if (length.unit == Unit::Invalid) return false;
63
+ *target = length;
64
+ return true;
65
+ }
66
+
67
+ static bool assignColor(Color *target, bool *has, const std::string &value) {
68
+ Color color = xt_css_parse_color(value);
69
+ if (!color.valid) return false;
70
+ *target = color;
71
+ *has = true;
72
+ return true;
73
+ }
74
+
75
+ float xt_style_resolve_absolute(const Length &length, float base, float em, float root, float vw,
76
+ float vh) {
77
+ switch (length.unit) {
78
+ case Unit::Px:
79
+ case Unit::Number:
80
+ return length.value;
81
+ case Unit::Percent:
82
+ return base * length.value / 100.0f;
83
+ case Unit::Em:
84
+ return em * length.value;
85
+ case Unit::Rem:
86
+ return root * length.value;
87
+ case Unit::Vw:
88
+ return vw * length.value / 100.0f;
89
+ case Unit::Vh:
90
+ return vh * length.value / 100.0f;
91
+ default:
92
+ return 0.0f;
93
+ }
94
+ }
95
+
96
+ static bool parseEnum(const std::string &value, const char *const *names, int count, int *out) {
97
+ std::string text = xt_css_lower(xt_css_trim(value));
98
+ for (int i = 0; i < count; i++) {
99
+ if (text == names[i]) {
100
+ *out = i;
101
+ return true;
102
+ }
103
+ }
104
+ return false;
105
+ }
106
+
107
+ /* -- transitions ---------------------------------------------------------- */
108
+
109
+ static bool parseTimeSeconds(const std::string &text, float *out) {
110
+ std::string token = xt_css_trim(text);
111
+ if (token.empty()) return false;
112
+ const char *begin = token.c_str();
113
+ char *end = nullptr;
114
+ double value = std::strtod(begin, &end);
115
+ if (end == begin) return false;
116
+ std::string unit = xt_css_lower(xt_css_trim(std::string(end)));
117
+ if (unit == "ms") {
118
+ *out = (float)(value / 1000.0);
119
+ } else if (unit.empty() || unit == "s") {
120
+ *out = (float)value;
121
+ } else {
122
+ return false;
123
+ }
124
+ return true;
125
+ }
126
+
127
+ static bool parseTimingFunction(const std::string &text, TimingFunction *out) {
128
+ std::string token = xt_css_lower(xt_css_trim(text));
129
+ if (token == "linear") {
130
+ *out = TimingFunction::Linear;
131
+ } else if (token == "ease") {
132
+ *out = TimingFunction::Ease;
133
+ } else if (token == "ease-in") {
134
+ *out = TimingFunction::EaseIn;
135
+ } else if (token == "ease-out") {
136
+ *out = TimingFunction::EaseOut;
137
+ } else if (token == "ease-in-out") {
138
+ *out = TimingFunction::EaseInOut;
139
+ } else {
140
+ return false;
141
+ }
142
+ return true;
143
+ }
144
+
145
+ static std::vector<std::string> splitCommas(const std::string &text) {
146
+ std::vector<std::string> parts;
147
+ size_t start = 0;
148
+ for (size_t i = 0; i <= text.size(); i++) {
149
+ if (i == text.size() || text[i] == ',') {
150
+ std::string part = xt_css_trim(text.substr(start, i - start));
151
+ if (!part.empty()) parts.push_back(part);
152
+ start = i + 1;
153
+ }
154
+ }
155
+ return parts;
156
+ }
157
+
158
+ static void parseTransitionShorthand(const std::string &value, std::vector<TransitionSpec> *out) {
159
+ out->clear();
160
+ for (const std::string &group : splitCommas(value)) {
161
+ std::vector<std::string> tokens = xt_css_split_whitespace(group);
162
+ if (tokens.empty()) continue;
163
+ TransitionSpec spec;
164
+ int time_seen = 0;
165
+ for (const std::string &token : tokens) {
166
+ float seconds = 0;
167
+ TimingFunction timing = TimingFunction::Ease;
168
+ if (parseTimeSeconds(token, &seconds)) {
169
+ if (time_seen == 0) {
170
+ spec.duration = seconds;
171
+ } else {
172
+ spec.delay = seconds;
173
+ }
174
+ time_seen++;
175
+ } else if (parseTimingFunction(token, &timing)) {
176
+ spec.timing = timing;
177
+ } else {
178
+ spec.property = xt_css_lower(token);
179
+ }
180
+ }
181
+ out->push_back(spec);
182
+ }
183
+ }
184
+
185
+ /* -- declaration application ---------------------------------------------- */
186
+
187
+ void xt_style_apply_declaration(XtStyle *style, const std::string &property,
188
+ const std::string &rawValue, const ApplyContext &context) {
189
+ std::string value = xt_css_trim(rawValue);
190
+
191
+ if (property == "display") {
192
+ static const char *const names[] = {"none", "block", "inline", "inline-block", "flex", "list-item"};
193
+ int index = 0;
194
+ if (parseEnum(value, names, 6, &index)) style->display = (Display)index;
195
+ return;
196
+ }
197
+ if (property == "position") {
198
+ static const char *const names[] = {"static", "relative", "absolute", "fixed"};
199
+ int index = 0;
200
+ if (parseEnum(value, names, 4, &index)) style->position = (Position)index;
201
+ return;
202
+ }
203
+ if (property == "width") { assignLength(&style->width, value); return; }
204
+ if (property == "height") { assignLength(&style->height, value); return; }
205
+ if (property == "min-width") { assignLength(&style->min_width, value); return; }
206
+ if (property == "min-height") { assignLength(&style->min_height, value); return; }
207
+ if (property == "max-width") { assignLength(&style->max_width, value); return; }
208
+ if (property == "max-height") { assignLength(&style->max_height, value); return; }
209
+ if (property == "top") { assignLength(&style->top, value); return; }
210
+ if (property == "right") { assignLength(&style->right, value); return; }
211
+ if (property == "bottom") { assignLength(&style->bottom, value); return; }
212
+ if (property == "left") { assignLength(&style->left, value); return; }
213
+
214
+ if (property == "margin") { parseEdgeLengths(value, &style->margin); return; }
215
+ if (property == "padding") { parseEdgeLengths(value, &style->padding); return; }
216
+ if (property == "margin-top") { setEdge(&style->margin, "top", xt_css_parse_length(value)); return; }
217
+ if (property == "margin-right") { setEdge(&style->margin, "right", xt_css_parse_length(value)); return; }
218
+ if (property == "margin-bottom") { setEdge(&style->margin, "bottom", xt_css_parse_length(value)); return; }
219
+ if (property == "margin-left") { setEdge(&style->margin, "left", xt_css_parse_length(value)); return; }
220
+ if (property == "padding-top") { setEdge(&style->padding, "top", xt_css_parse_length(value)); return; }
221
+ if (property == "padding-right") { setEdge(&style->padding, "right", xt_css_parse_length(value)); return; }
222
+ if (property == "padding-bottom") { setEdge(&style->padding, "bottom", xt_css_parse_length(value)); return; }
223
+ if (property == "padding-left") { setEdge(&style->padding, "left", xt_css_parse_length(value)); return; }
224
+
225
+ if (property == "border-width") { parseEdgeLengths(value, &style->border_width); return; }
226
+ if (property == "border-top-width") { setEdge(&style->border_width, "top", xt_css_parse_length(value)); return; }
227
+ if (property == "border-right-width") { setEdge(&style->border_width, "right", xt_css_parse_length(value)); return; }
228
+ if (property == "border-bottom-width") { setEdge(&style->border_width, "bottom", xt_css_parse_length(value)); return; }
229
+ if (property == "border-left-width") { setEdge(&style->border_width, "left", xt_css_parse_length(value)); return; }
230
+ if (property == "border-radius") {
231
+ Length length = xt_css_parse_length(value);
232
+ if (length.unit == Unit::Px || length.unit == Unit::Number) style->border_radius = length.value;
233
+ return;
234
+ }
235
+ if (property == "border-style" || property == "border-top-style" || property == "border-right-style" ||
236
+ property == "border-bottom-style" || property == "border-left-style") {
237
+ static const char *const names[] = {"none", "solid", "dashed", "dotted"};
238
+ int index = 0;
239
+ if (parseEnum(value, names, 4, &index)) style->border_style = (BorderStyle)index;
240
+ return;
241
+ }
242
+ if (property == "border-color" || property == "border-top-color" || property == "border-right-color" ||
243
+ property == "border-bottom-color" || property == "border-left-color") {
244
+ assignColor(&style->border_color, &style->has_border_color, value);
245
+ return;
246
+ }
247
+ if (property == "border" || property == "border-top" || property == "border-right" ||
248
+ property == "border-bottom" || property == "border-left") {
249
+ for (const std::string &token : xt_css_split_whitespace(value)) {
250
+ Length length = xt_css_parse_length(token);
251
+ if (length.unit == Unit::Px || length.unit == Unit::Number) {
252
+ if (property == "border") style->border_width.set(length);
253
+ else setEdge(&style->border_width, property.substr(7), length);
254
+ continue;
255
+ }
256
+ static const char *const names[] = {"none", "solid", "dashed", "dotted"};
257
+ int index = 0;
258
+ if (parseEnum(token, names, 4, &index)) {
259
+ style->border_style = (BorderStyle)index;
260
+ continue;
261
+ }
262
+ Color color = xt_css_parse_color(token);
263
+ if (color.valid) {
264
+ style->border_color = color;
265
+ style->has_border_color = true;
266
+ }
267
+ }
268
+ return;
269
+ }
270
+
271
+ if (property == "color") {
272
+ Color color = xt_css_parse_color(value);
273
+ if (color.valid) style->color = color;
274
+ return;
275
+ }
276
+ if (property == "background-color") { assignColor(&style->background, &style->has_background, value); return; }
277
+ if (property == "background") {
278
+ Color color = xt_css_parse_color(value);
279
+ if (color.valid) {
280
+ style->background = color;
281
+ style->has_background = true;
282
+ }
283
+ return;
284
+ }
285
+
286
+ if (property == "font-size") {
287
+ Length length = xt_css_parse_length(value);
288
+ if (length.unit == Unit::Invalid) return;
289
+ /* `em`/`%` resolve against the *parent* font size, never the element's own
290
+ * (possibly UA-set) size. */
291
+ style->font_size = xt_style_resolve_absolute(length, context.parentFontSize, context.parentFontSize,
292
+ context.rootFontSize, context.viewportWidth, context.viewportHeight);
293
+ if (style->font_size <= 0.0f) style->font_size = 16.0f;
294
+ return;
295
+ }
296
+ if (property == "font-weight") {
297
+ std::string text = xt_css_lower(value);
298
+ if (text == "normal") style->font_weight = 400;
299
+ else if (text == "bold") style->font_weight = 700;
300
+ else if (text == "lighter") style->font_weight = 100;
301
+ else if (text == "bolder") style->font_weight = 700;
302
+ else {
303
+ float number = 0;
304
+ if (xt_css_parse_number(text, &number)) style->font_weight = (int)number;
305
+ }
306
+ return;
307
+ }
308
+ if (property == "font-family") {
309
+ style->font_family = value;
310
+ return;
311
+ }
312
+ if (property == "font-style") {
313
+ style->font_style = (xt_css_lower(value) == "italic") ? FontStyle::Italic : FontStyle::Normal;
314
+ return;
315
+ }
316
+ if (property == "line-height") {
317
+ std::string text = xt_css_lower(value);
318
+ if (text == "normal") {
319
+ style->line_height = {};
320
+ return;
321
+ }
322
+ Length length = xt_css_parse_length(text);
323
+ if (length.unit == Unit::Invalid) return;
324
+ /* Relative units are resolved in `finalizeStyle`, once the element's own
325
+ * font size is settled. */
326
+ style->line_height = length;
327
+ return;
328
+ }
329
+ if (property == "text-align") {
330
+ std::string text = xt_css_lower(value);
331
+ if (text == "left" || text == "start") style->text_align = TextAlign::Start;
332
+ else if (text == "right" || text == "end") style->text_align = TextAlign::End;
333
+ else if (text == "center") style->text_align = TextAlign::Center;
334
+ else if (text == "justify") style->text_align = TextAlign::Justify;
335
+ return;
336
+ }
337
+ if (property == "white-space") {
338
+ static const char *const names[] = {"normal", "nowrap", "pre", "pre-wrap", "pre-line"};
339
+ int index = 0;
340
+ if (parseEnum(value, names, 5, &index)) style->white_space = (WhiteSpace)index;
341
+ return;
342
+ }
343
+
344
+ if (property == "flex-direction") {
345
+ static const char *const names[] = {"row", "row-reverse", "column", "column-reverse"};
346
+ int index = 0;
347
+ if (parseEnum(value, names, 4, &index)) style->flex_direction = (FlexDirection)index;
348
+ return;
349
+ }
350
+ if (property == "justify-content") {
351
+ static const char *const names[] = {"flex-start", "flex-end", "center", "space-between",
352
+ "space-around", "space-evenly"};
353
+ int index = 0;
354
+ if (parseEnum(value, names, 6, &index)) style->justify_content = (JustifyContent)index;
355
+ return;
356
+ }
357
+ if (property == "align-items") {
358
+ static const char *const names[] = {"stretch", "flex-start", "flex-end", "center", "baseline"};
359
+ int index = 0;
360
+ if (parseEnum(value, names, 5, &index)) style->align_items = (AlignItems)index;
361
+ return;
362
+ }
363
+ if (property == "gap" || property == "row-gap" || property == "column-gap") {
364
+ assignLength(&style->gap, value);
365
+ return;
366
+ }
367
+ if (property == "flex-grow") {
368
+ float number = 0;
369
+ if (xt_css_parse_number(value, &number)) style->flex_grow = number;
370
+ return;
371
+ }
372
+ if (property == "flex-shrink") {
373
+ float number = 0;
374
+ if (xt_css_parse_number(value, &number)) style->flex_shrink = number;
375
+ return;
376
+ }
377
+ if (property == "flex-basis") { assignLength(&style->flex_basis, value); return; }
378
+ if (property == "flex") {
379
+ std::string text = xt_css_lower(value);
380
+ if (text == "none") {
381
+ style->flex_grow = 0;
382
+ style->flex_shrink = 0;
383
+ style->flex_basis = {0.0f, Unit::Auto};
384
+ return;
385
+ }
386
+ if (text == "auto") {
387
+ style->flex_grow = 1;
388
+ style->flex_shrink = 1;
389
+ style->flex_basis = {0.0f, Unit::Auto};
390
+ return;
391
+ }
392
+ if (text == "initial") {
393
+ style->flex_grow = 0;
394
+ style->flex_shrink = 1;
395
+ style->flex_basis = {0.0f, Unit::Auto};
396
+ return;
397
+ }
398
+ std::vector<std::string> parts = xt_css_split_whitespace(text);
399
+ style->flex_grow = 0;
400
+ style->flex_shrink = 1;
401
+ style->flex_basis = {0.0f, Unit::Auto};
402
+ size_t index = 0;
403
+ float number = 0;
404
+ if (index < parts.size() && xt_css_parse_number(parts[index], &number)) {
405
+ style->flex_grow = number;
406
+ index++;
407
+ if (index < parts.size() && xt_css_parse_number(parts[index], &number)) {
408
+ style->flex_shrink = number;
409
+ index++;
410
+ }
411
+ if (index >= parts.size()) style->flex_basis = {0.0f, Unit::Px};
412
+ }
413
+ if (index < parts.size()) assignLength(&style->flex_basis, parts[index]);
414
+ return;
415
+ }
416
+
417
+ if (property == "opacity") {
418
+ float number = 0;
419
+ if (xt_css_parse_number(value, &number)) style->opacity = number < 0 ? 0 : (number > 1 ? 1 : number);
420
+ return;
421
+ }
422
+ if (property == "overflow" || property == "overflow-x" || property == "overflow-y") {
423
+ static const char *const names[] = {"visible", "hidden", "scroll", "auto"};
424
+ int index = 0;
425
+ if (parseEnum(value, names, 4, &index)) style->overflow = (Overflow)index;
426
+ return;
427
+ }
428
+ if (property == "z-index") {
429
+ float number = 0;
430
+ if (xt_css_parse_number(value, &number)) {
431
+ style->z_index = (int)number;
432
+ style->has_z_index = true;
433
+ }
434
+ return;
435
+ }
436
+ if (property == "transition") {
437
+ parseTransitionShorthand(value, &style->transitions);
438
+ return;
439
+ }
440
+ if (property == "transition-property") {
441
+ std::vector<std::string> parts = splitCommas(value);
442
+ style->transitions.resize(parts.size());
443
+ for (size_t i = 0; i < parts.size(); i++) style->transitions[i].property = xt_css_lower(parts[i]);
444
+ return;
445
+ }
446
+ if (property == "transition-duration" || property == "transition-delay") {
447
+ std::vector<std::string> parts = splitCommas(value);
448
+ style->transitions.resize(parts.size());
449
+ for (size_t i = 0; i < parts.size(); i++) {
450
+ float seconds = 0;
451
+ if (!parseTimeSeconds(parts[i], &seconds)) continue;
452
+ if (property == "transition-duration") style->transitions[i].duration = seconds;
453
+ else style->transitions[i].delay = seconds;
454
+ }
455
+ return;
456
+ }
457
+ if (property == "transition-timing-function") {
458
+ std::vector<std::string> parts = splitCommas(value);
459
+ style->transitions.resize(parts.size());
460
+ for (size_t i = 0; i < parts.size(); i++) {
461
+ TimingFunction timing = TimingFunction::Ease;
462
+ if (parseTimingFunction(parts[i], &timing)) style->transitions[i].timing = timing;
463
+ }
464
+ return;
465
+ }
466
+ }
467
+
468
+ } // namespace xtgui
@@ -10,6 +10,8 @@
10
10
 
11
11
  #include "gui_engine.h"
12
12
 
13
+ #include <algorithm>
14
+
13
15
  #include "paint.h"
14
16
  #include "text.h"
15
17
 
@@ -48,16 +50,23 @@ static xt_value win_load_html(xt_value self, xt_value env, int32_t argc, xt_valu
48
50
  XtGuiWindow *win = xt_gui_window_from_this(self);
49
51
  if (win != nullptr) {
50
52
  win->html = html;
53
+ /* Fresh document: invalidate every previously handed-out handle. */
54
+ xt_gui_handles_reset(win);
51
55
  win->document = std::make_unique<xtgui::XtDocument>();
52
56
  float width = 0;
53
57
  float height = 0;
54
58
  xt_gui_window_viewport(win, &width, &height);
55
59
  win->document->load(html, width, height);
56
60
  win->geometry.dirty = 1;
61
+ /* Run the AOT-compiled `<script>` bodies now that the tree is parsed, so
62
+ * `document.getElementById(...)` resolves, then settle the first layout. */
63
+ xt_gui_run_scripts(win);
64
+ xt_gui_flush_dom(win);
57
65
  }
58
66
  xt_object_set(self, xt_string_from_cstr("__xt_gui_html"), xt_string_from_cstr(html.c_str()));
59
67
  /* Painting the document is milestone M3b+; the parsed tree and computed
60
68
  * styles are available now (see `computedStyle`). */
69
+ xt_gui_emit(win, "DOMContentLoaded");
61
70
  xt_gui_emit(win, "load");
62
71
  return XT_UNDEFINED;
63
72
  }
@@ -69,12 +78,21 @@ static xt_value win_get_html(xt_value self, xt_value env, int32_t argc, xt_value
69
78
  return xt_object_get_cstr(self, "__xt_gui_html");
70
79
  }
71
80
 
81
+ /** The `document` handle for this window (created on first access). */
82
+ static xt_value win_document_get(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
83
+ (void)env;
84
+ (void)argc;
85
+ (void)argv;
86
+ return xt_gui_document_handle(xt_gui_window_from_this(self));
87
+ }
88
+
72
89
  /** Diagnostic/test hook: computed value of `property` for the first match of
73
90
  * `selector`, or an empty string. */
74
91
  static xt_value win_computed_style(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
75
92
  (void)env;
76
93
  XtGuiWindow *win = xt_gui_window_from_this(self);
77
94
  if (win == nullptr || win->document == nullptr || argc < 2) return xt_string_from_cstr("");
95
+ xt_gui_flush_dom(win);
78
96
  xt_value selectorValue = xt_to_string(xt_arg(argc, argv, 0));
79
97
  xt_value propertyValue = xt_to_string(xt_arg(argc, argv, 1));
80
98
  const char *selector = xt_string_data(selectorValue);
@@ -95,6 +113,7 @@ static xt_value win_document_tree(xt_value self, xt_value env, int32_t argc, xt_
95
113
  (void)argv;
96
114
  XtGuiWindow *win = xt_gui_window_from_this(self);
97
115
  if (win == nullptr || win->document == nullptr) return xt_string_from_cstr("");
116
+ xt_gui_flush_dom(win);
98
117
  std::string text = win->document->toDebugString();
99
118
  return xt_string_from_cstr(text.c_str());
100
119
  }
@@ -104,6 +123,7 @@ static xt_value win_query_count(xt_value self, xt_value env, int32_t argc, xt_va
104
123
  (void)env;
105
124
  XtGuiWindow *win = xt_gui_window_from_this(self);
106
125
  if (win == nullptr || win->document == nullptr || argc < 1) return xt_number(0);
126
+ xt_gui_flush_dom(win);
107
127
  xt_value selectorValue = xt_to_string(xt_arg(argc, argv, 0));
108
128
  const char *selector = xt_string_data(selectorValue);
109
129
  if (selector == nullptr) return xt_number(0);
@@ -116,6 +136,7 @@ static xt_value win_get_bounding_rect(xt_value self, xt_value env, int32_t argc,
116
136
  (void)env;
117
137
  XtGuiWindow *win = xt_gui_window_from_this(self);
118
138
  if (win == nullptr || win->document == nullptr || argc < 1) return XT_UNDEFINED;
139
+ xt_gui_flush_dom(win);
119
140
  xt_value selectorValue = xt_to_string(xt_arg(argc, argv, 0));
120
141
  const char *selector = xt_string_data(selectorValue);
121
142
  if (selector == nullptr) return XT_UNDEFINED;
@@ -138,6 +159,7 @@ static xt_value win_layout_tree(xt_value self, xt_value env, int32_t argc, xt_va
138
159
  (void)argv;
139
160
  XtGuiWindow *win = xt_gui_window_from_this(self);
140
161
  if (win == nullptr || win->document == nullptr) return xt_string_from_cstr("");
162
+ xt_gui_flush_dom(win);
141
163
  std::string text = win->document->layout().dump();
142
164
  return xt_string_from_cstr(text.c_str());
143
165
  }
@@ -169,6 +191,7 @@ static xt_value win_paint_list(xt_value self, xt_value env, int32_t argc, xt_val
169
191
  (void)argv;
170
192
  XtGuiWindow *win = xt_gui_window_from_this(self);
171
193
  if (win == nullptr || win->document == nullptr) return xt_string_from_cstr("");
194
+ xt_gui_flush_dom(win);
172
195
  xtgui::DisplayList list;
173
196
  xtgui::xt_paint_build(win->document->layout().root(), list);
174
197
  std::string text = list.dump();
@@ -182,6 +205,7 @@ static xt_value win_paint_count(xt_value self, xt_value env, int32_t argc, xt_va
182
205
  (void)argv;
183
206
  XtGuiWindow *win = xt_gui_window_from_this(self);
184
207
  if (win == nullptr || win->document == nullptr) return xt_number(0);
208
+ xt_gui_flush_dom(win);
185
209
  xtgui::DisplayList list;
186
210
  xtgui::xt_paint_build(win->document->layout().root(), list);
187
211
  return xt_number((double)list.rects.size());
@@ -259,6 +283,34 @@ static xt_value win_is_open(xt_value self, xt_value env, int32_t argc, xt_value
259
283
  return (win != NULL && win->open) ? XT_TRUE : XT_FALSE;
260
284
  }
261
285
 
286
+ /** `requestAnimationFrame(fn)` -> id. The callback runs once on the next frame
287
+ * with the frame timestamp (ms) as its only argument. */
288
+ static xt_value win_request_animation_frame(xt_value self, xt_value env, int32_t argc,
289
+ xt_value *argv) {
290
+ (void)env;
291
+ XtGuiWindow *win = xt_gui_window_from_this(self);
292
+ if (win == NULL || !win->open) return xt_number(0);
293
+ xt_value fn = xt_arg(argc, argv, 0);
294
+ if (!XT_IS_FUNCTION(fn)) return xt_number(0);
295
+ int id = win->next_animation_frame_id++;
296
+ win->animation_frames.push_back(XtGuiAnimationFrame{id, fn});
297
+ return xt_number((double)id);
298
+ }
299
+
300
+ /** `cancelAnimationFrame(id)` drops a callback queued for the next frame. */
301
+ static xt_value win_cancel_animation_frame(xt_value self, xt_value env, int32_t argc,
302
+ xt_value *argv) {
303
+ (void)env;
304
+ XtGuiWindow *win = xt_gui_window_from_this(self);
305
+ if (win == NULL || argc < 1) return XT_UNDEFINED;
306
+ int id = (int)xt_to_number(xt_arg(argc, argv, 0));
307
+ std::vector<XtGuiAnimationFrame> &frames = win->animation_frames;
308
+ frames.erase(std::remove_if(frames.begin(), frames.end(),
309
+ [id](const XtGuiAnimationFrame &frame) { return frame.id == id; }),
310
+ frames.end());
311
+ return XT_UNDEFINED;
312
+ }
313
+
262
314
  static xt_value win_on(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
263
315
  (void)env;
264
316
  if (argc >= 2) {
@@ -314,6 +366,7 @@ static xt_value win_hit_test(xt_value self, xt_value env, int32_t argc, xt_value
314
366
  (void)env;
315
367
  XtGuiWindow *win = xt_gui_window_from_this(self);
316
368
  if (win == nullptr || win->document == nullptr || argc < 2) return xt_string_from_cstr("");
369
+ xt_gui_flush_dom(win);
317
370
  float x = (float)xt_to_number(xt_arg(argc, argv, 0));
318
371
  float y = (float)xt_to_number(xt_arg(argc, argv, 1));
319
372
  std::string target = xt_gui_hit_test(win, x, y);
@@ -376,10 +429,15 @@ static void define_method(xt_value proto, const char *name, void *fn) {
376
429
  xt_object_set(proto, xt_string_from_cstr(name), xt_closure_new(fn, 0, NULL));
377
430
  }
378
431
 
432
+ static void define_getter(xt_value proto, const char *name, void *fn) {
433
+ xt_object_define_getter(proto, xt_string_from_cstr(name), xt_closure_new(fn, 0, NULL));
434
+ }
435
+
379
436
  xt_value xt_gui_window_proto(void) {
380
437
  if (XT_IS_OBJECT(g_window_proto)) return g_window_proto;
381
438
  xt_value proto = xt_object_new();
382
439
  define_method(proto, "setTitle", (void *)win_set_title);
440
+ define_getter(proto, "document", (void *)win_document_get);
383
441
  define_method(proto, "setSize", (void *)win_set_size);
384
442
  define_method(proto, "loadHTML", (void *)win_load_html);
385
443
  define_method(proto, "getHTML", (void *)win_get_html);
@@ -396,6 +454,8 @@ xt_value xt_gui_window_proto(void) {
396
454
  define_method(proto, "fontMetrics", (void *)win_font_metrics);
397
455
  define_method(proto, "close", (void *)win_close);
398
456
  define_method(proto, "isOpen", (void *)win_is_open);
457
+ define_method(proto, "requestAnimationFrame", (void *)win_request_animation_frame);
458
+ define_method(proto, "cancelAnimationFrame", (void *)win_cancel_animation_frame);
399
459
  define_method(proto, "on", (void *)win_on);
400
460
  define_method(proto, "off", (void *)win_off);
401
461
  define_method(proto, "hitTest", (void *)win_hit_test);
package/runtime/rt.h CHANGED
@@ -218,6 +218,12 @@ xt_value xt_crypto_create_hash(int32_t argc, xt_value *argv);
218
218
  /* -- node:child_process --------------------------------------------------- */
219
219
  xt_value xt_child_process_spawn_sync(int32_t argc, xt_value *argv);
220
220
 
221
+ /* -- fetch ---------------------------------------------------------------- */
222
+ /** Implements the global `fetch(input, init)`; returns a settled promise. */
223
+ xt_value xt_fetch(int32_t argc, xt_value *argv);
224
+ /** Construct a `TypeError` with one message argument (used by `fetch`). */
225
+ xt_value xt_type_error_ctor(int32_t argc, xt_value *argv);
226
+
221
227
  /* -- promises / async ----------------------------------------------------- */
222
228
  xt_value xt_promise_resolve(xt_value value);
223
229
  xt_value xt_promise_reject(xt_value value);
@@ -286,4 +286,8 @@ int xt_value_equals(xt_value a, xt_value b);
286
286
  xt_array *xt_as_array(xt_value value);
287
287
  xt_value xt_arg_at(int32_t argc, xt_value *argv, int32_t index);
288
288
 
289
+ /* Typed arrays (xt_typed_array.c). */
290
+ int xt_typed_array_kind_of(xt_value value);
291
+ double xt_typed_array_coerce(int kind, double value);
292
+
289
293
  #endif /* XT_RT_INTERNAL_H */
@@ -305,6 +305,7 @@ XT_BUILTIN_TRAMPOLINE(xt_builtin_value_setTimeout, xt_set_timeout)
305
305
  XT_BUILTIN_TRAMPOLINE(xt_builtin_value_clearTimeout, xt_clear_timeout)
306
306
  XT_BUILTIN_TRAMPOLINE(xt_builtin_value_setInterval, xt_set_interval)
307
307
  XT_BUILTIN_TRAMPOLINE(xt_builtin_value_clearInterval, xt_clear_interval)
308
+ XT_BUILTIN_TRAMPOLINE(xt_builtin_value_fetch, xt_fetch)
308
309
 
309
310
  xt_value xt_in(xt_value key, xt_value value) {
310
311
  if (XT_IS_OBJECT(value)) return xt_object_has(value, key);