xbintsc 0.5.36 → 0.5.37

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 (66) hide show
  1. package/dist/src/cli/help.d.ts +1 -1
  2. package/dist/src/driver/version.d.ts +1 -1
  3. package/dist/src/driver/version.js +1 -1
  4. package/dist/src/extensions/gui/index.js +12 -0
  5. package/dist/src/extensions/gui/index.js.map +1 -1
  6. package/dist/tests/e2e/gui-browser.test.js +65 -0
  7. package/dist/tests/e2e/gui-browser.test.js.map +1 -1
  8. package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
  9. package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
  10. package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
  11. package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
  12. package/dist/tests/e2e/gui-dom.test.js +215 -0
  13. package/dist/tests/e2e/gui-dom.test.js.map +1 -1
  14. package/dist/tests/e2e/gui-events.test.js +112 -0
  15. package/dist/tests/e2e/gui-events.test.js.map +1 -1
  16. package/doc/gui/api.md +38 -9
  17. package/doc/gui/canvas.md +49 -18
  18. package/doc/gui/dom.md +51 -4
  19. package/doc/zh-CN/gui/api.md +33 -4
  20. package/doc/zh-CN/gui/canvas.md +44 -19
  21. package/doc/zh-CN/gui/dom.md +45 -3
  22. package/package.json +7 -7
  23. package/runtime/ext_gui/browser.cpp +34 -49
  24. package/runtime/ext_gui/browser_abort.cpp +249 -0
  25. package/runtime/ext_gui/browser_dialogs.cpp +76 -0
  26. package/runtime/ext_gui/browser_media.cpp +176 -0
  27. package/runtime/ext_gui/browser_navigation.cpp +130 -0
  28. package/runtime/ext_gui/canvas.cpp +88 -30
  29. package/runtime/ext_gui/canvas.h +74 -9
  30. package/runtime/ext_gui/canvas_image_data.cpp +143 -0
  31. package/runtime/ext_gui/canvas_internal.h +8 -5
  32. package/runtime/ext_gui/canvas_pattern.cpp +90 -0
  33. package/runtime/ext_gui/canvas_raster.cpp +31 -9
  34. package/runtime/ext_gui/canvas_style.cpp +4 -2
  35. package/runtime/ext_gui/dom.h +4 -0
  36. package/runtime/ext_gui/dom_api.cpp +1 -0
  37. package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
  38. package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
  39. package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
  40. package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
  41. package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
  42. package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
  43. package/runtime/ext_gui/dom_api_document.cpp +58 -5
  44. package/runtime/ext_gui/dom_api_events.cpp +93 -17
  45. package/runtime/ext_gui/dom_api_internal.h +55 -2
  46. package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
  47. package/runtime/ext_gui/dom_api_node.cpp +40 -19
  48. package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
  49. package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
  50. package/runtime/ext_gui/dom_api_proto.cpp +35 -0
  51. package/runtime/ext_gui/dom_api_style.cpp +223 -13
  52. package/runtime/ext_gui/gui.cpp +59 -5
  53. package/runtime/ext_gui/gui_engine.h +47 -0
  54. package/runtime/ext_gui/gui_events.cpp +80 -9
  55. package/runtime/ext_gui/listener_options.h +43 -0
  56. package/runtime/ext_gui/paint.cpp +28 -0
  57. package/runtime/ext_gui/paint.h +4 -0
  58. package/runtime/ext_gui/window.cpp +14 -65
  59. package/runtime/ext_gui/window_events.cpp +125 -0
  60. package/runtime/rt.h +26 -0
  61. package/runtime/rt_internal.h +4 -0
  62. package/runtime/xt_builtins.c +0 -21
  63. package/runtime/xt_containers/access.inc +94 -2
  64. package/runtime/xt_containers/objects.inc +29 -31
  65. package/src/driver/version.ts +1 -1
  66. package/src/extensions/gui/index.ts +12 -0
@@ -0,0 +1,239 @@
1
+ /*
2
+ * xbintsc GUI engine — the Canvas2D image APIs (`putImageData`,
3
+ * `createImageData`, `createPattern`, `toDataURL`, `toBlob`).
4
+ *
5
+ * `ImageData` is the same `{ width, height, data }` shape `getImageData` already
6
+ * returns (a byte array), patterns are canvas-owned handles like gradients, and
7
+ * `toDataURL`/`toBlob` export the bitmap as a PNG data URL / callback result.
8
+ */
9
+
10
+ #include "dom_api_internal.h"
11
+
12
+ #include <cmath>
13
+ #include <string>
14
+ #include <vector>
15
+
16
+ #include "canvas.h"
17
+ #include "image.h"
18
+
19
+ namespace domapi {
20
+
21
+ namespace {
22
+
23
+ #define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
24
+
25
+ /** Read an `ImageData`-shaped value into width/height/RGBA bytes. */
26
+ bool image_data_arg(xt_value value, int *width, int *height, std::vector<unsigned char> *bytes) {
27
+ if (!XT_IS_OBJECT(value)) return false;
28
+ xt_value w = xt_object_get_cstr(value, "width");
29
+ xt_value h = xt_object_get_cstr(value, "height");
30
+ if (!XT_IS_NUMBER(w) || !XT_IS_NUMBER(h)) return false;
31
+ *width = (int)xt_to_number(w);
32
+ *height = (int)xt_to_number(h);
33
+ if (*width <= 0 || *height <= 0) return false;
34
+ xt_value data = xt_object_get_cstr(value, "data");
35
+ if (!XT_IS_ARRAY(data)) return false;
36
+ int32_t count = (int32_t)xt_to_number(xt_array_length(data));
37
+ if (count < *width * *height * 4) return false;
38
+ bytes->resize((size_t)count);
39
+ for (int32_t i = 0; i < count; i++) {
40
+ (*bytes)[(size_t)i] = (unsigned char)xt_to_number(xt_array_get(data, xt_number((double)i)));
41
+ }
42
+ return true;
43
+ }
44
+
45
+ xt_value make_image_data(int width, int height, const std::vector<unsigned char> &bytes) {
46
+ xt_value result = xt_object_new();
47
+ xt_value data = xt_array_new(0, nullptr);
48
+ for (unsigned char byte : bytes) xt_array_push(data, xt_number((double)byte));
49
+ xt_object_set(result, xt_string_from_cstr("width"), xt_number((double)width));
50
+ xt_object_set(result, xt_string_from_cstr("height"), xt_number((double)height));
51
+ xt_object_set(result, xt_string_from_cstr("data"), data);
52
+ return result;
53
+ }
54
+
55
+ /** Decode a `createPattern` source: an `<img>` or another `<canvas>`. */
56
+ bool source_image(XtGuiWindow *win, xt_value value, xtgui::Image *out) {
57
+ xtgui::Node *node = resolve_node(value, &win);
58
+ if (node == nullptr || !node->isElement()) return false;
59
+ if (node->isTag("img")) {
60
+ const std::string *src = node->attr("src");
61
+ if (src == nullptr) return false;
62
+ const xtgui::Image *image = xtgui::xt_image_load(*src);
63
+ if (image == nullptr || image->width <= 0 || image->height <= 0) return false;
64
+ *out = *image;
65
+ return true;
66
+ }
67
+ xtgui::Canvas2D *canvas = xt_gui_canvas_find(win, node);
68
+ if (canvas == nullptr) return false;
69
+ *out = canvas->image();
70
+ return out->width > 0 && out->height > 0;
71
+ }
72
+
73
+ const char kBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
74
+
75
+ std::string base64_encode(const std::vector<unsigned char> &bytes) {
76
+ std::string out;
77
+ out.reserve((bytes.size() + 2) / 3 * 4);
78
+ size_t i = 0;
79
+ while (i + 2 < bytes.size()) {
80
+ uint32_t triple = ((uint32_t)bytes[i] << 16) | ((uint32_t)bytes[i + 1] << 8) | bytes[i + 2];
81
+ out.push_back(kBase64[(triple >> 18) & 0x3F]);
82
+ out.push_back(kBase64[(triple >> 12) & 0x3F]);
83
+ out.push_back(kBase64[(triple >> 6) & 0x3F]);
84
+ out.push_back(kBase64[triple & 0x3F]);
85
+ i += 3;
86
+ }
87
+ if (i < bytes.size()) {
88
+ uint32_t triple = (uint32_t)bytes[i] << 16;
89
+ bool two = i + 1 < bytes.size();
90
+ if (two) triple |= (uint32_t)bytes[i + 1] << 8;
91
+ out.push_back(kBase64[(triple >> 18) & 0x3F]);
92
+ out.push_back(kBase64[(triple >> 12) & 0x3F]);
93
+ out.push_back(two ? kBase64[(triple >> 6) & 0x3F] : '=');
94
+ out.push_back('=');
95
+ }
96
+ return out;
97
+ }
98
+
99
+ xt_value blob_callback(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
100
+ (void)self;
101
+ (void)argc;
102
+ (void)argv;
103
+ xt_value fn = xt_closure_env(env, 0);
104
+ xt_value blob = xt_closure_env(env, 1);
105
+ xt_value args[1] = {blob};
106
+ return xt_call_with_this(fn, XT_UNDEFINED, 1, args);
107
+ }
108
+
109
+ } // namespace
110
+
111
+ /** The canvas behind a context handle or a `<canvas>` element handle. */
112
+ xtgui::Canvas2D *canvas_or_element(xt_value self) {
113
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
114
+ if (canvas != nullptr) return canvas;
115
+ XtGuiWindow *win = nullptr;
116
+ xtgui::Node *node = resolve_node(self, &win);
117
+ if (node == nullptr || win == nullptr) return nullptr;
118
+ return xt_gui_canvas_find(win, node);
119
+ }
120
+
121
+ xt_value canvas_pattern_handle(xt_value context, const xtgui::Canvas2D *canvas, uint64_t id) {
122
+ const std::string key = "__xt_gui_pattern_" + std::to_string(id);
123
+ xt_value cached = xt_object_get_cstr(context, key.c_str());
124
+ if (XT_IS_OBJECT(cached)) return cached;
125
+ xt_value handle = xt_object_new_with_proto(xt_object_prototype());
126
+ if (canvas != nullptr) {
127
+ xt_object_set(handle, xt_string_from_cstr("__xt_gui_canvas"), xt_number((double)canvas->id()));
128
+ }
129
+ xt_object_set(handle, xt_string_from_cstr("__xt_gui_pattern"), xt_number((double)id));
130
+ xt_object_set(context, xt_string_from_cstr(key.c_str()), handle);
131
+ return handle;
132
+ }
133
+
134
+ uint64_t canvas_pattern_id(xt_value handle, const xtgui::Canvas2D *canvas) {
135
+ if (canvas == nullptr || !XT_IS_OBJECT(handle)) return 0;
136
+ xt_value canvasId = xt_object_get_cstr(handle, "__xt_gui_canvas");
137
+ xt_value patternId = xt_object_get_cstr(handle, "__xt_gui_pattern");
138
+ if (!XT_IS_NUMBER(canvasId) || !XT_IS_NUMBER(patternId)) return 0;
139
+ if ((uint64_t)xt_to_number(canvasId) != canvas->id()) return 0;
140
+ uint64_t id = (uint64_t)xt_to_number(patternId);
141
+ return canvas->find_pattern(id) == nullptr ? 0 : id;
142
+ }
143
+
144
+ METHOD(canvas_put_image_data) {
145
+ (void)env;
146
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
147
+ if (canvas == nullptr || argc < 3) return XT_UNDEFINED;
148
+ int width = 0;
149
+ int height = 0;
150
+ std::vector<unsigned char> bytes;
151
+ if (!image_data_arg(xt_arg(argc, argv, 0), &width, &height, &bytes)) return XT_UNDEFINED;
152
+ int dx = (int)canvas_number_arg(argc, argv, 1, 0.0);
153
+ int dy = (int)canvas_number_arg(argc, argv, 2, 0.0);
154
+ int dirty_x = (int)canvas_number_arg(argc, argv, 3, 0.0);
155
+ int dirty_y = (int)canvas_number_arg(argc, argv, 4, 0.0);
156
+ int dirty_width = (int)canvas_number_arg(argc, argv, 5, 0.0);
157
+ int dirty_height = (int)canvas_number_arg(argc, argv, 6, 0.0);
158
+ canvas->set_image_data(bytes, width, height, dx, dy, dirty_x, dirty_y, dirty_width,
159
+ dirty_height);
160
+ return XT_UNDEFINED;
161
+ }
162
+
163
+ METHOD(canvas_create_image_data) {
164
+ (void)env;
165
+ int width = 0;
166
+ int height = 0;
167
+ if (argc >= 2) {
168
+ width = (int)canvas_number_arg(argc, argv, 0, 0.0);
169
+ height = (int)canvas_number_arg(argc, argv, 1, 0.0);
170
+ } else if (argc >= 1) {
171
+ int source_width = 0;
172
+ int source_height = 0;
173
+ std::vector<unsigned char> bytes;
174
+ if (image_data_arg(xt_arg(argc, argv, 0), &source_width, &source_height, &bytes)) {
175
+ width = source_width;
176
+ height = source_height;
177
+ }
178
+ }
179
+ if (width <= 0 || height <= 0) return XT_UNDEFINED;
180
+ return make_image_data(width, height, std::vector<unsigned char>((size_t)width * height * 4, 0));
181
+ }
182
+
183
+ METHOD(canvas_create_pattern) {
184
+ (void)env;
185
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
186
+ if (canvas == nullptr || argc < 1) return XT_NULL;
187
+ XtGuiWindow *win = nullptr;
188
+ resolve_node(xt_object_get_cstr(self, "__xt_gui_node"), &win);
189
+ xtgui::Image source;
190
+ if (win == nullptr || !source_image(win, xt_arg(argc, argv, 0), &source)) return XT_NULL;
191
+ std::string repetition = argc >= 2 ? canvas_string_arg(argc, argv, 1) : "repeat";
192
+ xtgui::CanvasPattern::Repeat repeat = xtgui::CanvasPattern::Repeat::Both;
193
+ if (repetition == "repeat-x") repeat = xtgui::CanvasPattern::Repeat::X;
194
+ else if (repetition == "repeat-y") repeat = xtgui::CanvasPattern::Repeat::Y;
195
+ else if (repetition == "no-repeat") repeat = xtgui::CanvasPattern::Repeat::None;
196
+ xtgui::CanvasPattern *pattern = canvas->create_pattern(source, repeat);
197
+ return canvas_pattern_handle(self, canvas, pattern->id());
198
+ }
199
+
200
+ /** `toDataURL(type?)`: a PNG data URL (the engine has one encoder; a browser
201
+ * also falls back to PNG for an unsupported type). */
202
+ METHOD(canvas_to_data_url) {
203
+ (void)env;
204
+ (void)argc;
205
+ (void)argv;
206
+ xtgui::Canvas2D *canvas = canvas_or_element(self);
207
+ if (canvas == nullptr) return xt_string_from_cstr("data:,");
208
+ std::vector<unsigned char> png;
209
+ if (!canvas->encode_png(&png)) return xt_string_from_cstr("data:,");
210
+ std::string url = "data:image/png;base64," + base64_encode(png);
211
+ return xt_string_from_cstr(url.c_str());
212
+ }
213
+
214
+ /** `toBlob(callback, type?)`: build a Blob-shaped result and deliver it from a
215
+ * microtask, as the browser does. The bytes are on `__xt_gui_blob_data`; there
216
+ * is no `URL.createObjectURL` in this runtime. */
217
+ METHOD(canvas_to_blob) {
218
+ (void)env;
219
+ xtgui::Canvas2D *canvas = canvas_or_element(self);
220
+ xt_value callback = argc >= 1 ? xt_arg(argc, argv, 0) : XT_UNDEFINED;
221
+ if (canvas == nullptr || !XT_IS_FUNCTION(callback)) return XT_UNDEFINED;
222
+ std::vector<unsigned char> png;
223
+ if (!canvas->encode_png(&png)) return XT_UNDEFINED;
224
+ xt_value data = xt_array_new(0, nullptr);
225
+ for (unsigned char byte : png) xt_array_push(data, xt_number((double)byte));
226
+ xt_value blob = xt_object_new();
227
+ xt_object_set(blob, xt_string_from_cstr("type"), xt_string_from_cstr("image/png"));
228
+ xt_object_set(blob, xt_string_from_cstr("size"), xt_number((double)png.size()));
229
+ xt_object_set(blob, xt_string_from_cstr("__xt_gui_blob_data"), data);
230
+ xt_value captured[2] = {callback, blob};
231
+ xt_value task = xt_closure_new((void *)blob_callback, 2, captured);
232
+ xt_value args[1] = {task};
233
+ xt_queue_microtask(1, args);
234
+ return XT_UNDEFINED;
235
+ }
236
+
237
+ #undef METHOD
238
+
239
+ } // namespace domapi
@@ -0,0 +1,179 @@
1
+ /*
2
+ * xbintsc GUI engine — the Canvas2D path and fill/stroke bindings.
3
+ *
4
+ * `beginPath`/`moveTo`/… build the context's current path; `fill`/`stroke`
5
+ * paint it (or a `Path2D` supplied as the argument), with the optional
6
+ * `"evenodd"` fill rule honoured.
7
+ */
8
+
9
+ #include "dom_api_internal.h"
10
+
11
+ #include <string>
12
+ #include <vector>
13
+
14
+ #include "canvas.h"
15
+
16
+ #define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
17
+
18
+ namespace domapi {
19
+ /* -- paths ---------------------------------------------------------------- */
20
+
21
+ namespace {
22
+
23
+ /** One argument of the fill/stroke family: a `Path2D` handle or a fill rule. */
24
+ bool canvas_fill_rule(xt_value value, bool *even_odd) {
25
+ if (!XT_IS_STRING(value)) return false;
26
+ std::string rule = to_string_value(value);
27
+ if (rule == "evenodd") {
28
+ *even_odd = true;
29
+ return true;
30
+ }
31
+ if (rule == "nonzero") {
32
+ *even_odd = false;
33
+ return true;
34
+ }
35
+ return false;
36
+ }
37
+
38
+ /** Collect the `Path2D` arguments of `fill`/`stroke` (the DOM accepts one path
39
+ * plus an optional fill rule, in either order). */
40
+ std::vector<std::vector<xtgui::CanvasSubpath>> canvas_path_args(int32_t argc, xt_value *argv,
41
+ bool *even_odd, bool *has_path) {
42
+ std::vector<std::vector<xtgui::CanvasSubpath>> paths;
43
+ *even_odd = false;
44
+ *has_path = false;
45
+ for (int32_t i = 0; i < argc; i++) {
46
+ xt_value value = xt_arg(argc, argv, i);
47
+ bool rule = false;
48
+ if (canvas_fill_rule(value, &rule)) {
49
+ *even_odd = rule;
50
+ continue;
51
+ }
52
+ if (path2d_is(value)) {
53
+ paths.push_back(path2d_subpaths(value));
54
+ *has_path = true;
55
+ }
56
+ }
57
+ return paths;
58
+ }
59
+
60
+ } // namespace
61
+
62
+ METHOD(canvas_begin_path) {
63
+ (void)env;
64
+ (void)argc;
65
+ (void)argv;
66
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
67
+ if (canvas != nullptr) canvas->begin_path();
68
+ return XT_UNDEFINED;
69
+ }
70
+
71
+ METHOD(canvas_close_path) {
72
+ (void)env;
73
+ (void)argc;
74
+ (void)argv;
75
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
76
+ if (canvas != nullptr) canvas->close_path();
77
+ return XT_UNDEFINED;
78
+ }
79
+
80
+ METHOD(canvas_move_to) {
81
+ (void)env;
82
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
83
+ if (canvas != nullptr)
84
+ canvas->move_to((float)canvas_number_arg(argc, argv, 0, 0.0), (float)canvas_number_arg(argc, argv, 1, 0.0));
85
+ return XT_UNDEFINED;
86
+ }
87
+
88
+ METHOD(canvas_line_to) {
89
+ (void)env;
90
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
91
+ if (canvas != nullptr)
92
+ canvas->line_to((float)canvas_number_arg(argc, argv, 0, 0.0), (float)canvas_number_arg(argc, argv, 1, 0.0));
93
+ return XT_UNDEFINED;
94
+ }
95
+
96
+ METHOD(canvas_rect) {
97
+ (void)env;
98
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
99
+ if (canvas != nullptr)
100
+ canvas->rect((float)canvas_number_arg(argc, argv, 0, 0.0), (float)canvas_number_arg(argc, argv, 1, 0.0),
101
+ (float)canvas_number_arg(argc, argv, 2, 0.0), (float)canvas_number_arg(argc, argv, 3, 0.0));
102
+ return XT_UNDEFINED;
103
+ }
104
+
105
+ METHOD(canvas_arc) {
106
+ (void)env;
107
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
108
+ if (canvas != nullptr) {
109
+ canvas->arc((float)canvas_number_arg(argc, argv, 0, 0.0), (float)canvas_number_arg(argc, argv, 1, 0.0),
110
+ (float)canvas_number_arg(argc, argv, 2, 0.0), (float)canvas_number_arg(argc, argv, 3, 0.0),
111
+ (float)canvas_number_arg(argc, argv, 4, 0.0), value_truthy(xt_arg(argc, argv, 5)));
112
+ }
113
+ return XT_UNDEFINED;
114
+ }
115
+
116
+ METHOD(canvas_quadratic_curve_to) {
117
+ (void)env;
118
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
119
+ if (canvas != nullptr)
120
+ canvas->quadratic_curve_to((float)canvas_number_arg(argc, argv, 0, 0.0),
121
+ (float)canvas_number_arg(argc, argv, 1, 0.0),
122
+ (float)canvas_number_arg(argc, argv, 2, 0.0),
123
+ (float)canvas_number_arg(argc, argv, 3, 0.0));
124
+ return XT_UNDEFINED;
125
+ }
126
+
127
+ METHOD(canvas_bezier_curve_to) {
128
+ (void)env;
129
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
130
+ if (canvas != nullptr)
131
+ canvas->bezier_curve_to((float)canvas_number_arg(argc, argv, 0, 0.0),
132
+ (float)canvas_number_arg(argc, argv, 1, 0.0),
133
+ (float)canvas_number_arg(argc, argv, 2, 0.0),
134
+ (float)canvas_number_arg(argc, argv, 3, 0.0),
135
+ (float)canvas_number_arg(argc, argv, 4, 0.0),
136
+ (float)canvas_number_arg(argc, argv, 5, 0.0));
137
+ return XT_UNDEFINED;
138
+ }
139
+
140
+ METHOD(canvas_fill) {
141
+ (void)env;
142
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
143
+ if (canvas == nullptr) return XT_UNDEFINED;
144
+ bool even_odd = false;
145
+ bool has_path = false;
146
+ std::vector<std::vector<xtgui::CanvasSubpath>> paths =
147
+ canvas_path_args(argc, argv, &even_odd, &has_path);
148
+ if (has_path) {
149
+ for (const std::vector<xtgui::CanvasSubpath> &path : paths) {
150
+ canvas->fill_paths(path, even_odd);
151
+ }
152
+ } else {
153
+ canvas->fill(even_odd);
154
+ }
155
+ return XT_UNDEFINED;
156
+ }
157
+
158
+ METHOD(canvas_stroke) {
159
+ (void)env;
160
+ xtgui::Canvas2D *canvas = canvas_from_context(self);
161
+ if (canvas == nullptr) return XT_UNDEFINED;
162
+ bool even_odd = false;
163
+ bool has_path = false;
164
+ std::vector<std::vector<xtgui::CanvasSubpath>> paths =
165
+ canvas_path_args(argc, argv, &even_odd, &has_path);
166
+ if (has_path) {
167
+ for (const std::vector<xtgui::CanvasSubpath> &path : paths) {
168
+ canvas->stroke_paths(path);
169
+ }
170
+ } else {
171
+ canvas->stroke();
172
+ }
173
+ return XT_UNDEFINED;
174
+ }
175
+
176
+
177
+ #undef METHOD
178
+
179
+ } // namespace domapi
@@ -0,0 +1,124 @@
1
+ /*
2
+ * xbintsc GUI engine — `getComputedStyle(element, pseudoElt)`.
3
+ *
4
+ * The handle reads through the *cascade* (`XtDocument::styleOf`), not the
5
+ * element's inline `style` object, and exposes the result both as named
6
+ * properties (`cs.width`, via the runtime's dynamic-property hooks) and through
7
+ * `getPropertyValue`/`getPropertyPriority`. Each read flushes a pending restyle
8
+ * first, so the handle stays correct after later DOM mutations, and
9
+ * `::before`/`::after` resolve the current generated-content node instead of a
10
+ * stale pointer.
11
+ */
12
+
13
+ #include "dom_api_internal.h"
14
+
15
+ #include "css.h"
16
+ #include "style.h"
17
+
18
+ namespace domapi {
19
+
20
+ namespace {
21
+
22
+ /** Which pseudo-element the handle addresses. `kPseudoUnknown` is a pseudo the
23
+ * engine never generates (`::marker`, `::selection`, …): it keeps answering
24
+ * empty values instead of falling back to the parent element's style. */
25
+ enum ComputedPseudo { kPseudoNone = 0, kPseudoBefore = 1, kPseudoAfter = 2, kPseudoUnknown = 3 };
26
+
27
+ int pseudo_from_argument(xt_value value) {
28
+ if (!XT_IS_STRING(value)) return kPseudoNone;
29
+ std::string text = xtgui::xt_css_lower(xtgui::xt_css_trim(to_string_value(value)));
30
+ if (text.empty()) return kPseudoNone;
31
+ if (text == "::before" || text == ":before") return kPseudoBefore;
32
+ if (text == "::after" || text == ":after") return kPseudoAfter;
33
+ return kPseudoUnknown;
34
+ }
35
+
36
+ const xtgui::XtStyle *computed_style_of(xt_value self) {
37
+ xt_value element = xt_object_get_cstr(self, "__xt_gui_element");
38
+ XtGuiWindow *win = nullptr;
39
+ xtgui::Node *node = resolve_node(element, &win);
40
+ if (node == nullptr || win == nullptr || win->document == nullptr) return nullptr;
41
+ xt_gui_flush_dom(win);
42
+ xt_value sideValue = xt_object_get_cstr(self, "__xt_gui_pseudo");
43
+ int side = XT_IS_NUMBER(sideValue) ? (int)xt_to_number(sideValue) : kPseudoNone;
44
+ if (side == kPseudoUnknown) return nullptr;
45
+ if (side == kPseudoBefore) node = node->pseudo_before;
46
+ else if (side == kPseudoAfter) node = node->pseudo_after;
47
+ if (node == nullptr) return nullptr;
48
+ return win->document->styleOf(node);
49
+ }
50
+
51
+ std::string computed_value(const xtgui::XtStyle *style, const std::string &rawName) {
52
+ if (style == nullptr) return std::string();
53
+ if (rawName.compare(0, 6, "__xt_") == 0) return std::string();
54
+ /* `cs.backgroundColor` and `cs.getPropertyValue("background-color")` name the
55
+ * same longhand; map the camel-cased spelling to the CSS one. */
56
+ std::string mapped;
57
+ for (char c : rawName) {
58
+ if (c >= 'A' && c <= 'Z') {
59
+ mapped.push_back('-');
60
+ mapped.push_back((char)std::tolower((unsigned char)c));
61
+ } else {
62
+ mapped.push_back(c);
63
+ }
64
+ }
65
+ std::string property = xtgui::xt_css_lower(xtgui::xt_css_trim(mapped));
66
+ if (property.empty()) return std::string();
67
+ if (property.size() > 2 && property[0] == '-' && property[1] == '-') {
68
+ auto found = style->variables.find(property);
69
+ return found == style->variables.end() ? std::string() : found->second;
70
+ }
71
+ return xtgui::xt_style_property_to_string(*style, property);
72
+ }
73
+
74
+ xt_value computed_view_get(xt_value self, xt_value key, void *data) {
75
+ (void)data;
76
+ if (!XT_IS_STRING(key)) return XT_UNDEFINED;
77
+ return xt_string_from_cstr(computed_value(computed_style_of(self),
78
+ to_string_value(key)).c_str());
79
+ }
80
+
81
+ /** Computed style is read-only; assigning a named property is a no-op. */
82
+ int computed_view_set(xt_value self, xt_value key, xt_value value, void *data) {
83
+ (void)self;
84
+ (void)key;
85
+ (void)value;
86
+ (void)data;
87
+ return 1;
88
+ }
89
+
90
+ const xt_object_hooks kComputedStyleHooks = {computed_view_get, computed_view_set, nullptr,
91
+ nullptr, nullptr};
92
+
93
+ } // namespace
94
+
95
+ #define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
96
+
97
+ METHOD(computed_style_get_property_value) {
98
+ (void)env;
99
+ if (argc < 1) return xt_string_from_cstr("");
100
+ return xt_string_from_cstr(
101
+ computed_value(computed_style_of(self), to_string_value(xt_arg(argc, argv, 0))).c_str());
102
+ }
103
+
104
+ /** Computed styles never carry a declaration priority (spec: always ""). */
105
+ METHOD(computed_style_get_property_priority) {
106
+ (void)self;
107
+ (void)env;
108
+ (void)argc;
109
+ (void)argv;
110
+ return xt_string_from_cstr("");
111
+ }
112
+
113
+ #undef METHOD
114
+
115
+ xt_value computed_style_handle(xt_value element, xt_value pseudo) {
116
+ xt_value handle = xt_object_new_with_proto(computed_style_proto());
117
+ xt_object_set(handle, xt_string_from_cstr("__xt_gui_element"), element);
118
+ xt_object_set(handle, xt_string_from_cstr("__xt_gui_pseudo"),
119
+ xt_number((double)pseudo_from_argument(pseudo)));
120
+ xt_object_set_hooks(handle, &kComputedStyleHooks, nullptr);
121
+ return handle;
122
+ }
123
+
124
+ } // namespace domapi
@@ -0,0 +1,154 @@
1
+ /*
2
+ * xbintsc GUI engine — `element.dataset`.
3
+ *
4
+ * A `DOMStringMap` is a *view* over the element's `data-*` attributes:
5
+ * `dataset.fooBar` reads `data-foo-bar`, assigning it writes the attribute and
6
+ * `delete dataset.fooBar` removes it. The runtime's dynamic-property hooks make
7
+ * that work for arbitrary keys without enumerating them.
8
+ */
9
+
10
+ #include "dom_api_internal.h"
11
+
12
+ #include <cctype>
13
+ #include <string>
14
+
15
+ namespace domapi {
16
+
17
+ namespace {
18
+
19
+ /** Symbol-keyed hidden slot holding the element handle. A string key would show
20
+ * up in `Object.keys(dataset)`, and `for (const k in dataset)` must only see the
21
+ * data attributes. */
22
+ xt_value dataset_node_key() {
23
+ static xt_value key = XT_UNDEFINED;
24
+ if (key == XT_UNDEFINED) {
25
+ xt_value description = xt_string_from_cstr("xbintsc.datasetNode");
26
+ key = xt_symbol(1, &description);
27
+ xt_gc_add_root(&key);
28
+ }
29
+ return key;
30
+ }
31
+
32
+ /** Resolve the element this DOMStringMap is a view of. Resolving the dataset
33
+ * object itself would ask it for `__xt_gui_win`, a dynamic-key miss that would
34
+ * re-enter the view. */
35
+ xtgui::Node *dataset_element(xt_value self, XtGuiWindow **win) {
36
+ return resolve_node(xt_object_get(self, dataset_node_key()), win);
37
+ }
38
+
39
+ bool dataset_key(xt_value key, std::string *out) {
40
+ if (!XT_IS_STRING(key)) return false;
41
+ *out = to_string_value(key);
42
+ /* Internal bookkeeping keys are never data attributes (and looking one up
43
+ * must not re-enter the view). */
44
+ if (out->compare(0, 6, "__xt_") == 0) return false;
45
+ return !out->empty();
46
+ }
47
+
48
+ /** `fooBar` -> `foo-bar` (the attribute half of the DOMStringMap mapping). */
49
+ std::string to_attribute_name(const std::string &key) {
50
+ std::string out;
51
+ for (char c : key) {
52
+ if (c >= 'A' && c <= 'Z') {
53
+ out.push_back('-');
54
+ out.push_back((char)std::tolower((unsigned char)c));
55
+ } else {
56
+ out.push_back(c);
57
+ }
58
+ }
59
+ return out;
60
+ }
61
+
62
+ /** `foo-bar` -> `fooBar`. */
63
+ std::string to_property_name(const std::string &name) {
64
+ std::string out;
65
+ bool upper = false;
66
+ for (char c : name) {
67
+ if (c == '-') {
68
+ upper = true;
69
+ continue;
70
+ }
71
+ out.push_back(upper ? (char)std::toupper((unsigned char)c) : c);
72
+ upper = false;
73
+ }
74
+ return out;
75
+ }
76
+
77
+ std::string attribute_of(const std::string &key) { return "data-" + to_attribute_name(key); }
78
+
79
+ const std::string *dataset_attribute(const xtgui::Node *node, const std::string &key) {
80
+ return node != nullptr && node->isElement() ? node->attr(attribute_of(key)) : nullptr;
81
+ }
82
+
83
+ xt_value dataset_view_get(xt_value self, xt_value key, void *data) {
84
+ (void)data;
85
+ std::string name;
86
+ if (!dataset_key(key, &name)) return XT_UNDEFINED;
87
+ const std::string *value = dataset_attribute(dataset_element(self, nullptr), name);
88
+ return value == nullptr ? XT_UNDEFINED : xt_string_from_cstr(value->c_str());
89
+ }
90
+
91
+ int dataset_view_set(xt_value self, xt_value key, xt_value value, void *data) {
92
+ (void)data;
93
+ std::string name;
94
+ if (!dataset_key(key, &name)) return 0;
95
+ XtGuiWindow *win = nullptr;
96
+ xtgui::Node *node = dataset_element(self, &win);
97
+ if (node == nullptr || !node->isElement()) return 1;
98
+ set_attribute(win, node, attribute_of(name), to_string_value(value));
99
+ return 1;
100
+ }
101
+
102
+ int dataset_view_has(xt_value self, xt_value key, void *data) {
103
+ (void)data;
104
+ std::string name;
105
+ if (!dataset_key(key, &name)) return 0;
106
+ return dataset_attribute(dataset_element(self, nullptr), name) != nullptr ? 1 : 0;
107
+ }
108
+
109
+ int dataset_view_remove(xt_value self, xt_value key, void *data) {
110
+ (void)data;
111
+ std::string name;
112
+ if (!dataset_key(key, &name)) return 0;
113
+ XtGuiWindow *win = nullptr;
114
+ xtgui::Node *node = dataset_element(self, &win);
115
+ if (node == nullptr || !node->isElement()) return 1;
116
+ remove_attribute_raw(node, attribute_of(name));
117
+ mark_dirty(win);
118
+ return 1;
119
+ }
120
+
121
+ xt_value dataset_view_keys(xt_value self, void *data) {
122
+ (void)data;
123
+ xtgui::Node *node = dataset_element(self, nullptr);
124
+ xt_value result = xt_array_new(0, nullptr);
125
+ if (node == nullptr || !node->isElement()) return result;
126
+ for (const xtgui::Attr &attr : node->attrs) {
127
+ if (attr.name.size() <= 5 || attr.name.compare(0, 5, "data-") != 0) continue;
128
+ std::string name = to_property_name(attr.name.substr(5));
129
+ if (!name.empty()) xt_array_push(result, xt_string_from_cstr(name.c_str()));
130
+ }
131
+ return result;
132
+ }
133
+
134
+ const xt_object_hooks kDatasetHooks = {dataset_view_get, dataset_view_set, dataset_view_has,
135
+ dataset_view_remove, dataset_view_keys};
136
+
137
+ } // namespace
138
+
139
+ #define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
140
+
141
+ METHOD(node_dataset_get) {
142
+ (void)env;
143
+ (void)argc;
144
+ (void)argv;
145
+ xt_value cached = xt_object_get_cstr(self, "__xt_gui_dataset");
146
+ if (XT_IS_OBJECT(cached)) return cached;
147
+ xt_value dataset = xt_object_new();
148
+ xt_object_set(dataset, dataset_node_key(), self);
149
+ xt_object_set_hooks(dataset, &kDatasetHooks, nullptr);
150
+ xt_object_set(self, xt_string_from_cstr("__xt_gui_dataset"), dataset);
151
+ return dataset;
152
+ }
153
+
154
+ } // namespace domapi