xbintsc 0.5.31 → 0.5.36
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/README.md +25 -16
- package/README.zh-CN.md +10 -8
- package/dist/scripts/check-version.d.ts +27 -0
- package/dist/scripts/check-version.js +71 -0
- package/dist/scripts/check-version.js.map +1 -0
- package/dist/scripts/package-platform.d.ts +58 -0
- package/dist/scripts/package-platform.js +142 -0
- package/dist/scripts/package-platform.js.map +1 -0
- package/dist/scripts/verify-platform-package.d.ts +34 -0
- package/dist/scripts/verify-platform-package.js +161 -0
- package/dist/scripts/verify-platform-package.js.map +1 -0
- package/dist/src/binder/references.js +14 -1
- package/dist/src/binder/references.js.map +1 -1
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/cli/main.js +3 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/module.d.ts +1 -0
- package/dist/src/codegen/generator/module.js +54 -0
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/statements/variables.js +6 -4
- package/dist/src/codegen/generator/statements/variables.js.map +1 -1
- package/dist/src/driver/runtime-lib.d.ts +29 -4
- package/dist/src/driver/runtime-lib.js +68 -10
- package/dist/src/driver/runtime-lib.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/css.d.ts +26 -0
- package/dist/src/extensions/gui/css.js +100 -0
- package/dist/src/extensions/gui/css.js.map +1 -0
- package/dist/src/extensions/gui/html.js +9 -2
- package/dist/src/extensions/gui/html.js.map +1 -1
- package/dist/src/extensions/gui/index.js +22 -7
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/gui/three.d.ts +16 -0
- package/dist/src/extensions/gui/three.js +22 -0
- package/dist/src/extensions/gui/three.js.map +1 -0
- package/dist/src/extensions/media/index.d.ts +6 -5
- package/dist/src/extensions/media/index.js +15 -16
- package/dist/src/extensions/media/index.js.map +1 -1
- package/dist/src/parser/context.d.ts +41 -0
- package/dist/src/parser/context.js +89 -4
- package/dist/src/parser/context.js.map +1 -1
- package/dist/src/parser/literals.js +6 -12
- package/dist/src/parser/literals.js.map +1 -1
- package/dist/tests/driver/platform-package.test.d.ts +1 -0
- package/dist/tests/driver/platform-package.test.js +49 -0
- package/dist/tests/driver/platform-package.test.js.map +1 -0
- package/dist/tests/driver/runtime-lib.test.js +51 -4
- package/dist/tests/driver/runtime-lib.test.js.map +1 -1
- package/dist/tests/e2e/compile-builtins.test.js +24 -0
- package/dist/tests/e2e/compile-builtins.test.js.map +1 -1
- package/dist/tests/e2e/compile-objects-classes.test.js +23 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
- package/dist/tests/e2e/gui-audio.test.d.ts +14 -0
- package/dist/tests/e2e/gui-audio.test.js +287 -0
- package/dist/tests/e2e/gui-audio.test.js.map +1 -0
- package/dist/tests/e2e/gui-browser.test.js +1 -1
- package/dist/tests/e2e/gui-browser.test.js.map +1 -1
- package/dist/tests/e2e/gui-effects.test.d.ts +6 -0
- package/dist/tests/e2e/gui-effects.test.js +262 -0
- package/dist/tests/e2e/gui-effects.test.js.map +1 -0
- package/dist/tests/e2e/gui-example-webgl.test.d.ts +10 -0
- package/dist/tests/e2e/gui-example-webgl.test.js +75 -0
- package/dist/tests/e2e/gui-example-webgl.test.js.map +1 -0
- package/dist/tests/e2e/gui-example-webgl2.test.d.ts +8 -0
- package/dist/tests/e2e/gui-example-webgl2.test.js +72 -0
- package/dist/tests/e2e/gui-example-webgl2.test.js.map +1 -0
- package/dist/tests/e2e/gui-helpers.d.ts +7 -0
- package/dist/tests/e2e/gui-helpers.js +1 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -1
- package/dist/tests/e2e/gui-layout.test.js +48 -0
- package/dist/tests/e2e/gui-layout.test.js.map +1 -1
- package/dist/tests/e2e/gui-media.test.js +83 -4
- package/dist/tests/e2e/gui-media.test.js.map +1 -1
- package/dist/tests/e2e/gui-style.test.js +55 -0
- package/dist/tests/e2e/gui-style.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl.test.d.ts +11 -0
- package/dist/tests/e2e/gui-webgl.test.js +437 -0
- package/dist/tests/e2e/gui-webgl.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.d.ts +4 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.js +264 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.js +262 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.d.ts +6 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.js +395 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.d.ts +4 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.js +105 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.d.ts +8 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.js +112 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2.test.js +282 -0
- package/dist/tests/e2e/gui-webgl2.test.js.map +1 -0
- package/dist/tests/extensions/gui-index.test.js +1 -0
- package/dist/tests/extensions/gui-index.test.js.map +1 -1
- package/dist/tests/extensions/gui.test.js +41 -0
- package/dist/tests/extensions/gui.test.js.map +1 -1
- package/dist/tests/extensions/media.test.js +8 -3
- package/dist/tests/extensions/media.test.js.map +1 -1
- package/dist/tests/parser/expressions.test.js +30 -0
- package/dist/tests/parser/expressions.test.js.map +1 -1
- package/dist/tests/platform-tarball.test.d.ts +1 -0
- package/dist/tests/platform-tarball.test.js +111 -0
- package/dist/tests/platform-tarball.test.js.map +1 -0
- package/dist/tests/version.test.js +6 -0
- package/dist/tests/version.test.js.map +1 -1
- package/doc/ai/contributing.md +11 -0
- package/doc/ai/extensions.md +35 -12
- package/doc/ai/troubleshooting.md +17 -11
- package/doc/ai/zh-CN/contributing.md +9 -0
- package/doc/ai/zh-CN/extensions.md +30 -10
- package/doc/ai/zh-CN/troubleshooting.md +14 -8
- package/doc/gui/api.md +63 -14
- package/doc/gui/build.md +79 -16
- package/doc/gui/canvas.md +11 -3
- package/doc/gui/dom.md +35 -17
- package/doc/gui/known-issues.md +57 -24
- package/doc/gui/layout.md +18 -0
- package/doc/gui/media.md +21 -13
- package/doc/gui/rendering.md +50 -3
- package/doc/gui/webgl.md +315 -0
- package/doc/gui.md +113 -18
- package/doc/npm-package.md +59 -29
- package/doc/requirements.md +24 -11
- package/doc/zh-CN/gui/api.md +53 -11
- package/doc/zh-CN/gui/build.md +70 -16
- package/doc/zh-CN/gui/canvas.md +9 -2
- package/doc/zh-CN/gui/dom.md +22 -7
- package/doc/zh-CN/gui/known-issues.md +45 -19
- package/doc/zh-CN/gui/layout.md +14 -0
- package/doc/zh-CN/gui/media.md +18 -11
- package/doc/zh-CN/gui/rendering.md +41 -4
- package/doc/zh-CN/gui/webgl.md +121 -0
- package/doc/zh-CN/gui.md +91 -15
- package/doc/zh-CN/npm-package.md +48 -24
- package/doc/zh-CN/requirements.md +16 -7
- package/llms.txt +2 -0
- package/package.json +17 -1
- package/runtime/ext_gui/audio.cpp +365 -153
- package/runtime/ext_gui/audio_buffer.cpp +166 -0
- package/runtime/ext_gui/audio_buffer.h +37 -0
- package/runtime/ext_gui/audio_decode.cpp +219 -0
- package/runtime/ext_gui/audio_decode.h +39 -0
- package/runtime/ext_gui/audio_device.cpp +87 -0
- package/runtime/ext_gui/audio_effects.cpp +163 -0
- package/runtime/ext_gui/audio_effects.h +25 -0
- package/runtime/ext_gui/audio_internal.h +30 -0
- package/runtime/ext_gui/audio_mixer.cpp +494 -0
- package/runtime/ext_gui/audio_mixer.h +238 -0
- package/runtime/ext_gui/audio_param.cpp +104 -0
- package/runtime/ext_gui/audio_param_object.cpp +155 -0
- package/runtime/ext_gui/browser.cpp +50 -14
- package/runtime/ext_gui/canvas.cpp +3 -18
- package/runtime/ext_gui/canvas_internal.h +10 -0
- package/runtime/ext_gui/canvas_raster.cpp +60 -0
- package/runtime/ext_gui/clip_values.cpp +246 -0
- package/runtime/ext_gui/css.cpp +94 -0
- package/runtime/ext_gui/css.h +22 -1
- package/runtime/ext_gui/css_match.cpp +3 -0
- package/runtime/ext_gui/css_selector.cpp +30 -3
- package/runtime/ext_gui/css_sheet.cpp +89 -45
- package/runtime/ext_gui/document.cpp +4 -1
- package/runtime/ext_gui/document.h +2 -0
- package/runtime/ext_gui/dom.h +16 -0
- package/runtime/ext_gui/dom_api.cpp +1 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +17 -80
- package/runtime/ext_gui/dom_api_canvas_extra.cpp +130 -1
- package/runtime/ext_gui/dom_api_canvas_pending.cpp +41 -0
- package/runtime/ext_gui/dom_api_internal.h +275 -0
- package/runtime/ext_gui/dom_api_webgl.cpp +383 -0
- package/runtime/ext_gui/dom_api_webgl2.cpp +549 -0
- package/runtime/ext_gui/dom_api_webgl2_draw.cpp +269 -0
- package/runtime/ext_gui/dom_api_webgl_buffer.cpp +216 -0
- package/runtime/ext_gui/dom_api_webgl_compressed.cpp +167 -0
- package/runtime/ext_gui/dom_api_webgl_framebuffer.cpp +160 -0
- package/runtime/ext_gui/dom_api_webgl_rt.h +30 -0
- package/runtime/ext_gui/dom_api_webgl_shader.cpp +443 -0
- package/runtime/ext_gui/dom_api_webgl_source.cpp +161 -0
- package/runtime/ext_gui/dom_api_webgl_state.cpp +590 -0
- package/runtime/ext_gui/dom_api_webgl_texture.cpp +485 -0
- package/runtime/ext_gui/dom_api_webgl_ubo.cpp +81 -0
- package/runtime/ext_gui/effect_values.cpp +312 -0
- package/runtime/ext_gui/gradient_values.cpp +476 -0
- package/runtime/ext_gui/grid_values.cpp +230 -0
- package/runtime/ext_gui/gui.cpp +44 -9
- package/runtime/ext_gui/gui_canvas.cpp +50 -2
- package/runtime/ext_gui/gui_engine.h +36 -4
- package/runtime/ext_gui/gui_events.cpp +4 -1
- package/runtime/ext_gui/gui_media.cpp +1 -1
- package/runtime/ext_gui/gui_webgl.cpp +83 -0
- package/runtime/ext_gui/image_bitmap.cpp +28 -0
- package/runtime/ext_gui/image_bitmap.h +26 -0
- package/runtime/ext_gui/image_bitmap_browser.cpp +136 -0
- package/runtime/ext_gui/layout.cpp +21 -0
- package/runtime/ext_gui/layout_flow.cpp +81 -4
- package/runtime/ext_gui/layout_flow_internal.h +31 -0
- package/runtime/ext_gui/layout_grid.cpp +374 -0
- package/runtime/ext_gui/media.cpp +32 -0
- package/runtime/ext_gui/media.h +14 -1
- package/runtime/ext_gui/media_api.h +25 -2
- package/runtime/ext_gui/media_query.cpp +211 -0
- package/runtime/ext_gui/paint.cpp +111 -77
- package/runtime/ext_gui/paint.h +33 -3
- package/runtime/ext_gui/paint_background.cpp +229 -0
- package/runtime/ext_gui/paint_clip.cpp +268 -0
- package/runtime/ext_gui/paint_effects.cpp +324 -0
- package/runtime/ext_gui/paint_internal.h +85 -0
- package/runtime/ext_gui/renderer.cpp +8 -31
- package/runtime/ext_gui/renderer_clip.cpp +196 -0
- package/runtime/ext_gui/renderer_image.cpp +13 -8
- package/runtime/ext_gui/renderer_internal.h +15 -8
- package/runtime/ext_gui/renderer_text.cpp +6 -31
- package/runtime/ext_gui/style.cpp +343 -20
- package/runtime/ext_gui/style.h +144 -3
- package/runtime/ext_gui/style_print.cpp +134 -1
- package/runtime/ext_gui/style_values.cpp +152 -126
- package/runtime/ext_gui/style_values_internal.h +35 -0
- package/runtime/ext_gui/webgl.h +600 -0
- package/runtime/ext_gui/webgl2_constants.h +274 -0
- package/runtime/ext_gui/webgl_blit.cpp +95 -0
- package/runtime/ext_gui/webgl_buffer.cpp +480 -0
- package/runtime/ext_gui/webgl_buffer_objects.cpp +270 -0
- package/runtime/ext_gui/webgl_clear.cpp +142 -0
- package/runtime/ext_gui/webgl_compressed.cpp +180 -0
- package/runtime/ext_gui/webgl_constants.h +317 -0
- package/runtime/ext_gui/webgl_context.cpp +551 -0
- package/runtime/ext_gui/webgl_draw.cpp +560 -0
- package/runtime/ext_gui/webgl_draw_buffers.cpp +75 -0
- package/runtime/ext_gui/webgl_glsl.h +165 -0
- package/runtime/ext_gui/webgl_glsl_compile.cpp +437 -0
- package/runtime/ext_gui/webgl_glsl_rewrite.cpp +367 -0
- package/runtime/ext_gui/webgl_glsl_scan.cpp +590 -0
- package/runtime/ext_gui/webgl_glsl_scan_block.cpp +126 -0
- package/runtime/ext_gui/webgl_glsl_tokens.h +28 -0
- package/runtime/ext_gui/webgl_msl.cpp +62 -0
- package/runtime/ext_gui/webgl_msl.h +33 -0
- package/runtime/ext_gui/webgl_pipeline.cpp +326 -0
- package/runtime/ext_gui/webgl_program_blocks.cpp +58 -0
- package/runtime/ext_gui/webgl_program_internal.h +24 -0
- package/runtime/ext_gui/webgl_shader.cpp +594 -0
- package/runtime/ext_gui/webgl_state.cpp +228 -0
- package/runtime/ext_gui/webgl_texture.cpp +420 -0
- package/runtime/ext_gui/webgl_texture_3d.cpp +215 -0
- package/runtime/ext_gui/webgl_uniform.cpp +167 -0
- package/runtime/ext_gui/webgl_uniform_block.h +31 -0
- package/runtime/ext_gui/webgl_webgl2.cpp +277 -0
- package/runtime/ext_media/decode_audio.cpp +308 -0
- package/runtime/ext_media/media.cpp +1 -1
- package/runtime/ext_media/media_bridge.c +11 -3
- package/runtime/rt.h +17 -0
- package/runtime/rt_internal.h +1 -0
- package/runtime/xt_array_buffer.c +30 -0
- package/runtime/xt_containers/access.inc +8 -0
- package/runtime/xt_regex/api.inc +2 -0
- package/runtime/xt_regex/matcher.inc +16 -3
- package/runtime/xt_regex/parser.inc +8 -2
- package/runtime/xt_regex/types.inc +3 -0
- package/runtime/xt_stdlib2/regexp-match.inc +60 -21
- package/runtime/xt_stdlib2/regexp.inc +1 -0
- package/runtime/xt_typed_array/elements.inc +13 -0
- package/runtime/xt_typed_array.c +1 -1
- package/scripts/build-gui.ts +90 -3
- package/scripts/build-media.ts +335 -62
- package/scripts/build-runtime.ts +21 -16
- package/scripts/check-version.ts +39 -9
- package/scripts/package-platform.ts +183 -0
- package/scripts/set-platform-version.ts +62 -0
- package/scripts/setup-vs-toolchain.ps1 +152 -0
- package/scripts/verify-platform-package.ts +182 -0
- package/src/binder/references.ts +13 -1
- package/src/cli/main.ts +5 -1
- package/src/codegen/generator/module.ts +50 -0
- package/src/codegen/generator/statements/variables.ts +6 -3
- package/src/driver/runtime-lib.ts +73 -9
- package/src/driver/version.ts +1 -1
- package/src/extensions/gui/css.ts +113 -0
- package/src/extensions/gui/html.ts +9 -2
- package/src/extensions/gui/index.ts +22 -7
- package/src/extensions/gui/three.ts +25 -0
- package/src/extensions/media/index.ts +16 -18
- package/src/parser/context.ts +99 -4
- package/src/parser/literals.ts +6 -12
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* The engine knows how to model `<video>`/`<audio>` elements and how to draw
|
|
8
8
|
* their frames, but it deliberately contains no decoder: FFmpeg lives in the
|
|
9
9
|
* separate `media` compile extension (`runtime/ext_media/`, linked as
|
|
10
|
-
* `media.a`). Keeping the boundary C-only means the backend can be a prebuilt
|
|
10
|
+
* `media.a`, or `media.lib` with the MSVC ABI on Windows). Keeping the boundary C-only means the backend can be a prebuilt
|
|
11
11
|
* C++ archive that is compiled independently of the engine.
|
|
12
12
|
*
|
|
13
13
|
* Registration is push-based: the backend's bridge translation unit is linked
|
|
@@ -40,10 +40,30 @@ typedef XtMediaPlayerHandle (*XtMediaByIdFn)(uint64_t id);
|
|
|
40
40
|
/** Tear down every decoder and process-wide media state. */
|
|
41
41
|
typedef void (*XtMediaShutdownFn)(void);
|
|
42
42
|
|
|
43
|
+
/** Interleaved float PCM returned by the memory decoder.
|
|
44
|
+
*
|
|
45
|
+
* `samples` is allocated by the backend and must be released with the table's
|
|
46
|
+
* `free_audio` entry point (both archives use the same static CRT). */
|
|
47
|
+
typedef struct XtMediaAudioBuffer {
|
|
48
|
+
int channels;
|
|
49
|
+
int sample_rate;
|
|
50
|
+
int frames;
|
|
51
|
+
float *samples;
|
|
52
|
+
} XtMediaAudioBuffer;
|
|
53
|
+
|
|
54
|
+
/** Decode an in-memory audio file (`decodeAudioData`). Returns 1 on success and
|
|
55
|
+
* fills `out`; on failure returns 0 and copies a message into `error`. */
|
|
56
|
+
typedef int (*XtMediaDecodeAudioFn)(const unsigned char *data, size_t size,
|
|
57
|
+
XtMediaAudioBuffer *out, char *error, size_t error_size);
|
|
58
|
+
/** Release the `samples` a successful `decode_audio` allocated. */
|
|
59
|
+
typedef void (*XtMediaFreeAudioFn)(void *samples);
|
|
60
|
+
|
|
43
61
|
typedef struct XtMediaApi {
|
|
44
62
|
XtMediaOpenFn open;
|
|
45
63
|
XtMediaByIdFn by_id;
|
|
46
64
|
XtMediaShutdownFn shutdown;
|
|
65
|
+
XtMediaDecodeAudioFn decode_audio;
|
|
66
|
+
XtMediaFreeAudioFn free_audio;
|
|
47
67
|
} XtMediaApi;
|
|
48
68
|
|
|
49
69
|
/** Install a decoder table. Implemented by the GUI engine; called once by the
|
|
@@ -51,12 +71,15 @@ typedef struct XtMediaApi {
|
|
|
51
71
|
void xt_media_install(const XtMediaApi *api);
|
|
52
72
|
|
|
53
73
|
/** Decoder backend entry points, implemented by `runtime/ext_media/media.cpp`
|
|
54
|
-
* (inside `media.a`). The bridge references them, which is what makes the
|
|
74
|
+
* (inside `media.a`/`media.lib`). The bridge references them, which is what makes the
|
|
55
75
|
* linker extract the archive member when the media extension is enabled. */
|
|
56
76
|
XtMediaPlayerHandle xt_media_backend_open(const char *path, char *error,
|
|
57
77
|
size_t error_size, void *audio_sink);
|
|
58
78
|
XtMediaPlayerHandle xt_media_backend_by_id(uint64_t id);
|
|
59
79
|
void xt_media_backend_shutdown(void);
|
|
80
|
+
int xt_media_backend_decode_audio(const unsigned char *data, size_t size,
|
|
81
|
+
XtMediaAudioBuffer *out, char *error, size_t error_size);
|
|
82
|
+
void xt_media_backend_free_audio(void *samples);
|
|
60
83
|
|
|
61
84
|
#ifdef __cplusplus
|
|
62
85
|
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — `@media` query evaluation.
|
|
3
|
+
*
|
|
4
|
+
* The CSS parser keeps each rule's raw media prelude and this unit decides
|
|
5
|
+
* whether it applies to the current viewport. The subset mirrors what a desktop
|
|
6
|
+
* window can answer: width/height ranges, orientation, hover/pointer class and
|
|
7
|
+
* the two accessibility preferences. Unknown features evaluate to false, which
|
|
8
|
+
* is what a browser does (and keeps e.g. `@media print` blocks inert).
|
|
9
|
+
*/
|
|
10
|
+
#include "css_internal.h"
|
|
11
|
+
|
|
12
|
+
#include <cmath>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
namespace xtgui {
|
|
17
|
+
|
|
18
|
+
namespace {
|
|
19
|
+
|
|
20
|
+
std::string lowerTrim(const std::string &text) { return xt_css_lower(xt_css_trim(text)); }
|
|
21
|
+
|
|
22
|
+
/** Split `text` on the word `and` at nesting depth zero. */
|
|
23
|
+
std::vector<std::string> splitAnd(const std::string &text) {
|
|
24
|
+
std::vector<std::string> parts;
|
|
25
|
+
std::string current;
|
|
26
|
+
int depth = 0;
|
|
27
|
+
for (size_t i = 0; i < text.size();) {
|
|
28
|
+
char c = text[i];
|
|
29
|
+
if (c == '(') depth++;
|
|
30
|
+
else if (c == ')') depth--;
|
|
31
|
+
bool atWord = (c == 'a' || c == 'A') && i + 3 <= text.size() &&
|
|
32
|
+
xt_css_lower(text.substr(i, 3)) == "and" &&
|
|
33
|
+
(i == 0 || xt_is_css_space(text[i - 1])) &&
|
|
34
|
+
(i + 3 == text.size() || xt_is_css_space(text[i + 3]));
|
|
35
|
+
if (atWord && depth == 0) {
|
|
36
|
+
std::string part = xt_css_trim(current);
|
|
37
|
+
if (!part.empty()) parts.push_back(part);
|
|
38
|
+
current.clear();
|
|
39
|
+
i += 3;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
current.push_back(c);
|
|
43
|
+
i++;
|
|
44
|
+
}
|
|
45
|
+
std::string part = xt_css_trim(current);
|
|
46
|
+
if (!part.empty()) parts.push_back(part);
|
|
47
|
+
return parts;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Resolve a media-feature length. `rem`/`em` assume the 16px initial font. */
|
|
51
|
+
bool mediaLength(const std::string &raw, float vw, float vh, float *out) {
|
|
52
|
+
Length length = xt_css_parse_length(raw);
|
|
53
|
+
switch (length.unit) {
|
|
54
|
+
case Unit::Px:
|
|
55
|
+
case Unit::Number:
|
|
56
|
+
*out = length.value;
|
|
57
|
+
return true;
|
|
58
|
+
case Unit::Em:
|
|
59
|
+
case Unit::Rem:
|
|
60
|
+
*out = length.value * 16.0f;
|
|
61
|
+
return true;
|
|
62
|
+
case Unit::Vw:
|
|
63
|
+
*out = vw * length.value / 100.0f;
|
|
64
|
+
return true;
|
|
65
|
+
case Unit::Vh:
|
|
66
|
+
*out = vh * length.value / 100.0f;
|
|
67
|
+
return true;
|
|
68
|
+
default:
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Compare `actual` with a feature value, honouring a min-/max- prefix. */
|
|
74
|
+
bool compareLength(const std::string &name, const std::string &rawValue, float actual, float vw,
|
|
75
|
+
float vh) {
|
|
76
|
+
float value = 0.0f;
|
|
77
|
+
if (!mediaLength(rawValue, vw, vh, &value)) return false;
|
|
78
|
+
const bool min = name.rfind("min-", 0) == 0;
|
|
79
|
+
const bool max = name.rfind("max-", 0) == 0;
|
|
80
|
+
if (min) return actual >= value;
|
|
81
|
+
if (max) return actual <= value;
|
|
82
|
+
return std::fabs(actual - value) < 0.01f;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
bool splitFeature(const std::string &inner, std::string *name, std::string *value) {
|
|
86
|
+
int depth = 0;
|
|
87
|
+
for (size_t i = 0; i < inner.size(); i++) {
|
|
88
|
+
char c = inner[i];
|
|
89
|
+
if (c == '(') depth++;
|
|
90
|
+
else if (c == ')') depth--;
|
|
91
|
+
else if (c == ':' && depth == 0) {
|
|
92
|
+
*name = lowerTrim(inner.substr(0, i));
|
|
93
|
+
*value = lowerTrim(inner.substr(i + 1));
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** `(width >= 600px)` / `(600px <= width)` range syntax. */
|
|
101
|
+
bool evaluateRange(const std::string &inner, float vw, float vh, bool *matched) {
|
|
102
|
+
static const char *const ops[] = {">=", "<=", ">", "<", "="};
|
|
103
|
+
for (const char *op : ops) {
|
|
104
|
+
size_t pos = inner.find(op);
|
|
105
|
+
if (pos == std::string::npos) continue;
|
|
106
|
+
std::string left = lowerTrim(inner.substr(0, pos));
|
|
107
|
+
std::string right = lowerTrim(inner.substr(pos + std::string(op).size()));
|
|
108
|
+
std::string opText(op);
|
|
109
|
+
/* `600px < width` means width > 600px: flip the comparison. */
|
|
110
|
+
bool flipped = left != "width" && left != "height";
|
|
111
|
+
std::string feature = flipped ? right : left;
|
|
112
|
+
std::string valueText = flipped ? left : right;
|
|
113
|
+
float actual = 0.0f;
|
|
114
|
+
if (feature == "width") actual = vw;
|
|
115
|
+
else if (feature == "height") actual = vh;
|
|
116
|
+
else return false;
|
|
117
|
+
float value = 0.0f;
|
|
118
|
+
if (!mediaLength(valueText, vw, vh, &value)) return false;
|
|
119
|
+
if (flipped) {
|
|
120
|
+
if (opText == ">") opText = "<";
|
|
121
|
+
else if (opText == "<") opText = ">";
|
|
122
|
+
else if (opText == ">=") opText = "<=";
|
|
123
|
+
else if (opText == "<=") opText = ">=";
|
|
124
|
+
}
|
|
125
|
+
if (opText == ">=") *matched = actual >= value;
|
|
126
|
+
else if (opText == "<=") *matched = actual <= value;
|
|
127
|
+
else if (opText == ">") *matched = actual > value;
|
|
128
|
+
else if (opText == "<") *matched = actual < value;
|
|
129
|
+
else *matched = std::fabs(actual - value) < 0.01f;
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
bool evaluateFeature(const std::string &inner, float vw, float vh) {
|
|
136
|
+
bool rangeMatched = false;
|
|
137
|
+
if (evaluateRange(inner, vw, vh, &rangeMatched)) return rangeMatched;
|
|
138
|
+
|
|
139
|
+
std::string name;
|
|
140
|
+
std::string value;
|
|
141
|
+
if (!splitFeature(inner, &name, &value)) return false;
|
|
142
|
+
if (name.empty() || value.empty()) return false;
|
|
143
|
+
|
|
144
|
+
if (name == "width" || name == "min-width" || name == "max-width") {
|
|
145
|
+
return compareLength(name, value, vw, vw, vh);
|
|
146
|
+
}
|
|
147
|
+
if (name == "height" || name == "min-height" || name == "max-height") {
|
|
148
|
+
return compareLength(name, value, vh, vw, vh);
|
|
149
|
+
}
|
|
150
|
+
if (name == "orientation") {
|
|
151
|
+
if (value == "landscape") return vw >= vh;
|
|
152
|
+
if (value == "portrait") return vh > vw;
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
if (name == "hover" || name == "any-hover") return value == "hover";
|
|
156
|
+
if (name == "pointer" || name == "any-pointer") return value == "fine";
|
|
157
|
+
if (name == "prefers-color-scheme") return value == "light";
|
|
158
|
+
if (name == "prefers-reduced-motion") return value == "no-preference";
|
|
159
|
+
if (name == "prefers-contrast") return value == "no-preference";
|
|
160
|
+
if (name == "scripting") return value == "enabled";
|
|
161
|
+
if (name == "display-mode") return value == "browser";
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
} // namespace
|
|
166
|
+
|
|
167
|
+
bool xt_css_media_matches(const std::string &query, float viewport_width, float viewport_height) {
|
|
168
|
+
std::string text = lowerTrim(query);
|
|
169
|
+
if (text.empty()) return true;
|
|
170
|
+
|
|
171
|
+
bool any = false;
|
|
172
|
+
for (const std::string &rawQuery : xt_css_split_commas(text)) {
|
|
173
|
+
std::string part = lowerTrim(rawQuery);
|
|
174
|
+
bool negate = false;
|
|
175
|
+
if (part.rfind("not ", 0) == 0) {
|
|
176
|
+
negate = true;
|
|
177
|
+
part = xt_css_trim(part.substr(4));
|
|
178
|
+
} else if (part.rfind("only ", 0) == 0) {
|
|
179
|
+
part = xt_css_trim(part.substr(5));
|
|
180
|
+
}
|
|
181
|
+
bool matched = true;
|
|
182
|
+
bool first = true;
|
|
183
|
+
for (const std::string &segment : splitAnd(part)) {
|
|
184
|
+
std::string token = xt_css_trim(segment);
|
|
185
|
+
if (token.empty()) continue;
|
|
186
|
+
if (first && token[0] != '(') {
|
|
187
|
+
/* Media type: a desktop window is `screen`, never `print`. */
|
|
188
|
+
if (token == "all" || token == "screen") {
|
|
189
|
+
matched = true;
|
|
190
|
+
} else {
|
|
191
|
+
matched = false;
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
std::string inner = token;
|
|
195
|
+
if (inner.front() == '(' && inner.back() == ')') {
|
|
196
|
+
inner = xt_css_trim(inner.substr(1, inner.size() - 2));
|
|
197
|
+
}
|
|
198
|
+
if (!evaluateFeature(inner, viewport_width, viewport_height)) matched = false;
|
|
199
|
+
}
|
|
200
|
+
first = false;
|
|
201
|
+
}
|
|
202
|
+
if (negate) matched = !matched;
|
|
203
|
+
if (matched) {
|
|
204
|
+
any = true;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return any;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
} // namespace xtgui
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* xbintsc GUI engine — display list construction.
|
|
3
|
+
*
|
|
4
|
+
* The tree walk lives here. Background layers are in `paint_background.cpp`,
|
|
5
|
+
* filter/shadow approximations in `paint_effects.cpp` and `clip-path`
|
|
6
|
+
* triangulation in `paint_clip.cpp`.
|
|
3
7
|
*/
|
|
4
8
|
|
|
5
9
|
#include "paint.h"
|
|
@@ -8,6 +12,7 @@
|
|
|
8
12
|
#include <cstdio>
|
|
9
13
|
#include <utility>
|
|
10
14
|
|
|
15
|
+
#include "paint_internal.h"
|
|
11
16
|
#include "style.h"
|
|
12
17
|
|
|
13
18
|
namespace xtgui {
|
|
@@ -43,50 +48,14 @@ FontSpec specOf(const XtStyle &style) {
|
|
|
43
48
|
return spec;
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
PaintRect rect;
|
|
50
|
-
rect.x = x;
|
|
51
|
-
rect.y = y;
|
|
52
|
-
rect.width = width;
|
|
53
|
-
rect.height = height;
|
|
54
|
-
rect.radius = radius;
|
|
55
|
-
rect.color = color;
|
|
56
|
-
rect.transform = transform;
|
|
57
|
-
out.rects.push_back(rect);
|
|
58
|
-
out.ops.push_back(PaintOp{PaintKind::Rect, (uint32_t)(out.rects.size() - 1), clip});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
void addTextRuns(const LayoutBox *box, const XtStyle &style, DisplayList &out,
|
|
62
|
-
const PaintClip &clip) {
|
|
63
|
-
if (box->fragments.empty() || style.color.a <= 0.0f) return;
|
|
64
|
-
FontSpec spec = specOf(style);
|
|
65
|
-
float ascent = 0;
|
|
66
|
-
xt_text_metrics(spec, &ascent, nullptr, nullptr);
|
|
67
|
-
if (ascent <= 0.0f) ascent = style.font_size * 0.8f;
|
|
68
|
-
for (const TextFragment &fragment : box->fragments) {
|
|
69
|
-
if (fragment.text.empty()) continue;
|
|
70
|
-
PaintText run;
|
|
71
|
-
run.x = fragment.x;
|
|
72
|
-
run.baseline = fragment.y + ascent;
|
|
73
|
-
run.width = fragment.width;
|
|
74
|
-
run.height = fragment.height;
|
|
75
|
-
run.color = style.color;
|
|
76
|
-
run.text = fragment.text;
|
|
77
|
-
run.font = spec;
|
|
78
|
-
run.transform = box->world;
|
|
79
|
-
out.texts.push_back(std::move(run));
|
|
80
|
-
out.ops.push_back(PaintOp{PaintKind::Text, (uint32_t)(out.texts.size() - 1), clip});
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
void addImage(const PaintImage &image, DisplayList &out, const PaintClip &clip) {
|
|
85
|
-
out.images.push_back(image);
|
|
86
|
-
out.ops.push_back(PaintOp{PaintKind::Image, (uint32_t)(out.images.size() - 1), clip});
|
|
51
|
+
int zIndexOf(const LayoutBox *box) {
|
|
52
|
+
if (box->style == nullptr || !box->style->has_z_index) return 0;
|
|
53
|
+
return box->style->z_index;
|
|
87
54
|
}
|
|
88
55
|
|
|
89
|
-
void paintBox(const LayoutBox *box, DisplayList &out, const PaintClip &clip
|
|
56
|
+
void paintBox(const LayoutBox *box, DisplayList &out, const PaintClip &clip,
|
|
57
|
+
const paint_detail::FilterChain &parentChain, float viewportWidth,
|
|
58
|
+
float viewportHeight);
|
|
90
59
|
|
|
91
60
|
/** The clip a box imposes on its content: its padding box, intersected with the
|
|
92
61
|
* clip already in effect. Only `overflow: hidden/scroll/auto` clips. A
|
|
@@ -106,23 +75,32 @@ PaintClip clipForChildren(const LayoutBox *box, const PaintClip &parent) {
|
|
|
106
75
|
width = bounds.width;
|
|
107
76
|
height = bounds.height;
|
|
108
77
|
}
|
|
109
|
-
|
|
78
|
+
PaintClip result = parent;
|
|
79
|
+
result.active = true;
|
|
80
|
+
if (!parent.active) {
|
|
81
|
+
result.x = x;
|
|
82
|
+
result.y = y;
|
|
83
|
+
result.width = width;
|
|
84
|
+
result.height = height;
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
110
87
|
float x0 = std::max(x, parent.x);
|
|
111
88
|
float y0 = std::max(y, parent.y);
|
|
112
89
|
float x1 = std::min(x + width, parent.x + parent.width);
|
|
113
90
|
float y1 = std::min(y + height, parent.y + parent.height);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return box->style->z_index;
|
|
91
|
+
result.x = x0;
|
|
92
|
+
result.y = y0;
|
|
93
|
+
result.width = std::max(0.0f, x1 - x0);
|
|
94
|
+
result.height = std::max(0.0f, y1 - y0);
|
|
95
|
+
return result;
|
|
120
96
|
}
|
|
121
97
|
|
|
122
98
|
/** Paint `box`'s children following z-index: negative z first (ascending), then
|
|
123
99
|
* auto/zero in document order, then positive z (ascending), so higher stacks
|
|
124
100
|
* paint last. */
|
|
125
|
-
void paintChildren(const LayoutBox *box, DisplayList &out, const PaintClip &clip
|
|
101
|
+
void paintChildren(const LayoutBox *box, DisplayList &out, const PaintClip &clip,
|
|
102
|
+
const paint_detail::FilterChain &chain, float viewportWidth,
|
|
103
|
+
float viewportHeight) {
|
|
126
104
|
std::vector<const LayoutBox *> children;
|
|
127
105
|
for (const std::unique_ptr<LayoutBox> &child : box->children) {
|
|
128
106
|
if (child->display == Display::None || child->style == nullptr) continue;
|
|
@@ -131,41 +109,78 @@ void paintChildren(const LayoutBox *box, DisplayList &out, const PaintClip &clip
|
|
|
131
109
|
std::stable_sort(children.begin(), children.end(), [](const LayoutBox *a, const LayoutBox *b) {
|
|
132
110
|
return zIndexOf(a) < zIndexOf(b);
|
|
133
111
|
});
|
|
134
|
-
for (const LayoutBox *child : children)
|
|
112
|
+
for (const LayoutBox *child : children)
|
|
113
|
+
paintBox(child, out, clip, chain, viewportWidth, viewportHeight);
|
|
135
114
|
}
|
|
136
115
|
|
|
137
|
-
void
|
|
116
|
+
void addTextRuns(const LayoutBox *box, const XtStyle &style, DisplayList &out,
|
|
117
|
+
const PaintClip &clip, const paint_detail::FilterChain &chain,
|
|
118
|
+
float viewportWidth, float viewportHeight) {
|
|
119
|
+
if (box->fragments.empty() || style.color.a <= 0.0f) return;
|
|
120
|
+
FontSpec spec = specOf(style);
|
|
121
|
+
float ascent = 0;
|
|
122
|
+
xt_text_metrics(spec, &ascent, nullptr, nullptr);
|
|
123
|
+
if (ascent <= 0.0f) ascent = style.font_size * 0.8f;
|
|
124
|
+
Color base = paint_detail::filterColor(chain, style.color);
|
|
125
|
+
for (const TextFragment &fragment : box->fragments) {
|
|
126
|
+
if (fragment.text.empty()) continue;
|
|
127
|
+
paint_detail::addTextRun(out, fragment.x, fragment.y + ascent, fragment.width,
|
|
128
|
+
fragment.height, fragment.text, spec, base, box->world, clip, style,
|
|
129
|
+
chain, viewportWidth, viewportHeight);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
void paintBox(const LayoutBox *box, DisplayList &out, const PaintClip &clip,
|
|
134
|
+
const paint_detail::FilterChain &parentChain, float viewportWidth,
|
|
135
|
+
float viewportHeight) {
|
|
138
136
|
if (box == nullptr) return;
|
|
139
137
|
const XtStyle *style = box->style;
|
|
140
138
|
|
|
141
|
-
/* A fully transparent box paints nothing — not itself, and not its
|
|
142
|
-
|
|
139
|
+
/* A fully transparent or hidden box paints nothing — not itself, and not its
|
|
140
|
+
* subtree. */
|
|
141
|
+
if (style != nullptr && (style->opacity <= 0.0f || !style->visible)) return;
|
|
142
|
+
|
|
143
|
+
/* A text box points at its parent element's style; its filters were already
|
|
144
|
+
* folded into `parentChain`, so applying them again would double them. */
|
|
145
|
+
paint_detail::FilterChain chain = parentChain;
|
|
146
|
+
if (style != nullptr && !box->is_text && !style->filters.empty()) {
|
|
147
|
+
chain = paint_detail::extendFilter(parentChain, style->filters, *style, viewportWidth,
|
|
148
|
+
viewportHeight);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
PaintClip own = clip;
|
|
152
|
+
if (style != nullptr && box->display != Display::None && !box->is_text) {
|
|
153
|
+
if (style->clip_path.type != ClipPathSpec::Type::None) {
|
|
154
|
+
int index = paint_detail::addClipPathForBox(out, *box, *style, viewportWidth, viewportHeight);
|
|
155
|
+
own = paint_detail::addClipPath(own, index);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
143
158
|
|
|
144
159
|
if (style != nullptr && box->display != Display::None) {
|
|
145
160
|
if (box->is_text) {
|
|
146
|
-
addTextRuns(box, *style, out,
|
|
161
|
+
addTextRuns(box, *style, out, own, chain, viewportWidth, viewportHeight);
|
|
147
162
|
} else {
|
|
148
|
-
/*
|
|
149
|
-
*
|
|
150
|
-
|
|
163
|
+
/* Drop shadows sit behind the box's own background but above earlier
|
|
164
|
+
* siblings. */
|
|
165
|
+
paint_detail::addBoxDropShadows(out, *box, *style, chain, own);
|
|
166
|
+
paint_detail::paintBackgrounds(out, *box, *style, chain, own, viewportWidth,
|
|
167
|
+
viewportHeight);
|
|
151
168
|
const Matrix2D &matrix = box->world;
|
|
152
|
-
if (style->has_background) {
|
|
153
|
-
addRect(out, box->x, box->y, box->width, box->height, style->border_radius,
|
|
154
|
-
style->background, clip, matrix);
|
|
155
|
-
}
|
|
156
169
|
if (style->border_style != BorderStyle::None && style->has_border_color) {
|
|
157
|
-
const Color bc = style->border_color;
|
|
170
|
+
const Color bc = paint_detail::filterColor(chain, style->border_color);
|
|
158
171
|
float top = box->border_top;
|
|
159
172
|
float right = box->border_right;
|
|
160
173
|
float bottom = box->border_bottom;
|
|
161
174
|
float left = box->border_left;
|
|
162
175
|
float inner_height = box->height - top - bottom;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
176
|
+
paint_detail::addRectFiltered(out, box->x, box->y, box->width, top, 0.0f, bc, own,
|
|
177
|
+
matrix, chain);
|
|
178
|
+
paint_detail::addRectFiltered(out, box->x, box->y + box->height - bottom, box->width,
|
|
179
|
+
bottom, 0.0f, bc, own, matrix, chain);
|
|
180
|
+
paint_detail::addRectFiltered(out, box->x, box->y + top, left, inner_height, 0.0f, bc,
|
|
181
|
+
own, matrix, chain);
|
|
182
|
+
paint_detail::addRectFiltered(out, box->x + box->width - right, box->y + top, right,
|
|
183
|
+
inner_height, 0.0f, bc, own, matrix, chain);
|
|
169
184
|
}
|
|
170
185
|
if (box->is_canvas && box->canvas_id != 0 && box->content_width > 0.0f &&
|
|
171
186
|
box->content_height > 0.0f) {
|
|
@@ -176,7 +191,7 @@ void paintBox(const LayoutBox *box, DisplayList &out, const PaintClip &clip) {
|
|
|
176
191
|
image.height = box->content_height;
|
|
177
192
|
image.canvas_id = box->canvas_id;
|
|
178
193
|
image.transform = matrix;
|
|
179
|
-
addImage(image,
|
|
194
|
+
paint_detail::addImage(out, image, own, chain);
|
|
180
195
|
} else if (box->is_media && box->media_id != 0 && box->content_width > 0.0f &&
|
|
181
196
|
box->content_height > 0.0f) {
|
|
182
197
|
PaintImage image;
|
|
@@ -186,7 +201,7 @@ void paintBox(const LayoutBox *box, DisplayList &out, const PaintClip &clip) {
|
|
|
186
201
|
image.height = box->content_height;
|
|
187
202
|
image.media_id = box->media_id;
|
|
188
203
|
image.transform = matrix;
|
|
189
|
-
addImage(image,
|
|
204
|
+
paint_detail::addImage(out, image, own, chain);
|
|
190
205
|
} else if (box->is_inline_svg && !box->svg_markup.empty() && box->content_width > 0.0f &&
|
|
191
206
|
box->content_height > 0.0f) {
|
|
192
207
|
PaintImage image;
|
|
@@ -196,7 +211,7 @@ void paintBox(const LayoutBox *box, DisplayList &out, const PaintClip &clip) {
|
|
|
196
211
|
image.height = box->content_height;
|
|
197
212
|
image.svg = box->svg_markup;
|
|
198
213
|
image.transform = matrix;
|
|
199
|
-
addImage(image,
|
|
214
|
+
paint_detail::addImage(out, image, own, chain);
|
|
200
215
|
} else if (box->is_image && !box->image_src.empty() && box->content_width > 0.0f &&
|
|
201
216
|
box->content_height > 0.0f) {
|
|
202
217
|
PaintImage image;
|
|
@@ -206,24 +221,34 @@ void paintBox(const LayoutBox *box, DisplayList &out, const PaintClip &clip) {
|
|
|
206
221
|
image.height = box->content_height;
|
|
207
222
|
image.src = box->image_src;
|
|
208
223
|
image.transform = matrix;
|
|
209
|
-
addImage(image,
|
|
224
|
+
paint_detail::addImage(out, image, own, chain);
|
|
210
225
|
}
|
|
211
226
|
}
|
|
212
227
|
}
|
|
213
228
|
|
|
214
|
-
|
|
229
|
+
/* Drop shadows belong to the box that declared them: its silhouette has
|
|
230
|
+
* already been emitted, so descendants must not repeat it. */
|
|
231
|
+
paint_detail::FilterChain childChain = chain;
|
|
232
|
+
childChain.shadows.clear();
|
|
233
|
+
paintChildren(box, out, clipForChildren(box, own), childChain, viewportWidth, viewportHeight);
|
|
215
234
|
}
|
|
216
235
|
|
|
217
236
|
} // namespace
|
|
218
237
|
|
|
219
|
-
void xt_paint_build(const LayoutBox *root, DisplayList &out
|
|
238
|
+
void xt_paint_build(const LayoutBox *root, DisplayList &out, float viewportWidth,
|
|
239
|
+
float viewportHeight) {
|
|
220
240
|
PaintClip none;
|
|
221
|
-
|
|
241
|
+
paint_detail::FilterChain chain;
|
|
242
|
+
if (root != nullptr && viewportWidth <= 0.0f) {
|
|
243
|
+
viewportWidth = root->cb_width;
|
|
244
|
+
viewportHeight = root->cb_height;
|
|
245
|
+
}
|
|
246
|
+
paintBox(root, out, none, chain, viewportWidth, viewportHeight);
|
|
222
247
|
}
|
|
223
248
|
|
|
224
249
|
std::string DisplayList::dump() const {
|
|
225
250
|
std::string text;
|
|
226
|
-
char buffer[
|
|
251
|
+
char buffer[320];
|
|
227
252
|
for (const PaintOp &op : ops) {
|
|
228
253
|
std::string prefix;
|
|
229
254
|
if (op.clip.active) {
|
|
@@ -231,6 +256,14 @@ std::string DisplayList::dump() const {
|
|
|
231
256
|
op.clip.width, op.clip.height);
|
|
232
257
|
prefix = buffer;
|
|
233
258
|
}
|
|
259
|
+
if (op.clip.path_count > 0) {
|
|
260
|
+
prefix += "clip-path=";
|
|
261
|
+
for (int i = 0; i < op.clip.path_count; i++) {
|
|
262
|
+
if (i > 0) prefix += ",";
|
|
263
|
+
prefix += std::to_string(op.clip.paths[i]);
|
|
264
|
+
}
|
|
265
|
+
prefix += " ";
|
|
266
|
+
}
|
|
234
267
|
std::string suffix;
|
|
235
268
|
if (op.kind == PaintKind::Rect) {
|
|
236
269
|
suffix = transformSuffix(rects[op.index].transform);
|
|
@@ -246,6 +279,7 @@ std::string DisplayList::dump() const {
|
|
|
246
279
|
"text x=%.1f baseline=%.1f size=%.1f color=%s \"%s\"", run.x, run.baseline,
|
|
247
280
|
run.font.pixel_size, formatColor(run.color).c_str(), run.text.c_str());
|
|
248
281
|
text += prefix + buffer;
|
|
282
|
+
if (run.shadow) text += " shadow=1";
|
|
249
283
|
} else {
|
|
250
284
|
const PaintImage &image = images[op.index];
|
|
251
285
|
suffix = transformSuffix(image.transform);
|
package/runtime/ext_gui/paint.h
CHANGED
|
@@ -46,6 +46,9 @@ struct PaintText {
|
|
|
46
46
|
std::string text;
|
|
47
47
|
FontSpec font;
|
|
48
48
|
Matrix2D transform;
|
|
49
|
+
/** Set on a `text-shadow` copy (diagnostics only; the renderer draws it like
|
|
50
|
+
* any other glyph run). */
|
|
51
|
+
bool shadow = false;
|
|
49
52
|
};
|
|
50
53
|
|
|
51
54
|
/** An image to draw, scaled into the given rectangle (viewport-relative).
|
|
@@ -65,17 +68,38 @@ struct PaintImage {
|
|
|
65
68
|
uint64_t media_id = 0;
|
|
66
69
|
uint64_t canvas_id = 0;
|
|
67
70
|
Matrix2D transform;
|
|
71
|
+
/** Modulating colour for the image shader (white normally; a filter's
|
|
72
|
+
* multiplicative effect may tint it). */
|
|
73
|
+
Color tint{1.0f, 1.0f, 1.0f, 1.0f};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/** One triangle of a clip path in device-space logical pixels. The paint pass
|
|
77
|
+
* triangulates (possibly concave) polygons once so the renderer can clip
|
|
78
|
+
* convex shape/glyph quads by a simple half-plane test per triangle. */
|
|
79
|
+
struct ClipTriangle {
|
|
80
|
+
float x[3] = {0, 0, 0};
|
|
81
|
+
float y[3] = {0, 0, 0};
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/** A resolved `clip-path` polygon plus its triangulation. */
|
|
85
|
+
struct ClipPathGeometry {
|
|
86
|
+
std::vector<float> points; // x, y pairs (diagnostics)
|
|
87
|
+
std::vector<ClipTriangle> triangles;
|
|
68
88
|
};
|
|
69
89
|
|
|
70
90
|
/** The clip rectangle in effect for a painted shape (device-space logical
|
|
71
91
|
* pixels — a transformed subtree contributes the bounding box of its clip).
|
|
72
|
-
* `width <= 0` means "no clip".
|
|
92
|
+
* `width <= 0` means "no clip". `paths` index `DisplayList::clip_paths`; at most
|
|
93
|
+
* two nested `clip-path` shapes are kept (CSS would intersect every ancestor;
|
|
94
|
+
* deeper nesting is rare and the outermost two are the ones a UI uses). */
|
|
73
95
|
struct PaintClip {
|
|
74
96
|
bool active = false;
|
|
75
97
|
float x = 0;
|
|
76
98
|
float y = 0;
|
|
77
99
|
float width = 0;
|
|
78
100
|
float height = 0;
|
|
101
|
+
int path_count = 0;
|
|
102
|
+
int paths[2] = {-1, -1};
|
|
79
103
|
};
|
|
80
104
|
|
|
81
105
|
enum class PaintKind { Rect, Text, Image };
|
|
@@ -95,20 +119,26 @@ struct DisplayList {
|
|
|
95
119
|
/** Shapes in painter order (backgrounds/borders first, then children, then
|
|
96
120
|
* stacked siblings), each with the clip rectangle that bounds it. */
|
|
97
121
|
std::vector<PaintOp> ops;
|
|
122
|
+
/** Device-space clip-path polygons shared by an element's ops. */
|
|
123
|
+
std::vector<ClipPathGeometry> clip_paths;
|
|
98
124
|
|
|
99
125
|
void clear() {
|
|
100
126
|
rects.clear();
|
|
101
127
|
texts.clear();
|
|
102
128
|
images.clear();
|
|
103
129
|
ops.clear();
|
|
130
|
+
clip_paths.clear();
|
|
104
131
|
}
|
|
105
132
|
bool empty() const { return rects.empty() && texts.empty() && images.empty(); }
|
|
106
133
|
/** Human-readable dump, one shape per line, in painter order (diagnostics/tests). */
|
|
107
134
|
std::string dump() const;
|
|
108
135
|
};
|
|
109
136
|
|
|
110
|
-
/** Build the display list for a laid-out tree and append it to `out`.
|
|
111
|
-
|
|
137
|
+
/** Build the display list for a laid-out tree and append it to `out`. The
|
|
138
|
+
* viewport size resolves `vw`/`vh` units inside `clip-path` and shadows; pass 0
|
|
139
|
+
* to take it from the root box's containing block. */
|
|
140
|
+
void xt_paint_build(const LayoutBox *root, DisplayList &out, float viewportWidth = 0.0f,
|
|
141
|
+
float viewportHeight = 0.0f);
|
|
112
142
|
|
|
113
143
|
} // namespace xtgui
|
|
114
144
|
|