xbintsc 0.5.36 → 0.5.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/codegen/generator/state.js +1 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.js +1 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/deferred.d.ts +63 -0
- package/dist/src/driver/bundler/deferred.js +279 -0
- package/dist/src/driver/bundler/deferred.js.map +1 -0
- package/dist/src/driver/bundler/merge.js +214 -2
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/runtime-objects.d.ts +1 -1
- package/dist/src/driver/runtime-objects.js +1 -0
- package/dist/src/driver/runtime-objects.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/gui/html.js +89 -13
- package/dist/src/extensions/gui/html.js.map +1 -1
- package/dist/src/extensions/gui/index.js +13 -0
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/gui/three.d.ts +4 -3
- package/dist/src/extensions/gui/three.js +4 -3
- package/dist/src/extensions/gui/three.js.map +1 -1
- package/dist/tests/e2e/gui-browser.test.js +65 -0
- package/dist/tests/e2e/gui-browser.test.js.map +1 -1
- package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
- package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
- package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
- package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
- package/dist/tests/e2e/gui-dom.test.js +215 -0
- package/dist/tests/e2e/gui-dom.test.js.map +1 -1
- package/dist/tests/e2e/gui-events.test.js +112 -0
- package/dist/tests/e2e/gui-events.test.js.map +1 -1
- package/dist/tests/e2e/gui-script-import-order.test.d.ts +10 -0
- package/dist/tests/e2e/gui-script-import-order.test.js +161 -0
- package/dist/tests/e2e/gui-script-import-order.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.js +108 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.js.map +1 -0
- package/dist/tests/e2e/url.test.d.ts +6 -0
- package/dist/tests/e2e/url.test.js +114 -0
- package/dist/tests/e2e/url.test.js.map +1 -0
- package/dist/tests/extensions/gui.test.js +22 -10
- package/dist/tests/extensions/gui.test.js.map +1 -1
- package/doc/gui/api.md +38 -9
- package/doc/gui/canvas.md +49 -18
- package/doc/gui/dom.md +51 -4
- package/doc/gui/webgl.md +20 -10
- package/doc/gui-scripts.md +19 -19
- package/doc/gui.md +10 -8
- package/doc/implemented/fetch.md +5 -0
- package/doc/implemented/modules.md +13 -0
- package/doc/implemented/stdlib.md +17 -0
- package/doc/unimplemented/deviations.md +1 -0
- package/doc/zh-CN/gui/api.md +33 -4
- package/doc/zh-CN/gui/canvas.md +44 -19
- package/doc/zh-CN/gui/dom.md +45 -3
- package/doc/zh-CN/gui/webgl.md +3 -3
- package/doc/zh-CN/gui-scripts.md +14 -13
- package/doc/zh-CN/gui.md +7 -6
- package/doc/zh-CN/implemented/fetch.md +4 -0
- package/doc/zh-CN/implemented/modules.md +10 -0
- package/doc/zh-CN/implemented/stdlib.md +15 -0
- package/doc/zh-CN/unimplemented/deviations.md +1 -0
- package/package.json +7 -7
- package/runtime/ext_gui/browser.cpp +34 -49
- package/runtime/ext_gui/browser_abort.cpp +249 -0
- package/runtime/ext_gui/browser_dialogs.cpp +76 -0
- package/runtime/ext_gui/browser_media.cpp +176 -0
- package/runtime/ext_gui/browser_navigation.cpp +130 -0
- package/runtime/ext_gui/canvas.cpp +88 -30
- package/runtime/ext_gui/canvas.h +74 -9
- package/runtime/ext_gui/canvas_image_data.cpp +143 -0
- package/runtime/ext_gui/canvas_internal.h +8 -5
- package/runtime/ext_gui/canvas_pattern.cpp +90 -0
- package/runtime/ext_gui/canvas_raster.cpp +31 -9
- package/runtime/ext_gui/canvas_style.cpp +4 -2
- package/runtime/ext_gui/dom.h +4 -0
- package/runtime/ext_gui/dom_api.cpp +1 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
- package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
- package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
- package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
- package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
- package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
- package/runtime/ext_gui/dom_api_document.cpp +58 -5
- package/runtime/ext_gui/dom_api_events.cpp +93 -17
- package/runtime/ext_gui/dom_api_internal.h +55 -2
- package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
- package/runtime/ext_gui/dom_api_node.cpp +40 -19
- package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
- package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
- package/runtime/ext_gui/dom_api_proto.cpp +35 -0
- package/runtime/ext_gui/dom_api_style.cpp +223 -13
- package/runtime/ext_gui/dom_api_webgl.cpp +10 -0
- package/runtime/ext_gui/dom_api_webgl2.cpp +33 -1
- package/runtime/ext_gui/gui.cpp +59 -5
- package/runtime/ext_gui/gui_engine.h +49 -0
- package/runtime/ext_gui/gui_events.cpp +80 -9
- package/runtime/ext_gui/listener_options.h +43 -0
- package/runtime/ext_gui/paint.cpp +28 -0
- package/runtime/ext_gui/paint.h +4 -0
- package/runtime/ext_gui/script.cpp +31 -0
- package/runtime/ext_gui/webgl_glsl.h +3 -0
- package/runtime/ext_gui/webgl_glsl_unroll.cpp +283 -0
- package/runtime/ext_gui/webgl_msl.cpp +4 -0
- package/runtime/ext_gui/webgl_shader.cpp +12 -6
- package/runtime/ext_gui/window.cpp +14 -65
- package/runtime/ext_gui/window_events.cpp +125 -0
- package/runtime/rt.h +29 -0
- package/runtime/rt_internal.h +4 -0
- package/runtime/xt_builtins.c +0 -21
- package/runtime/xt_containers/access.inc +94 -2
- package/runtime/xt_containers/objects.inc +29 -31
- package/runtime/xt_fetch/async.inc +60 -0
- package/runtime/xt_url_object.c +463 -0
- package/src/codegen/generator/state.ts +1 -0
- package/src/codegen/generator/tables.ts +1 -0
- package/src/driver/bundler/deferred.ts +351 -0
- package/src/driver/bundler/merge.ts +219 -2
- package/src/driver/runtime-objects.ts +1 -0
- package/src/driver/version.ts +1 -1
- package/src/extensions/gui/html.ts +91 -15
- package/src/extensions/gui/index.ts +13 -0
- package/src/extensions/gui/three.ts +4 -3
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — live `matchMedia` lists.
|
|
3
|
+
*
|
|
4
|
+
* `window.matchMedia(query)` returns one cached `MediaQueryList` per query. The
|
|
5
|
+
* engine re-evaluates every live list on a window resize (the same evaluator
|
|
6
|
+
* `@media` rules use) and dispatches a `change` event when the match flips, so
|
|
7
|
+
* a page can react to a viewport change instead of polling `matches`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "gui_engine.h"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
#include "css.h"
|
|
17
|
+
|
|
18
|
+
namespace {
|
|
19
|
+
|
|
20
|
+
std::string string_value(xt_value value) {
|
|
21
|
+
const char *data = xt_string_data(xt_to_string(value));
|
|
22
|
+
return data != nullptr ? data : "";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
xt_value listeners_of(xt_value self, bool create) {
|
|
26
|
+
xt_value listeners = xt_object_get_cstr(self, "__xt_gui_media_listeners");
|
|
27
|
+
if (XT_IS_ARRAY(listeners) || !create) return listeners;
|
|
28
|
+
listeners = xt_array_new(0, nullptr);
|
|
29
|
+
xt_object_set(self, xt_string_from_cstr("__xt_gui_media_listeners"), listeners);
|
|
30
|
+
return listeners;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
void add_listener(xt_value self, xt_value fn, bool once, xt_value signal) {
|
|
34
|
+
if (!XT_IS_FUNCTION(fn)) return;
|
|
35
|
+
xt_value entry = xt_object_new();
|
|
36
|
+
xt_object_set(entry, xt_string_from_cstr("fn"), fn);
|
|
37
|
+
xt_object_set(entry, xt_string_from_cstr("once"), once ? XT_TRUE : XT_FALSE);
|
|
38
|
+
xt_object_set(entry, xt_string_from_cstr("signal"), signal);
|
|
39
|
+
xt_array_push(listeners_of(self, true), entry);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
void remove_listener(xt_value self, xt_value fn) {
|
|
43
|
+
xt_value listeners = listeners_of(self, false);
|
|
44
|
+
if (!XT_IS_ARRAY(listeners)) return;
|
|
45
|
+
xt_value kept = xt_array_new(0, nullptr);
|
|
46
|
+
int32_t count = (int32_t)xt_to_number(xt_array_length(listeners));
|
|
47
|
+
for (int32_t i = 0; i < count; i++) {
|
|
48
|
+
xt_value entry = xt_array_get(listeners, xt_number((double)i));
|
|
49
|
+
if (!xt_truthy(xt_seq(xt_object_get_cstr(entry, "fn"), fn))) xt_array_push(kept, entry);
|
|
50
|
+
}
|
|
51
|
+
xt_object_set(self, xt_string_from_cstr("__xt_gui_media_listeners"), kept);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
void call_change_listener(xt_value fn, xt_value list, xt_value event) {
|
|
55
|
+
if (!XT_IS_FUNCTION(fn)) return;
|
|
56
|
+
xt_value args[1] = {event};
|
|
57
|
+
xt_call_with_this(fn, list, 1, args);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
|
|
61
|
+
|
|
62
|
+
METHOD(media_add_event_listener) {
|
|
63
|
+
(void)env;
|
|
64
|
+
if (argc < 2 || string_value(xt_arg(argc, argv, 0)) != "change") return XT_UNDEFINED;
|
|
65
|
+
bool once = false;
|
|
66
|
+
xt_value options = xt_arg(argc, argv, 2);
|
|
67
|
+
if (XT_IS_OBJECT(options)) once = xt_truthy(xt_object_get_cstr(options, "once")) != 0;
|
|
68
|
+
add_listener(self, xt_arg(argc, argv, 1), once, XT_UNDEFINED);
|
|
69
|
+
return XT_UNDEFINED;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
METHOD(media_remove_event_listener) {
|
|
73
|
+
(void)env;
|
|
74
|
+
if (argc < 2 || string_value(xt_arg(argc, argv, 0)) != "change") return XT_UNDEFINED;
|
|
75
|
+
remove_listener(self, xt_arg(argc, argv, 1));
|
|
76
|
+
return XT_UNDEFINED;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Legacy `addListener(fn)` / `removeListener(fn)`. */
|
|
80
|
+
METHOD(media_add_listener) {
|
|
81
|
+
(void)env;
|
|
82
|
+
if (argc >= 1) add_listener(self, xt_arg(argc, argv, 0), false, XT_UNDEFINED);
|
|
83
|
+
return XT_UNDEFINED;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
METHOD(media_remove_listener) {
|
|
87
|
+
(void)env;
|
|
88
|
+
if (argc >= 1) remove_listener(self, xt_arg(argc, argv, 0));
|
|
89
|
+
return XT_UNDEFINED;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
METHOD(media_onchange_get) {
|
|
93
|
+
(void)env;
|
|
94
|
+
(void)argc;
|
|
95
|
+
(void)argv;
|
|
96
|
+
return xt_object_get_cstr(self, "__xt_gui_media_onchange");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
METHOD(media_onchange_set) {
|
|
100
|
+
(void)env;
|
|
101
|
+
if (argc >= 1) {
|
|
102
|
+
xt_object_set(self, xt_string_from_cstr("__xt_gui_media_onchange"), xt_arg(argc, argv, 0));
|
|
103
|
+
}
|
|
104
|
+
return XT_UNDEFINED;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#undef METHOD
|
|
108
|
+
|
|
109
|
+
xt_value build_list(const std::string &query, bool matches) {
|
|
110
|
+
xt_value list = xt_object_new();
|
|
111
|
+
xt_object_set(list, xt_string_from_cstr("media"), xt_string_from_cstr(query.c_str()));
|
|
112
|
+
xt_object_set(list, xt_string_from_cstr("matches"), matches ? XT_TRUE : XT_FALSE);
|
|
113
|
+
xt_object_set(list, xt_string_from_cstr("__xt_gui_media_query"),
|
|
114
|
+
xt_string_from_cstr(query.c_str()));
|
|
115
|
+
xt_object_set(list, xt_string_from_cstr("addEventListener"),
|
|
116
|
+
xt_closure_new((void *)media_add_event_listener, 0, nullptr));
|
|
117
|
+
xt_object_set(list, xt_string_from_cstr("removeEventListener"),
|
|
118
|
+
xt_closure_new((void *)media_remove_event_listener, 0, nullptr));
|
|
119
|
+
xt_object_set(list, xt_string_from_cstr("addListener"),
|
|
120
|
+
xt_closure_new((void *)media_add_listener, 0, nullptr));
|
|
121
|
+
xt_object_set(list, xt_string_from_cstr("removeListener"),
|
|
122
|
+
xt_closure_new((void *)media_remove_listener, 0, nullptr));
|
|
123
|
+
xt_object_define_getter(list, xt_string_from_cstr("onchange"),
|
|
124
|
+
xt_closure_new((void *)media_onchange_get, 0, nullptr));
|
|
125
|
+
xt_object_define_setter(list, xt_string_from_cstr("onchange"),
|
|
126
|
+
xt_closure_new((void *)media_onchange_set, 0, nullptr));
|
|
127
|
+
return list;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
} // namespace
|
|
131
|
+
|
|
132
|
+
xt_value xt_browser_media_list(xt_value window, const std::string &query) {
|
|
133
|
+
XtGuiWindow *win = xt_gui_window_from_this(window);
|
|
134
|
+
if (win == nullptr) return build_list(query, false);
|
|
135
|
+
auto cached = win->media_lists_by_query.find(query);
|
|
136
|
+
if (cached != win->media_lists_by_query.end()) return cached->second;
|
|
137
|
+
float width = 0.0f;
|
|
138
|
+
float height = 0.0f;
|
|
139
|
+
xt_gui_window_viewport(win, &width, &height);
|
|
140
|
+
xt_value list = build_list(query, xtgui::xt_css_media_matches(query, width, height));
|
|
141
|
+
win->media_lists.push_back(list);
|
|
142
|
+
win->media_lists_by_query[query] = list;
|
|
143
|
+
return list;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
void xt_gui_media_lists_notify(XtGuiWindow *win) {
|
|
147
|
+
if (win == nullptr) return;
|
|
148
|
+
float width = 0.0f;
|
|
149
|
+
float height = 0.0f;
|
|
150
|
+
xt_gui_window_viewport(win, &width, &height);
|
|
151
|
+
for (xt_value list : win->media_lists) {
|
|
152
|
+
if (!XT_IS_OBJECT(list)) continue;
|
|
153
|
+
std::string query = string_value(xt_object_get_cstr(list, "__xt_gui_media_query"));
|
|
154
|
+
bool matches = xtgui::xt_css_media_matches(query, width, height);
|
|
155
|
+
bool was = xt_truthy(xt_object_get_cstr(list, "matches"));
|
|
156
|
+
xt_object_set(list, xt_string_from_cstr("matches"), matches ? XT_TRUE : XT_FALSE);
|
|
157
|
+
if (was == matches) continue;
|
|
158
|
+
xt_value event = xt_object_new();
|
|
159
|
+
xt_object_set(event, xt_string_from_cstr("type"), xt_string_from_cstr("change"));
|
|
160
|
+
xt_object_set(event, xt_string_from_cstr("media"), xt_string_from_cstr(query.c_str()));
|
|
161
|
+
xt_object_set(event, xt_string_from_cstr("matches"), matches ? XT_TRUE : XT_FALSE);
|
|
162
|
+
xt_value onchange = xt_object_get_cstr(list, "__xt_gui_media_onchange");
|
|
163
|
+
call_change_listener(onchange, list, event);
|
|
164
|
+
xt_value listeners = listeners_of(list, false);
|
|
165
|
+
if (!XT_IS_ARRAY(listeners)) continue;
|
|
166
|
+
xt_value snapshot = xt_array_new(0, nullptr);
|
|
167
|
+
int32_t count = (int32_t)xt_to_number(xt_array_length(listeners));
|
|
168
|
+
for (int32_t i = 0; i < count; i++) {
|
|
169
|
+
xt_array_push(snapshot, xt_array_get(listeners, xt_number((double)i)));
|
|
170
|
+
}
|
|
171
|
+
for (int32_t i = 0; i < count; i++) {
|
|
172
|
+
xt_value entry = xt_array_get(snapshot, xt_number((double)i));
|
|
173
|
+
call_change_listener(xt_object_get_cstr(entry, "fn"), list, event);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — window navigation and focus.
|
|
3
|
+
*
|
|
4
|
+
* `scrollTo` / `scrollBy` move the page viewport (painting and hit testing
|
|
5
|
+
* subtract the offset), `scrollX`/`scrollY` (and the `page*Offset` aliases)
|
|
6
|
+
* report it, and `focus` / `blur` raise the OS window and toggle
|
|
7
|
+
* `document.hasFocus()` plus the corresponding window events.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "gui_engine.h"
|
|
11
|
+
|
|
12
|
+
#include <cmath>
|
|
13
|
+
|
|
14
|
+
namespace {
|
|
15
|
+
|
|
16
|
+
xt_value scroll_axis(xt_value options, const char *name) {
|
|
17
|
+
if (!XT_IS_OBJECT(options)) return XT_UNDEFINED;
|
|
18
|
+
return xt_object_get_cstr(options, name);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
double coordinate(xt_value value, double fallback) {
|
|
22
|
+
return XT_IS_NUMBER(value) ? xt_to_number(value) : fallback;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
|
|
26
|
+
|
|
27
|
+
/** `scrollTo(x, y)` / `scrollTo({ left, top })`. */
|
|
28
|
+
METHOD(win_scroll_to) {
|
|
29
|
+
(void)env;
|
|
30
|
+
XtGuiWindow *win = xt_gui_window_from_this(self);
|
|
31
|
+
if (win == nullptr || argc < 1) return XT_UNDEFINED;
|
|
32
|
+
xt_value options = xt_arg(argc, argv, 0);
|
|
33
|
+
double x = 0;
|
|
34
|
+
double y = 0;
|
|
35
|
+
if (XT_IS_OBJECT(options) && !XT_IS_ARRAY(options)) {
|
|
36
|
+
x = coordinate(scroll_axis(options, "left"), coordinate(scroll_axis(options, "x"), 0));
|
|
37
|
+
y = coordinate(scroll_axis(options, "top"), coordinate(scroll_axis(options, "y"), 0));
|
|
38
|
+
} else {
|
|
39
|
+
x = coordinate(options, 0);
|
|
40
|
+
if (argc >= 2) y = coordinate(xt_arg(argc, argv, 1), 0);
|
|
41
|
+
}
|
|
42
|
+
xt_gui_scroll_to(win, (float)x, (float)y);
|
|
43
|
+
return XT_UNDEFINED;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
METHOD(win_scroll_by) {
|
|
47
|
+
(void)env;
|
|
48
|
+
XtGuiWindow *win = xt_gui_window_from_this(self);
|
|
49
|
+
if (win == nullptr || argc < 1) return XT_UNDEFINED;
|
|
50
|
+
xt_value options = xt_arg(argc, argv, 0);
|
|
51
|
+
double dx = 0;
|
|
52
|
+
double dy = 0;
|
|
53
|
+
if (XT_IS_OBJECT(options) && !XT_IS_ARRAY(options)) {
|
|
54
|
+
dx = coordinate(scroll_axis(options, "left"), coordinate(scroll_axis(options, "x"), 0));
|
|
55
|
+
dy = coordinate(scroll_axis(options, "top"), coordinate(scroll_axis(options, "y"), 0));
|
|
56
|
+
} else {
|
|
57
|
+
dx = coordinate(options, 0);
|
|
58
|
+
if (argc >= 2) dy = coordinate(xt_arg(argc, argv, 1), 0);
|
|
59
|
+
}
|
|
60
|
+
xt_gui_scroll_to(win, win->scroll_x + (float)dx, win->scroll_y + (float)dy);
|
|
61
|
+
return XT_UNDEFINED;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
METHOD(win_scroll_x_get) {
|
|
65
|
+
(void)env;
|
|
66
|
+
(void)argc;
|
|
67
|
+
(void)argv;
|
|
68
|
+
XtGuiWindow *win = xt_gui_window_from_this(self);
|
|
69
|
+
return xt_number(win != nullptr ? (double)win->scroll_x : 0.0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
METHOD(win_scroll_y_get) {
|
|
73
|
+
(void)env;
|
|
74
|
+
(void)argc;
|
|
75
|
+
(void)argv;
|
|
76
|
+
XtGuiWindow *win = xt_gui_window_from_this(self);
|
|
77
|
+
return xt_number(win != nullptr ? (double)win->scroll_y : 0.0);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
METHOD(win_focus) {
|
|
81
|
+
(void)env;
|
|
82
|
+
(void)argc;
|
|
83
|
+
(void)argv;
|
|
84
|
+
XtGuiWindow *win = xt_gui_window_from_this(self);
|
|
85
|
+
if (win == nullptr || !win->open) return XT_UNDEFINED;
|
|
86
|
+
if (win->window != nullptr) SDL_RaiseWindow(win->window);
|
|
87
|
+
if (!win->focused) {
|
|
88
|
+
win->focused = 1;
|
|
89
|
+
xt_gui_emit(win, "focus");
|
|
90
|
+
}
|
|
91
|
+
return XT_UNDEFINED;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
METHOD(win_blur) {
|
|
95
|
+
(void)env;
|
|
96
|
+
(void)argc;
|
|
97
|
+
(void)argv;
|
|
98
|
+
XtGuiWindow *win = xt_gui_window_from_this(self);
|
|
99
|
+
if (win == nullptr || !win->open) return XT_UNDEFINED;
|
|
100
|
+
if (win->focused) {
|
|
101
|
+
win->focused = 0;
|
|
102
|
+
xt_gui_emit(win, "blur");
|
|
103
|
+
}
|
|
104
|
+
return XT_UNDEFINED;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#undef METHOD
|
|
108
|
+
|
|
109
|
+
} // namespace
|
|
110
|
+
|
|
111
|
+
void xt_browser_install_window_navigation(xt_value proto) {
|
|
112
|
+
xt_object_set(proto, xt_string_from_cstr("scrollTo"),
|
|
113
|
+
xt_closure_new((void *)win_scroll_to, 0, nullptr));
|
|
114
|
+
xt_object_set(proto, xt_string_from_cstr("scroll"),
|
|
115
|
+
xt_closure_new((void *)win_scroll_to, 0, nullptr));
|
|
116
|
+
xt_object_set(proto, xt_string_from_cstr("scrollBy"),
|
|
117
|
+
xt_closure_new((void *)win_scroll_by, 0, nullptr));
|
|
118
|
+
xt_object_set(proto, xt_string_from_cstr("focus"),
|
|
119
|
+
xt_closure_new((void *)win_focus, 0, nullptr));
|
|
120
|
+
xt_object_set(proto, xt_string_from_cstr("blur"),
|
|
121
|
+
xt_closure_new((void *)win_blur, 0, nullptr));
|
|
122
|
+
xt_object_define_getter(proto, xt_string_from_cstr("scrollX"),
|
|
123
|
+
xt_closure_new((void *)win_scroll_x_get, 0, nullptr));
|
|
124
|
+
xt_object_define_getter(proto, xt_string_from_cstr("scrollY"),
|
|
125
|
+
xt_closure_new((void *)win_scroll_y_get, 0, nullptr));
|
|
126
|
+
xt_object_define_getter(proto, xt_string_from_cstr("pageXOffset"),
|
|
127
|
+
xt_closure_new((void *)win_scroll_x_get, 0, nullptr));
|
|
128
|
+
xt_object_define_getter(proto, xt_string_from_cstr("pageYOffset"),
|
|
129
|
+
xt_closure_new((void *)win_scroll_y_get, 0, nullptr));
|
|
130
|
+
}
|
|
@@ -85,9 +85,20 @@ void Canvas2D::resize(int width, int height) {
|
|
|
85
85
|
image_.width = width;
|
|
86
86
|
image_.height = height;
|
|
87
87
|
image_.pixels.assign((size_t)width * (size_t)height * 4, 0);
|
|
88
|
+
if (!alpha_) {
|
|
89
|
+
/* `{ alpha: false }`: the canvas has an opaque black backdrop, so cleared
|
|
90
|
+
* pixels are black rather than transparent. */
|
|
91
|
+
for (size_t i = 3; i < image_.pixels.size(); i += 4) image_.pixels[i] = 255;
|
|
92
|
+
}
|
|
88
93
|
generation_++;
|
|
89
94
|
}
|
|
90
95
|
|
|
96
|
+
void Canvas2D::set_alpha(bool alpha) {
|
|
97
|
+
if (alpha_ == alpha) return;
|
|
98
|
+
alpha_ = alpha;
|
|
99
|
+
resize(image_.width, image_.height);
|
|
100
|
+
}
|
|
101
|
+
|
|
91
102
|
void Canvas2D::save() { stack_.push_back(state_); }
|
|
92
103
|
|
|
93
104
|
void Canvas2D::restore() {
|
|
@@ -214,7 +225,8 @@ void Canvas2D::ellipse(float cx, float cy, float rx, float ry, float rotation, f
|
|
|
214
225
|
append_path_points(points);
|
|
215
226
|
}
|
|
216
227
|
|
|
217
|
-
void Canvas2D::round_rect(float x, float y, float width, float height, float
|
|
228
|
+
void Canvas2D::round_rect(float x, float y, float width, float height, float radius_tl,
|
|
229
|
+
float radius_tr, float radius_br, float radius_bl) {
|
|
218
230
|
if (width < 0.0f) {
|
|
219
231
|
x += width;
|
|
220
232
|
width = -width;
|
|
@@ -223,18 +235,25 @@ void Canvas2D::round_rect(float x, float y, float width, float height, float rad
|
|
|
223
235
|
y += height;
|
|
224
236
|
height = -height;
|
|
225
237
|
}
|
|
226
|
-
float maxRadius = std::min(width, height) * 0.5f;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
+
const float maxRadius = std::min(width, height) * 0.5f;
|
|
239
|
+
float radii[4] = {radius_tl, radius_tr, radius_br, radius_bl};
|
|
240
|
+
for (float &radius : radii) {
|
|
241
|
+
if (radius < 0.0f) radius = 0.0f;
|
|
242
|
+
if (radius > maxRadius) radius = maxRadius;
|
|
243
|
+
}
|
|
244
|
+
const float tl = radii[0];
|
|
245
|
+
const float tr = radii[1];
|
|
246
|
+
const float br = radii[2];
|
|
247
|
+
const float bl = radii[3];
|
|
248
|
+
move_to(x + tl, y);
|
|
249
|
+
line_to(x + width - tr, y);
|
|
250
|
+
quadratic_curve_to(x + width, y, x + width, y + tr);
|
|
251
|
+
line_to(x + width, y + height - br);
|
|
252
|
+
quadratic_curve_to(x + width, y + height, x + width - br, y + height);
|
|
253
|
+
line_to(x + bl, y + height);
|
|
254
|
+
quadratic_curve_to(x, y + height, x, y + height - bl);
|
|
255
|
+
line_to(x, y + tl);
|
|
256
|
+
quadratic_curve_to(x, y, x + tl, y);
|
|
238
257
|
close_path();
|
|
239
258
|
}
|
|
240
259
|
|
|
@@ -274,9 +293,14 @@ void Canvas2D::to_device(float x, float y, float *outX, float *outY) const {
|
|
|
274
293
|
}
|
|
275
294
|
|
|
276
295
|
std::vector<CanvasSubpath> Canvas2D::device_subpaths() const {
|
|
296
|
+
return device_subpaths_of(path_);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
std::vector<CanvasSubpath> Canvas2D::device_subpaths_of(
|
|
300
|
+
const std::vector<CanvasSubpath> &paths) const {
|
|
277
301
|
std::vector<CanvasSubpath> result;
|
|
278
|
-
result.reserve(
|
|
279
|
-
for (const CanvasSubpath &subpath :
|
|
302
|
+
result.reserve(paths.size());
|
|
303
|
+
for (const CanvasSubpath &subpath : paths) {
|
|
280
304
|
CanvasSubpath device;
|
|
281
305
|
device.reserve(subpath.size());
|
|
282
306
|
for (const CanvasPoint &point : subpath) {
|
|
@@ -289,23 +313,25 @@ std::vector<CanvasSubpath> Canvas2D::device_subpaths() const {
|
|
|
289
313
|
return result;
|
|
290
314
|
}
|
|
291
315
|
|
|
292
|
-
CanvasPaint Canvas2D::make_paint(uint64_t gradient_id,
|
|
316
|
+
CanvasPaint Canvas2D::make_paint(uint64_t gradient_id, uint64_t pattern_id,
|
|
317
|
+
const float color[4]) const {
|
|
293
318
|
CanvasPaint paint;
|
|
294
319
|
paint.color = color;
|
|
295
320
|
paint.gradient = gradient_id == 0 ? nullptr : find_gradient(gradient_id);
|
|
321
|
+
paint.pattern = pattern_id == 0 ? nullptr : find_pattern(pattern_id);
|
|
296
322
|
paint.inverse = state_.transform.inverted();
|
|
297
323
|
return paint;
|
|
298
324
|
}
|
|
299
325
|
|
|
300
326
|
CanvasPaint Canvas2D::fill_paint() const {
|
|
301
|
-
return make_paint(state_.fill_gradient, state_.fill_color);
|
|
327
|
+
return make_paint(state_.fill_gradient, state_.fill_pattern, state_.fill_color);
|
|
302
328
|
}
|
|
303
329
|
|
|
304
330
|
CanvasPaint Canvas2D::stroke_paint() const {
|
|
305
|
-
return make_paint(state_.stroke_gradient, state_.stroke_color);
|
|
331
|
+
return make_paint(state_.stroke_gradient, state_.stroke_pattern, state_.stroke_color);
|
|
306
332
|
}
|
|
307
333
|
|
|
308
|
-
void Canvas2D::fill() {
|
|
334
|
+
void Canvas2D::fill(bool even_odd) {
|
|
309
335
|
std::vector<CanvasSubpath> subpaths = device_subpaths();
|
|
310
336
|
std::vector<CanvasSubpath> loops;
|
|
311
337
|
for (CanvasSubpath &subpath : subpaths) {
|
|
@@ -314,7 +340,20 @@ void Canvas2D::fill() {
|
|
|
314
340
|
}
|
|
315
341
|
if (loops.empty()) return;
|
|
316
342
|
canvas_detail::fill_loops(image_.pixels, image_.width, image_.height, loops, fill_paint(),
|
|
317
|
-
state_.global_alpha);
|
|
343
|
+
state_.global_alpha, even_odd);
|
|
344
|
+
generation_++;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
void Canvas2D::fill_paths(const std::vector<CanvasSubpath> &paths, bool even_odd) {
|
|
348
|
+
std::vector<CanvasSubpath> subpaths = device_subpaths_of(paths);
|
|
349
|
+
std::vector<CanvasSubpath> loops;
|
|
350
|
+
for (CanvasSubpath &subpath : subpaths) {
|
|
351
|
+
if (subpath.size() < 3) continue;
|
|
352
|
+
loops.push_back(std::move(subpath));
|
|
353
|
+
}
|
|
354
|
+
if (loops.empty()) return;
|
|
355
|
+
canvas_detail::fill_loops(image_.pixels, image_.width, image_.height, loops, fill_paint(),
|
|
356
|
+
state_.global_alpha, even_odd);
|
|
318
357
|
generation_++;
|
|
319
358
|
}
|
|
320
359
|
|
|
@@ -330,6 +369,18 @@ void Canvas2D::stroke() {
|
|
|
330
369
|
generation_++;
|
|
331
370
|
}
|
|
332
371
|
|
|
372
|
+
void Canvas2D::stroke_paths(const std::vector<CanvasSubpath> &paths) {
|
|
373
|
+
std::vector<CanvasSubpath> subpaths = device_subpaths_of(paths);
|
|
374
|
+
const CanvasPaint paint = stroke_paint();
|
|
375
|
+
for (const CanvasSubpath &subpath : subpaths) {
|
|
376
|
+
if (subpath.size() < 2) continue;
|
|
377
|
+
bool closed = subpath.size() >= 3 && subpath.front().x == subpath.back().x &&
|
|
378
|
+
subpath.front().y == subpath.back().y;
|
|
379
|
+
stroke_subpath(subpath, closed, paint);
|
|
380
|
+
}
|
|
381
|
+
generation_++;
|
|
382
|
+
}
|
|
383
|
+
|
|
333
384
|
void Canvas2D::clear_rect(float x, float y, float width, float height) {
|
|
334
385
|
float x0, y0, x1, y1;
|
|
335
386
|
to_device(x, y, &x0, &y0);
|
|
@@ -378,7 +429,7 @@ float Canvas2D::measure_text(const std::string &text) const {
|
|
|
378
429
|
}
|
|
379
430
|
|
|
380
431
|
void Canvas2D::paint_text(const std::string &text, float x, float y, const CanvasPaint &paint,
|
|
381
|
-
bool stroke) {
|
|
432
|
+
bool stroke, float max_width) {
|
|
382
433
|
Font *font = xt_text_resolve(state_.font);
|
|
383
434
|
if (font == nullptr) return;
|
|
384
435
|
std::vector<ShapedGlyph> glyphs;
|
|
@@ -386,11 +437,18 @@ void Canvas2D::paint_text(const std::string &text, float x, float y, const Canva
|
|
|
386
437
|
float ascent = 0.0f, descent = 0.0f, lineHeight = 0.0f;
|
|
387
438
|
xt_text_metrics(state_.font, &ascent, &descent, &lineHeight);
|
|
388
439
|
|
|
440
|
+
/* The optional `maxWidth` condenses the run horizontally instead of clipping
|
|
441
|
+
* it: advances and glyph bitmaps are squeezed by the same factor. */
|
|
442
|
+
float horizontal = 1.0f;
|
|
443
|
+
if (max_width > 0.0f && width > max_width && width > 0.0001f) {
|
|
444
|
+
horizontal = max_width / width;
|
|
445
|
+
}
|
|
446
|
+
|
|
389
447
|
float originX = x;
|
|
390
448
|
if (state_.text_align == 1) {
|
|
391
|
-
originX -= width * 0.5f;
|
|
449
|
+
originX -= width * horizontal * 0.5f;
|
|
392
450
|
} else if (state_.text_align == 2) {
|
|
393
|
-
originX -= width;
|
|
451
|
+
originX -= width * horizontal;
|
|
394
452
|
}
|
|
395
453
|
float originY = y;
|
|
396
454
|
switch (state_.text_baseline) {
|
|
@@ -419,7 +477,7 @@ void Canvas2D::paint_text(const std::string &text, float x, float y, const Canva
|
|
|
419
477
|
|
|
420
478
|
float penX = 0.0f;
|
|
421
479
|
for (const ShapedGlyph &glyph : glyphs) {
|
|
422
|
-
float userX = originX + penX + glyph.x_offset;
|
|
480
|
+
float userX = originX + (penX + glyph.x_offset) * horizontal;
|
|
423
481
|
float userY = originY + glyph.y_offset;
|
|
424
482
|
float deviceX = 0.0f, deviceY = 0.0f;
|
|
425
483
|
to_device(userX, userY, &deviceX, &deviceY);
|
|
@@ -431,8 +489,8 @@ void Canvas2D::paint_text(const std::string &text, float x, float y, const Canva
|
|
|
431
489
|
GlyphImage image;
|
|
432
490
|
if (xt_text_rasterize(face, glyph.glyph, image) && image.width > 0 && image.height > 0) {
|
|
433
491
|
canvas_detail::blit_glyph(image_.pixels, image_.width, image_.height, image.pixels, image.width,
|
|
434
|
-
image.height, image.left, image.top, deviceX, deviceY,
|
|
435
|
-
state_.global_alpha);
|
|
492
|
+
image.height, image.left, image.top, deviceX, deviceY, horizontal,
|
|
493
|
+
paint, state_.global_alpha);
|
|
436
494
|
}
|
|
437
495
|
penX += glyph.x_advance;
|
|
438
496
|
}
|
|
@@ -440,12 +498,12 @@ void Canvas2D::paint_text(const std::string &text, float x, float y, const Canva
|
|
|
440
498
|
generation_++;
|
|
441
499
|
}
|
|
442
500
|
|
|
443
|
-
void Canvas2D::fill_text(const std::string &text, float x, float y) {
|
|
444
|
-
paint_text(text, x, y, fill_paint(), false);
|
|
501
|
+
void Canvas2D::fill_text(const std::string &text, float x, float y, float max_width) {
|
|
502
|
+
paint_text(text, x, y, fill_paint(), false, max_width);
|
|
445
503
|
}
|
|
446
504
|
|
|
447
|
-
void Canvas2D::stroke_text(const std::string &text, float x, float y) {
|
|
448
|
-
paint_text(text, x, y, stroke_paint(), true);
|
|
505
|
+
void Canvas2D::stroke_text(const std::string &text, float x, float y, float max_width) {
|
|
506
|
+
paint_text(text, x, y, stroke_paint(), true, max_width);
|
|
449
507
|
}
|
|
450
508
|
|
|
451
509
|
void Canvas2D::draw_image(const Image &source, float dx, float dy, float dw, float dh, float sx,
|