xbintsc 0.5.21 → 0.5.23
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/AGENTS.md +1 -1
- package/README.md +15 -5
- package/README.zh-CN.md +11 -4
- package/dist/src/cli/hints.js +11 -0
- package/dist/src/cli/hints.js.map +1 -1
- package/dist/src/cli/main.js +1 -1
- package/dist/src/driver/compiler.js +1 -0
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/catalog.js +2 -1
- package/dist/src/extensions/catalog.js.map +1 -1
- package/dist/src/extensions/media/index.d.ts +27 -0
- package/dist/src/extensions/media/index.js +60 -0
- package/dist/src/extensions/media/index.js.map +1 -0
- package/dist/src/extensions/registry.d.ts +12 -0
- package/dist/src/extensions/registry.js +16 -0
- package/dist/src/extensions/registry.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/tests/cli/hints.test.js +15 -0
- package/dist/tests/cli/hints.test.js.map +1 -1
- package/dist/tests/cli/process.test.js +12 -0
- package/dist/tests/cli/process.test.js.map +1 -1
- package/dist/tests/e2e/gui-canvas.test.js +159 -0
- package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
- package/dist/tests/e2e/gui-example-canvas.test.d.ts +11 -0
- package/dist/tests/e2e/gui-example-canvas.test.js +72 -0
- package/dist/tests/e2e/gui-example-canvas.test.js.map +1 -0
- package/dist/tests/e2e/gui-helpers.d.ts +3 -0
- package/dist/tests/e2e/gui-helpers.js +2 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -1
- package/dist/tests/e2e/gui-media-disabled.test.d.ts +9 -0
- package/dist/tests/e2e/gui-media-disabled.test.js +42 -0
- package/dist/tests/e2e/gui-media-disabled.test.js.map +1 -0
- package/dist/tests/e2e/gui-media.test.d.ts +6 -4
- package/dist/tests/e2e/gui-media.test.js +15 -12
- package/dist/tests/e2e/gui-media.test.js.map +1 -1
- package/dist/tests/e2e/unicode-output.test.d.ts +16 -0
- package/dist/tests/e2e/unicode-output.test.js +65 -0
- package/dist/tests/e2e/unicode-output.test.js.map +1 -0
- package/dist/tests/extensions/media.test.d.ts +1 -0
- package/dist/tests/extensions/media.test.js +58 -0
- package/dist/tests/extensions/media.test.js.map +1 -0
- package/dist/tests/extensions/registry.test.js +8 -0
- package/dist/tests/extensions/registry.test.js.map +1 -1
- package/dist/tests/lexer/identifiers.test.js +12 -0
- package/dist/tests/lexer/identifiers.test.js.map +1 -1
- package/dist/tests/lexer/scanner.test.js +8 -0
- package/dist/tests/lexer/scanner.test.js.map +1 -1
- package/dist/tests/lexer/strings.test.js +30 -0
- package/dist/tests/lexer/strings.test.js.map +1 -1
- package/doc/ai/cli.md +1 -1
- package/doc/ai/extensions.md +35 -22
- package/doc/ai/troubleshooting.md +9 -0
- package/doc/ai/zh-CN/cli.md +1 -1
- package/doc/ai/zh-CN/extensions.md +22 -9
- package/doc/ai/zh-CN/troubleshooting.md +8 -0
- package/doc/gui/build.md +37 -21
- package/doc/gui/canvas.md +47 -21
- package/doc/gui/known-issues.md +7 -6
- package/doc/gui/media.md +26 -11
- package/doc/gui.md +50 -34
- package/doc/zh-CN/gui/build.md +28 -16
- package/doc/zh-CN/gui/canvas.md +36 -17
- package/doc/zh-CN/gui/known-issues.md +6 -5
- package/doc/zh-CN/gui/media.md +21 -10
- package/doc/zh-CN/gui.md +43 -34
- package/package.json +2 -1
- package/runtime/ext_gui/canvas.cpp +114 -199
- package/runtime/ext_gui/canvas.h +101 -2
- package/runtime/ext_gui/canvas_gradient.cpp +112 -0
- package/runtime/ext_gui/canvas_internal.h +6 -6
- package/runtime/ext_gui/canvas_raster.cpp +27 -5
- package/runtime/ext_gui/canvas_stroke.cpp +252 -0
- package/runtime/ext_gui/canvas_style.cpp +240 -0
- package/runtime/ext_gui/dom_api.cpp +1 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +38 -4
- package/runtime/ext_gui/dom_api_canvas_extra.cpp +232 -0
- package/runtime/ext_gui/dom_api_internal.h +31 -0
- package/runtime/ext_gui/dom_api_proto.cpp +24 -0
- package/runtime/ext_gui/gui_media.cpp +69 -2
- package/runtime/ext_gui/media.cpp +34 -488
- package/runtime/ext_gui/media.h +54 -19
- package/runtime/ext_gui/media_api.h +65 -0
- package/runtime/ext_media/media.cpp +507 -0
- package/runtime/ext_media/media_bridge.c +35 -0
- package/scripts/build-gui.ts +6 -72
- package/scripts/build-media.ts +245 -0
- package/src/cli/hints.ts +11 -0
- package/src/cli/main.ts +1 -1
- package/src/driver/compiler.ts +1 -0
- package/src/driver/version.ts +1 -1
- package/src/extensions/catalog.ts +2 -1
- package/src/extensions/media/index.ts +67 -0
- package/src/extensions/registry.ts +24 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — Canvas2D drawing-state parsing.
|
|
3
|
+
*
|
|
4
|
+
* The state object itself lives in `canvas.h`; this unit parses the strings the
|
|
5
|
+
* DOM setters receive (CSS colours, the `font` shorthand, `lineCap`/`lineJoin`
|
|
6
|
+
* keywords) and validates the dash list. Keeping the parsing out of
|
|
7
|
+
* `canvas.cpp` leaves that unit free to own the path and draw entry points.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include <cmath>
|
|
11
|
+
#include <cstdlib>
|
|
12
|
+
#include <cstring>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
#include "canvas.h"
|
|
17
|
+
#include "css.h"
|
|
18
|
+
|
|
19
|
+
namespace xtgui {
|
|
20
|
+
|
|
21
|
+
namespace {
|
|
22
|
+
|
|
23
|
+
std::string lower_ascii(const std::string &value) {
|
|
24
|
+
std::string out = value;
|
|
25
|
+
for (char &c : out) {
|
|
26
|
+
if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a');
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
bool parse_font_size_token(const std::string &token, float fallback, float *out) {
|
|
32
|
+
std::string lower = lower_ascii(token);
|
|
33
|
+
struct Suffix {
|
|
34
|
+
const char *text;
|
|
35
|
+
float factor;
|
|
36
|
+
};
|
|
37
|
+
static const Suffix suffixes[] = {{"px", 1.0f}, {"pt", 96.0f / 72.0f}, {"pc", 16.0f},
|
|
38
|
+
{"in", 96.0f}, {"cm", 96.0f / 2.54f}, {"mm", 96.0f / 25.4f},
|
|
39
|
+
{"q", 96.0f / 101.6f}, {"em", 0.0f}, {"rem", 0.0f}};
|
|
40
|
+
for (const Suffix &suffix : suffixes) {
|
|
41
|
+
size_t length = std::strlen(suffix.text);
|
|
42
|
+
if (lower.size() <= length || lower.compare(lower.size() - length, length, suffix.text) != 0)
|
|
43
|
+
continue;
|
|
44
|
+
char *end = nullptr;
|
|
45
|
+
float value = std::strtof(lower.c_str(), &end);
|
|
46
|
+
if (end == lower.c_str()) return false;
|
|
47
|
+
if (suffix.factor == 0.0f) {
|
|
48
|
+
*out = value * fallback;
|
|
49
|
+
} else {
|
|
50
|
+
*out = value * suffix.factor;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Parse the CSS `font` shorthand into a `FontSpec` (the pieces Canvas2D
|
|
58
|
+
* understands; unknown tokens are ignored). */
|
|
59
|
+
FontSpec parse_font_string(const std::string &text) {
|
|
60
|
+
FontSpec spec;
|
|
61
|
+
spec.family = "sans-serif";
|
|
62
|
+
spec.pixel_size = 10.0f;
|
|
63
|
+
spec.weight = 400;
|
|
64
|
+
spec.italic = false;
|
|
65
|
+
std::vector<std::string> tokens = xt_css_split_whitespace(text);
|
|
66
|
+
std::vector<std::string> family;
|
|
67
|
+
float size = 10.0f;
|
|
68
|
+
bool have_size = false;
|
|
69
|
+
for (const std::string &token : tokens) {
|
|
70
|
+
std::string lower = lower_ascii(token);
|
|
71
|
+
float parsed = 0.0f;
|
|
72
|
+
if (!have_size && parse_font_size_token(lower, 16.0f, &parsed)) {
|
|
73
|
+
size = parsed;
|
|
74
|
+
have_size = true;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (lower == "italic" || lower == "oblique") {
|
|
78
|
+
spec.italic = true;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (lower == "bold" || lower == "bolder") {
|
|
82
|
+
spec.weight = 700;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (lower == "lighter") {
|
|
86
|
+
spec.weight = 300;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (lower == "normal" || lower == "small-caps") continue;
|
|
90
|
+
if (!lower.empty() && lower.back() == ',') lower.pop_back();
|
|
91
|
+
if (!lower.empty()) family.push_back(lower);
|
|
92
|
+
}
|
|
93
|
+
spec.pixel_size = size;
|
|
94
|
+
if (!family.empty()) spec.family = family[0];
|
|
95
|
+
return spec;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
void parse_color_into(const std::string &css, float color[4], std::string *stored) {
|
|
99
|
+
Color parsed = xt_css_parse_color(css);
|
|
100
|
+
if (!parsed.valid) return;
|
|
101
|
+
color[0] = parsed.r;
|
|
102
|
+
color[1] = parsed.g;
|
|
103
|
+
color[2] = parsed.b;
|
|
104
|
+
color[3] = parsed.a;
|
|
105
|
+
*stored = css;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
bool positive_finite(float value) { return std::isfinite(value) && value >= 0.0f; }
|
|
109
|
+
|
|
110
|
+
} // namespace
|
|
111
|
+
|
|
112
|
+
void Canvas2D::set_fill_style(const std::string &css) {
|
|
113
|
+
parse_color_into(css, state_.fill_color, &state_.fill_style);
|
|
114
|
+
state_.fill_gradient = 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
void Canvas2D::set_stroke_style(const std::string &css) {
|
|
118
|
+
parse_color_into(css, state_.stroke_color, &state_.stroke_style);
|
|
119
|
+
state_.stroke_gradient = 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
void Canvas2D::set_fill_gradient(uint64_t id) {
|
|
123
|
+
if (id == 0) {
|
|
124
|
+
state_.fill_gradient = 0;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (find_gradient(id) != nullptr) state_.fill_gradient = id;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
void Canvas2D::set_stroke_gradient(uint64_t id) {
|
|
131
|
+
if (id == 0) {
|
|
132
|
+
state_.stroke_gradient = 0;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (find_gradient(id) != nullptr) state_.stroke_gradient = id;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
void Canvas2D::set_global_alpha(float alpha) {
|
|
139
|
+
if (!std::isfinite(alpha)) return;
|
|
140
|
+
if (alpha < 0.0f) alpha = 0.0f;
|
|
141
|
+
if (alpha > 1.0f) alpha = 1.0f;
|
|
142
|
+
state_.global_alpha = alpha;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
void Canvas2D::set_font(const std::string &font) {
|
|
146
|
+
state_.font = parse_font_string(font);
|
|
147
|
+
state_.font_string = font;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
void Canvas2D::set_text_align(const std::string &value) {
|
|
151
|
+
std::string lower = lower_ascii(value);
|
|
152
|
+
if (lower == "start" || lower == "left") {
|
|
153
|
+
state_.text_align = 0;
|
|
154
|
+
} else if (lower == "center") {
|
|
155
|
+
state_.text_align = 1;
|
|
156
|
+
} else if (lower == "right" || lower == "end") {
|
|
157
|
+
state_.text_align = 2;
|
|
158
|
+
} else {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
state_.text_align_string = lower;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
void Canvas2D::set_text_baseline(const std::string &value) {
|
|
165
|
+
std::string lower = lower_ascii(value);
|
|
166
|
+
if (lower == "alphabetic") {
|
|
167
|
+
state_.text_baseline = 0;
|
|
168
|
+
} else if (lower == "top") {
|
|
169
|
+
state_.text_baseline = 1;
|
|
170
|
+
} else if (lower == "middle") {
|
|
171
|
+
state_.text_baseline = 2;
|
|
172
|
+
} else if (lower == "bottom") {
|
|
173
|
+
state_.text_baseline = 3;
|
|
174
|
+
} else if (lower == "hanging") {
|
|
175
|
+
state_.text_baseline = 4;
|
|
176
|
+
} else if (lower == "ideographic") {
|
|
177
|
+
state_.text_baseline = 5;
|
|
178
|
+
} else {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
state_.text_baseline_string = lower;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
void Canvas2D::set_line_cap(const std::string &value) {
|
|
185
|
+
std::string lower = lower_ascii(value);
|
|
186
|
+
if (lower == "butt") {
|
|
187
|
+
state_.line_cap = 0;
|
|
188
|
+
} else if (lower == "round") {
|
|
189
|
+
state_.line_cap = 1;
|
|
190
|
+
} else if (lower == "square") {
|
|
191
|
+
state_.line_cap = 2;
|
|
192
|
+
} else {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
state_.line_cap_string = lower;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
void Canvas2D::set_line_join(const std::string &value) {
|
|
199
|
+
std::string lower = lower_ascii(value);
|
|
200
|
+
if (lower == "miter") {
|
|
201
|
+
state_.line_join = 0;
|
|
202
|
+
} else if (lower == "round") {
|
|
203
|
+
state_.line_join = 1;
|
|
204
|
+
} else if (lower == "bevel") {
|
|
205
|
+
state_.line_join = 2;
|
|
206
|
+
} else {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
state_.line_join_string = lower;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
void Canvas2D::set_miter_limit(float limit) {
|
|
213
|
+
if (!std::isfinite(limit) || limit <= 0.0f) return;
|
|
214
|
+
state_.miter_limit = limit;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
void Canvas2D::set_line_dash(const std::vector<float> &segments) {
|
|
218
|
+
if (segments.empty()) {
|
|
219
|
+
state_.line_dash.clear();
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
for (float segment : segments) {
|
|
223
|
+
/* The spec ignores a list containing a negative or non-finite value. */
|
|
224
|
+
if (!positive_finite(segment)) return;
|
|
225
|
+
}
|
|
226
|
+
std::vector<float> pattern = segments;
|
|
227
|
+
if (pattern.size() % 2 == 1) {
|
|
228
|
+
const size_t count = pattern.size();
|
|
229
|
+
for (size_t i = 0; i < count; i++) pattern.push_back(pattern[i]);
|
|
230
|
+
}
|
|
231
|
+
float total = 0.0f;
|
|
232
|
+
for (float segment : pattern) total += segment;
|
|
233
|
+
if (total <= 0.0f) {
|
|
234
|
+
state_.line_dash.clear();
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
state_.line_dash = std::move(pattern);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
} // namespace xtgui
|
|
@@ -33,6 +33,7 @@ xt_value g_event_proto = XT_UNDEFINED;
|
|
|
33
33
|
xt_value g_class_list_proto = XT_UNDEFINED;
|
|
34
34
|
xt_value g_style_proto = XT_UNDEFINED;
|
|
35
35
|
xt_value g_canvas_proto = XT_UNDEFINED;
|
|
36
|
+
xt_value g_canvas_gradient_proto = XT_UNDEFINED;
|
|
36
37
|
|
|
37
38
|
/* -- small helpers -------------------------------------------------------- */
|
|
38
39
|
|
|
@@ -43,6 +43,16 @@ std::string string_arg(int32_t argc, xt_value *argv, int32_t index) {
|
|
|
43
43
|
|
|
44
44
|
} // namespace
|
|
45
45
|
|
|
46
|
+
xtgui::Canvas2D *canvas_from_context(xt_value self) { return context_canvas(self); }
|
|
47
|
+
|
|
48
|
+
double canvas_number_arg(int32_t argc, xt_value *argv, int32_t index, double fallback) {
|
|
49
|
+
return number_arg(argc, argv, index, fallback);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
std::string canvas_string_arg(int32_t argc, xt_value *argv, int32_t index) {
|
|
53
|
+
return string_arg(argc, argv, index);
|
|
54
|
+
}
|
|
55
|
+
|
|
46
56
|
METHOD(node_get_context) {
|
|
47
57
|
(void)env;
|
|
48
58
|
std::string mode = string_arg(argc, argv, 0);
|
|
@@ -152,13 +162,25 @@ METHOD(canvas_fill_style_get) {
|
|
|
152
162
|
(void)argc;
|
|
153
163
|
(void)argv;
|
|
154
164
|
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
155
|
-
|
|
165
|
+
if (canvas == nullptr) return xt_string_from_cstr("#000000");
|
|
166
|
+
if (canvas->fill_gradient_id() != 0) {
|
|
167
|
+
return canvas_gradient_handle(self, canvas, canvas->fill_gradient_id());
|
|
168
|
+
}
|
|
169
|
+
return xt_string_from_cstr(canvas->fill_style().c_str());
|
|
156
170
|
}
|
|
157
171
|
|
|
158
172
|
METHOD(canvas_fill_style_set) {
|
|
159
173
|
(void)env;
|
|
160
174
|
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
161
|
-
if (canvas
|
|
175
|
+
if (canvas == nullptr) return XT_UNDEFINED;
|
|
176
|
+
if (argc > 0) {
|
|
177
|
+
uint64_t gradient = canvas_gradient_id(xt_arg(argc, argv, 0), canvas);
|
|
178
|
+
if (gradient != 0) {
|
|
179
|
+
canvas->set_fill_gradient(gradient);
|
|
180
|
+
return XT_UNDEFINED;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
canvas->set_fill_style(string_arg(argc, argv, 0));
|
|
162
184
|
return XT_UNDEFINED;
|
|
163
185
|
}
|
|
164
186
|
|
|
@@ -167,13 +189,25 @@ METHOD(canvas_stroke_style_get) {
|
|
|
167
189
|
(void)argc;
|
|
168
190
|
(void)argv;
|
|
169
191
|
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
170
|
-
|
|
192
|
+
if (canvas == nullptr) return xt_string_from_cstr("#000000");
|
|
193
|
+
if (canvas->stroke_gradient_id() != 0) {
|
|
194
|
+
return canvas_gradient_handle(self, canvas, canvas->stroke_gradient_id());
|
|
195
|
+
}
|
|
196
|
+
return xt_string_from_cstr(canvas->stroke_style().c_str());
|
|
171
197
|
}
|
|
172
198
|
|
|
173
199
|
METHOD(canvas_stroke_style_set) {
|
|
174
200
|
(void)env;
|
|
175
201
|
xtgui::Canvas2D *canvas = context_canvas(self);
|
|
176
|
-
if (canvas
|
|
202
|
+
if (canvas == nullptr) return XT_UNDEFINED;
|
|
203
|
+
if (argc > 0) {
|
|
204
|
+
uint64_t gradient = canvas_gradient_id(xt_arg(argc, argv, 0), canvas);
|
|
205
|
+
if (gradient != 0) {
|
|
206
|
+
canvas->set_stroke_gradient(gradient);
|
|
207
|
+
return XT_UNDEFINED;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
canvas->set_stroke_style(string_arg(argc, argv, 0));
|
|
177
211
|
return XT_UNDEFINED;
|
|
178
212
|
}
|
|
179
213
|
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — the newer half of the Canvas2D DOM surface.
|
|
3
|
+
*
|
|
4
|
+
* `dom_api_canvas.cpp` owns the original path/rect/text/image bindings and the
|
|
5
|
+
* shared context helpers; this unit adds gradients, ellipses, rounded
|
|
6
|
+
* rectangles, line caps/joins, dash patterns and `getTransform`. Keeping them
|
|
7
|
+
* separate keeps both translation units under the repository's file budget.
|
|
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 "css.h"
|
|
18
|
+
|
|
19
|
+
#define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
|
|
20
|
+
|
|
21
|
+
namespace domapi {
|
|
22
|
+
|
|
23
|
+
xt_value canvas_gradient_handle(xt_value context, const xtgui::Canvas2D *canvas, uint64_t id) {
|
|
24
|
+
const std::string key = "__xt_gui_grad_" + std::to_string(id);
|
|
25
|
+
xt_value cached = xt_object_get_cstr(context, key.c_str());
|
|
26
|
+
if (XT_IS_OBJECT(cached)) return cached;
|
|
27
|
+
xt_value handle = xt_object_new_with_proto(canvas_gradient_proto());
|
|
28
|
+
if (canvas != nullptr) {
|
|
29
|
+
xt_object_set(handle, xt_string_from_cstr("__xt_gui_canvas"), xt_number((double)canvas->id()));
|
|
30
|
+
}
|
|
31
|
+
xt_object_set(handle, xt_string_from_cstr("__xt_gui_gradient"), xt_number((double)id));
|
|
32
|
+
xt_object_set(context, xt_string_from_cstr(key.c_str()), handle);
|
|
33
|
+
return handle;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
uint64_t canvas_gradient_id(xt_value handle, const xtgui::Canvas2D *canvas) {
|
|
37
|
+
if (canvas == nullptr || !XT_IS_OBJECT(handle)) return 0;
|
|
38
|
+
xt_value canvasId = xt_object_get_cstr(handle, "__xt_gui_canvas");
|
|
39
|
+
xt_value gradientId = xt_object_get_cstr(handle, "__xt_gui_gradient");
|
|
40
|
+
if (!XT_IS_NUMBER(canvasId) || !XT_IS_NUMBER(gradientId)) return 0;
|
|
41
|
+
if ((uint64_t)xt_to_number(canvasId) != canvas->id()) return 0;
|
|
42
|
+
uint64_t id = (uint64_t)xt_to_number(gradientId);
|
|
43
|
+
return canvas->find_gradient(id) == nullptr ? 0 : id;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* -- gradients ------------------------------------------------------------ */
|
|
47
|
+
|
|
48
|
+
METHOD(canvas_create_linear_gradient) {
|
|
49
|
+
(void)env;
|
|
50
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
51
|
+
if (canvas == nullptr) return XT_NULL;
|
|
52
|
+
xtgui::CanvasGradient *gradient = canvas->create_linear_gradient(
|
|
53
|
+
(float)canvas_number_arg(argc, argv, 0, 0.0), (float)canvas_number_arg(argc, argv, 1, 0.0),
|
|
54
|
+
(float)canvas_number_arg(argc, argv, 2, 0.0), (float)canvas_number_arg(argc, argv, 3, 0.0));
|
|
55
|
+
return canvas_gradient_handle(self, canvas, gradient->id());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
METHOD(canvas_create_radial_gradient) {
|
|
59
|
+
(void)env;
|
|
60
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
61
|
+
if (canvas == nullptr) return XT_NULL;
|
|
62
|
+
xtgui::CanvasGradient *gradient = canvas->create_radial_gradient(
|
|
63
|
+
(float)canvas_number_arg(argc, argv, 0, 0.0), (float)canvas_number_arg(argc, argv, 1, 0.0),
|
|
64
|
+
(float)canvas_number_arg(argc, argv, 2, 0.0), (float)canvas_number_arg(argc, argv, 3, 0.0),
|
|
65
|
+
(float)canvas_number_arg(argc, argv, 4, 0.0), (float)canvas_number_arg(argc, argv, 5, 0.0));
|
|
66
|
+
return canvas_gradient_handle(self, canvas, gradient->id());
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
METHOD(canvas_gradient_add_color_stop) {
|
|
70
|
+
(void)env;
|
|
71
|
+
xt_value canvasId = xt_object_get_cstr(self, "__xt_gui_canvas");
|
|
72
|
+
xt_value gradientId = xt_object_get_cstr(self, "__xt_gui_gradient");
|
|
73
|
+
if (!XT_IS_NUMBER(canvasId) || !XT_IS_NUMBER(gradientId)) return XT_UNDEFINED;
|
|
74
|
+
xtgui::Canvas2D *canvas = xtgui::xt_canvas_by_id((uint64_t)xt_to_number(canvasId));
|
|
75
|
+
if (canvas == nullptr) return XT_UNDEFINED;
|
|
76
|
+
xtgui::CanvasGradient *gradient = canvas->find_gradient((uint64_t)xt_to_number(gradientId));
|
|
77
|
+
if (gradient == nullptr) return XT_UNDEFINED;
|
|
78
|
+
xtgui::Color color = xtgui::xt_css_parse_color(canvas_string_arg(argc, argv, 1));
|
|
79
|
+
if (!color.valid) return XT_UNDEFINED;
|
|
80
|
+
float rgba[4] = {color.r, color.g, color.b, color.a};
|
|
81
|
+
gradient->add_color_stop((float)canvas_number_arg(argc, argv, 0, 0.0), rgba);
|
|
82
|
+
return XT_UNDEFINED;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* -- paths ---------------------------------------------------------------- */
|
|
86
|
+
|
|
87
|
+
METHOD(canvas_ellipse) {
|
|
88
|
+
(void)env;
|
|
89
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
90
|
+
if (canvas != nullptr) {
|
|
91
|
+
canvas->ellipse((float)canvas_number_arg(argc, argv, 0, 0.0),
|
|
92
|
+
(float)canvas_number_arg(argc, argv, 1, 0.0),
|
|
93
|
+
(float)canvas_number_arg(argc, argv, 2, 0.0),
|
|
94
|
+
(float)canvas_number_arg(argc, argv, 3, 0.0),
|
|
95
|
+
(float)canvas_number_arg(argc, argv, 4, 0.0),
|
|
96
|
+
(float)canvas_number_arg(argc, argv, 5, 0.0),
|
|
97
|
+
(float)canvas_number_arg(argc, argv, 6, 0.0),
|
|
98
|
+
value_truthy(xt_arg(argc, argv, 7)));
|
|
99
|
+
}
|
|
100
|
+
return XT_UNDEFINED;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
METHOD(canvas_round_rect) {
|
|
104
|
+
(void)env;
|
|
105
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
106
|
+
if (canvas != nullptr) {
|
|
107
|
+
canvas->round_rect((float)canvas_number_arg(argc, argv, 0, 0.0),
|
|
108
|
+
(float)canvas_number_arg(argc, argv, 1, 0.0),
|
|
109
|
+
(float)canvas_number_arg(argc, argv, 2, 0.0),
|
|
110
|
+
(float)canvas_number_arg(argc, argv, 3, 0.0),
|
|
111
|
+
(float)canvas_number_arg(argc, argv, 4, 0.0));
|
|
112
|
+
}
|
|
113
|
+
return XT_UNDEFINED;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* -- line styles ---------------------------------------------------------- */
|
|
117
|
+
|
|
118
|
+
METHOD(canvas_line_cap_get) {
|
|
119
|
+
(void)env;
|
|
120
|
+
(void)argc;
|
|
121
|
+
(void)argv;
|
|
122
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
123
|
+
return xt_string_from_cstr(canvas != nullptr ? canvas->line_cap().c_str() : "butt");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
METHOD(canvas_line_cap_set) {
|
|
127
|
+
(void)env;
|
|
128
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
129
|
+
if (canvas != nullptr) canvas->set_line_cap(canvas_string_arg(argc, argv, 0));
|
|
130
|
+
return XT_UNDEFINED;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
METHOD(canvas_line_join_get) {
|
|
134
|
+
(void)env;
|
|
135
|
+
(void)argc;
|
|
136
|
+
(void)argv;
|
|
137
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
138
|
+
return xt_string_from_cstr(canvas != nullptr ? canvas->line_join().c_str() : "miter");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
METHOD(canvas_line_join_set) {
|
|
142
|
+
(void)env;
|
|
143
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
144
|
+
if (canvas != nullptr) canvas->set_line_join(canvas_string_arg(argc, argv, 0));
|
|
145
|
+
return XT_UNDEFINED;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
METHOD(canvas_miter_limit_get) {
|
|
149
|
+
(void)env;
|
|
150
|
+
(void)argc;
|
|
151
|
+
(void)argv;
|
|
152
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
153
|
+
return xt_number(canvas != nullptr ? canvas->miter_limit() : 10.0);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
METHOD(canvas_miter_limit_set) {
|
|
157
|
+
(void)env;
|
|
158
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
159
|
+
if (canvas != nullptr) canvas->set_miter_limit((float)canvas_number_arg(argc, argv, 0, 10.0));
|
|
160
|
+
return XT_UNDEFINED;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
METHOD(canvas_line_dash_get) {
|
|
164
|
+
(void)env;
|
|
165
|
+
(void)argc;
|
|
166
|
+
(void)argv;
|
|
167
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
168
|
+
xt_value result = xt_array_new(0, nullptr);
|
|
169
|
+
if (canvas != nullptr) {
|
|
170
|
+
for (float entry : canvas->line_dash()) xt_array_push(result, xt_number(entry));
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
METHOD(canvas_line_dash_set) {
|
|
176
|
+
(void)env;
|
|
177
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
178
|
+
if (canvas == nullptr) return XT_UNDEFINED;
|
|
179
|
+
std::vector<float> pattern;
|
|
180
|
+
if (argc > 0) {
|
|
181
|
+
xt_value value = xt_arg(argc, argv, 0);
|
|
182
|
+
if (XT_IS_ARRAY(value)) {
|
|
183
|
+
int32_t size = xt_array_size(value);
|
|
184
|
+
for (int32_t i = 0; i < size; i++) {
|
|
185
|
+
pattern.push_back((float)xt_to_number(xt_array_get(value, xt_number((double)i))));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
canvas->set_line_dash(pattern);
|
|
190
|
+
return XT_UNDEFINED;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
METHOD(canvas_line_dash_offset_get) {
|
|
194
|
+
(void)env;
|
|
195
|
+
(void)argc;
|
|
196
|
+
(void)argv;
|
|
197
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
198
|
+
return xt_number(canvas != nullptr ? canvas->line_dash_offset() : 0.0);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
METHOD(canvas_line_dash_offset_set) {
|
|
202
|
+
(void)env;
|
|
203
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
204
|
+
if (canvas != nullptr) {
|
|
205
|
+
canvas->set_line_dash_offset((float)canvas_number_arg(argc, argv, 0, 0.0));
|
|
206
|
+
}
|
|
207
|
+
return XT_UNDEFINED;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* -- transform ------------------------------------------------------------ */
|
|
211
|
+
|
|
212
|
+
METHOD(canvas_get_transform) {
|
|
213
|
+
(void)env;
|
|
214
|
+
(void)argc;
|
|
215
|
+
(void)argv;
|
|
216
|
+
xtgui::Canvas2D *canvas = canvas_from_context(self);
|
|
217
|
+
const xtgui::CanvasTransform transform =
|
|
218
|
+
canvas != nullptr ? canvas->current_transform() : xtgui::CanvasTransform();
|
|
219
|
+
xt_value result = xt_object_new();
|
|
220
|
+
xt_object_set(result, xt_string_from_cstr("a"), xt_number(transform.a));
|
|
221
|
+
xt_object_set(result, xt_string_from_cstr("b"), xt_number(transform.b));
|
|
222
|
+
xt_object_set(result, xt_string_from_cstr("c"), xt_number(transform.c));
|
|
223
|
+
xt_object_set(result, xt_string_from_cstr("d"), xt_number(transform.d));
|
|
224
|
+
xt_object_set(result, xt_string_from_cstr("e"), xt_number(transform.e));
|
|
225
|
+
xt_object_set(result, xt_string_from_cstr("f"), xt_number(transform.f));
|
|
226
|
+
xt_object_set(result, xt_string_from_cstr("is2D"), xt_bool(true));
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
} // namespace domapi
|
|
231
|
+
|
|
232
|
+
#undef METHOD
|
|
@@ -28,6 +28,7 @@ extern xt_value g_event_proto;
|
|
|
28
28
|
extern xt_value g_class_list_proto;
|
|
29
29
|
extern xt_value g_style_proto;
|
|
30
30
|
extern xt_value g_canvas_proto;
|
|
31
|
+
extern xt_value g_canvas_gradient_proto;
|
|
31
32
|
|
|
32
33
|
xt_value node_proto();
|
|
33
34
|
xt_value document_proto();
|
|
@@ -35,6 +36,20 @@ xt_value event_proto();
|
|
|
35
36
|
xt_value class_list_proto();
|
|
36
37
|
xt_value style_proto();
|
|
37
38
|
xt_value canvas_proto();
|
|
39
|
+
xt_value canvas_gradient_proto();
|
|
40
|
+
|
|
41
|
+
/* -- canvas helpers shared by `dom_api_canvas*.cpp` ----------------------- */
|
|
42
|
+
|
|
43
|
+
/** The engine canvas behind a 2D context handle, or null when stale/foreign. */
|
|
44
|
+
xtgui::Canvas2D *canvas_from_context(xt_value self);
|
|
45
|
+
double canvas_number_arg(int32_t argc, xt_value *argv, int32_t index, double fallback);
|
|
46
|
+
std::string canvas_string_arg(int32_t argc, xt_value *argv, int32_t index);
|
|
47
|
+
/** The cached `CanvasGradient` handle for `id` on `canvas`; cached on the
|
|
48
|
+
* context so `ctx.fillStyle === gradient` holds after `fillStyle = gradient`. */
|
|
49
|
+
xt_value canvas_gradient_handle(xt_value context, const xtgui::Canvas2D *canvas, uint64_t id);
|
|
50
|
+
/** The gradient id stored on a handle, or 0 when it is not a gradient for
|
|
51
|
+
* `canvas` (stale or created by another canvas). */
|
|
52
|
+
uint64_t canvas_gradient_id(xt_value handle, const xtgui::Canvas2D *canvas);
|
|
38
53
|
|
|
39
54
|
/* -- cross-unit helpers --------------------------------------------------- */
|
|
40
55
|
|
|
@@ -195,6 +210,22 @@ METHOD(canvas_fill_text);
|
|
|
195
210
|
METHOD(canvas_stroke_text);
|
|
196
211
|
METHOD(canvas_draw_image);
|
|
197
212
|
METHOD(canvas_get_image_data);
|
|
213
|
+
METHOD(canvas_ellipse);
|
|
214
|
+
METHOD(canvas_round_rect);
|
|
215
|
+
METHOD(canvas_line_cap_get);
|
|
216
|
+
METHOD(canvas_line_cap_set);
|
|
217
|
+
METHOD(canvas_line_join_get);
|
|
218
|
+
METHOD(canvas_line_join_set);
|
|
219
|
+
METHOD(canvas_miter_limit_get);
|
|
220
|
+
METHOD(canvas_miter_limit_set);
|
|
221
|
+
METHOD(canvas_line_dash_get);
|
|
222
|
+
METHOD(canvas_line_dash_set);
|
|
223
|
+
METHOD(canvas_line_dash_offset_get);
|
|
224
|
+
METHOD(canvas_line_dash_offset_set);
|
|
225
|
+
METHOD(canvas_get_transform);
|
|
226
|
+
METHOD(canvas_create_linear_gradient);
|
|
227
|
+
METHOD(canvas_create_radial_gradient);
|
|
228
|
+
METHOD(canvas_gradient_add_color_stop);
|
|
198
229
|
|
|
199
230
|
/* classList methods */
|
|
200
231
|
METHOD(class_list_add);
|
|
@@ -181,6 +181,14 @@ xt_value canvas_proto() {
|
|
|
181
181
|
define_setter(proto, "textAlign", (void *)canvas_text_align_set);
|
|
182
182
|
define_getter(proto, "textBaseline", (void *)canvas_text_baseline_get);
|
|
183
183
|
define_setter(proto, "textBaseline", (void *)canvas_text_baseline_set);
|
|
184
|
+
define_getter(proto, "lineCap", (void *)canvas_line_cap_get);
|
|
185
|
+
define_setter(proto, "lineCap", (void *)canvas_line_cap_set);
|
|
186
|
+
define_getter(proto, "lineJoin", (void *)canvas_line_join_get);
|
|
187
|
+
define_setter(proto, "lineJoin", (void *)canvas_line_join_set);
|
|
188
|
+
define_getter(proto, "miterLimit", (void *)canvas_miter_limit_get);
|
|
189
|
+
define_setter(proto, "miterLimit", (void *)canvas_miter_limit_set);
|
|
190
|
+
define_getter(proto, "lineDashOffset", (void *)canvas_line_dash_offset_get);
|
|
191
|
+
define_setter(proto, "lineDashOffset", (void *)canvas_line_dash_offset_set);
|
|
184
192
|
/* Methods. */
|
|
185
193
|
define_method(proto, "save", (void *)canvas_save);
|
|
186
194
|
define_method(proto, "restore", (void *)canvas_restore);
|
|
@@ -195,7 +203,9 @@ xt_value canvas_proto() {
|
|
|
195
203
|
define_method(proto, "moveTo", (void *)canvas_move_to);
|
|
196
204
|
define_method(proto, "lineTo", (void *)canvas_line_to);
|
|
197
205
|
define_method(proto, "rect", (void *)canvas_rect);
|
|
206
|
+
define_method(proto, "roundRect", (void *)canvas_round_rect);
|
|
198
207
|
define_method(proto, "arc", (void *)canvas_arc);
|
|
208
|
+
define_method(proto, "ellipse", (void *)canvas_ellipse);
|
|
199
209
|
define_method(proto, "quadraticCurveTo", (void *)canvas_quadratic_curve_to);
|
|
200
210
|
define_method(proto, "bezierCurveTo", (void *)canvas_bezier_curve_to);
|
|
201
211
|
define_method(proto, "fill", (void *)canvas_fill);
|
|
@@ -208,8 +218,22 @@ xt_value canvas_proto() {
|
|
|
208
218
|
define_method(proto, "strokeText", (void *)canvas_stroke_text);
|
|
209
219
|
define_method(proto, "drawImage", (void *)canvas_draw_image);
|
|
210
220
|
define_method(proto, "getImageData", (void *)canvas_get_image_data);
|
|
221
|
+
define_method(proto, "setLineDash", (void *)canvas_line_dash_set);
|
|
222
|
+
define_method(proto, "getLineDash", (void *)canvas_line_dash_get);
|
|
223
|
+
define_method(proto, "getTransform", (void *)canvas_get_transform);
|
|
224
|
+
define_method(proto, "createLinearGradient", (void *)canvas_create_linear_gradient);
|
|
225
|
+
define_method(proto, "createRadialGradient", (void *)canvas_create_radial_gradient);
|
|
211
226
|
g_canvas_proto = proto;
|
|
212
227
|
return proto;
|
|
213
228
|
}
|
|
214
229
|
|
|
230
|
+
xt_value canvas_gradient_proto() {
|
|
231
|
+
xt_gc_add_root(&g_canvas_gradient_proto);
|
|
232
|
+
if (XT_IS_OBJECT(g_canvas_gradient_proto)) return g_canvas_gradient_proto;
|
|
233
|
+
xt_value proto = xt_object_new();
|
|
234
|
+
define_method(proto, "addColorStop", (void *)canvas_gradient_add_color_stop);
|
|
235
|
+
g_canvas_gradient_proto = proto;
|
|
236
|
+
return proto;
|
|
237
|
+
}
|
|
238
|
+
|
|
215
239
|
} // namespace domapi
|