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
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — CSS background layer values.
|
|
3
|
+
*
|
|
4
|
+
* `background-image` is a comma-separated layer list; each layer may be a
|
|
5
|
+
* linear or radial gradient. They are modelled (not rasterised here) and painted
|
|
6
|
+
* back-to-front by `paint_background.cpp`. `background` also accepts a final
|
|
7
|
+
* colour layer.
|
|
8
|
+
*/
|
|
9
|
+
#include <cmath>
|
|
10
|
+
#include <cctype>
|
|
11
|
+
#include <string>
|
|
12
|
+
#include <utility>
|
|
13
|
+
#include <vector>
|
|
14
|
+
|
|
15
|
+
#include "style_internal.h"
|
|
16
|
+
#include "style_values_internal.h"
|
|
17
|
+
|
|
18
|
+
namespace xtgui {
|
|
19
|
+
|
|
20
|
+
namespace {
|
|
21
|
+
|
|
22
|
+
/** Split at top-level commas (function arguments stay intact). */
|
|
23
|
+
std::vector<std::string> splitCommas(const std::string &text) {
|
|
24
|
+
std::vector<std::string> parts;
|
|
25
|
+
std::string current;
|
|
26
|
+
int depth = 0;
|
|
27
|
+
char quote = 0;
|
|
28
|
+
for (char c : text) {
|
|
29
|
+
if (quote != 0) {
|
|
30
|
+
current.push_back(c);
|
|
31
|
+
if (c == quote) quote = 0;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (c == '"' || c == '\'') {
|
|
35
|
+
quote = c;
|
|
36
|
+
current.push_back(c);
|
|
37
|
+
} else if (c == '(') {
|
|
38
|
+
depth++;
|
|
39
|
+
current.push_back(c);
|
|
40
|
+
} else if (c == ')') {
|
|
41
|
+
if (depth > 0) depth--;
|
|
42
|
+
current.push_back(c);
|
|
43
|
+
} else if (c == ',' && depth == 0) {
|
|
44
|
+
std::string part = xt_css_trim(current);
|
|
45
|
+
if (!part.empty()) parts.push_back(part);
|
|
46
|
+
current.clear();
|
|
47
|
+
} else {
|
|
48
|
+
current.push_back(c);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
std::string part = xt_css_trim(current);
|
|
52
|
+
if (!part.empty()) parts.push_back(part);
|
|
53
|
+
return parts;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Split on whitespace outside function parentheses. */
|
|
57
|
+
std::vector<std::string> splitTokens(const std::string &text) {
|
|
58
|
+
std::vector<std::string> parts;
|
|
59
|
+
std::string current;
|
|
60
|
+
int depth = 0;
|
|
61
|
+
for (char c : text) {
|
|
62
|
+
if (c == '(') depth++;
|
|
63
|
+
if (c == ')') depth--;
|
|
64
|
+
if ((c == ' ' || c == '\t' || c == '\n' || c == '\r') && depth == 0) {
|
|
65
|
+
if (!current.empty()) {
|
|
66
|
+
parts.push_back(current);
|
|
67
|
+
current.clear();
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
current.push_back(c);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!current.empty()) parts.push_back(current);
|
|
74
|
+
return parts;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Locate `<name>(...)` in `text`, returning the body and the call's bounds. */
|
|
78
|
+
bool findCall(const std::string &text, const std::string &name, std::string *body) {
|
|
79
|
+
std::string lower = xt_css_lower(text);
|
|
80
|
+
size_t start = lower.find(name + "(");
|
|
81
|
+
if (start == std::string::npos) return false;
|
|
82
|
+
size_t open = start + name.size();
|
|
83
|
+
int depth = 0;
|
|
84
|
+
for (size_t i = open; i < text.size(); i++) {
|
|
85
|
+
if (text[i] == '(') depth++;
|
|
86
|
+
else if (text[i] == ')') {
|
|
87
|
+
depth--;
|
|
88
|
+
if (depth == 0) {
|
|
89
|
+
*body = text.substr(open + 1, i - open - 1);
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Parse a colour at the start of a gradient stop, leaving the offsets in
|
|
98
|
+
* `rest`. Handles `#rgb`, `rgb()/rgba()`, `hsl()` and named colours. */
|
|
99
|
+
bool parseGradientColor(const std::string &token, Color *color, std::string *rest) {
|
|
100
|
+
std::string text = xt_css_trim(token);
|
|
101
|
+
if (text.empty()) return false;
|
|
102
|
+
size_t end = 0;
|
|
103
|
+
if (text[0] == '#') {
|
|
104
|
+
end = 1;
|
|
105
|
+
while (end < text.size() && std::isxdigit((unsigned char)text[end])) end++;
|
|
106
|
+
} else if (text.compare(0, 4, "rgb(") == 0 || text.compare(0, 5, "rgba(") == 0 ||
|
|
107
|
+
text.compare(0, 4, "hsl(") == 0 || text.compare(0, 5, "hsla(") == 0) {
|
|
108
|
+
int depth = 0;
|
|
109
|
+
for (; end < text.size(); end++) {
|
|
110
|
+
if (text[end] == '(') depth++;
|
|
111
|
+
else if (text[end] == ')') {
|
|
112
|
+
depth--;
|
|
113
|
+
if (depth == 0) {
|
|
114
|
+
end++;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
while (end < text.size() && text[end] != ' ' && text[end] != '\t' && text[end] != '\n' &&
|
|
121
|
+
text[end] != '\r')
|
|
122
|
+
end++;
|
|
123
|
+
}
|
|
124
|
+
if (end == 0) return false;
|
|
125
|
+
std::string colorText = text.substr(0, end);
|
|
126
|
+
*color = xt_css_parse_color(colorText);
|
|
127
|
+
if (!color->valid && xt_css_lower(colorText) == "transparent") {
|
|
128
|
+
color->valid = true;
|
|
129
|
+
color->r = color->g = color->b = 0.0f;
|
|
130
|
+
color->a = 0.0f;
|
|
131
|
+
}
|
|
132
|
+
if (!color->valid) return false;
|
|
133
|
+
*rest = xt_css_trim(text.substr(end));
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Parse one `[<percent>]` / `<number>` offset. */
|
|
138
|
+
bool parseGradientOffset(const std::string &raw, float *out) {
|
|
139
|
+
std::string text = xt_css_trim(raw);
|
|
140
|
+
if (text.empty()) return false;
|
|
141
|
+
bool percent = text.back() == '%';
|
|
142
|
+
if (percent) text.pop_back();
|
|
143
|
+
float value = 0;
|
|
144
|
+
if (!xt_css_parse_number(text, &value)) return false;
|
|
145
|
+
if (percent) value /= 100.0f;
|
|
146
|
+
else if (value > 1.0f) value /= 100.0f;
|
|
147
|
+
if (value < 0) value = 0;
|
|
148
|
+
if (value > 1) value = 1;
|
|
149
|
+
*out = value;
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Parse a comma-separated `[color [offset [offset]]]` stop list. */
|
|
154
|
+
bool parseStops(const std::string &text, std::vector<BackgroundGradientStop> *stops) {
|
|
155
|
+
std::vector<std::string> parts = splitCommas(text);
|
|
156
|
+
if (parts.size() < 2) return false;
|
|
157
|
+
float last = -1.0f;
|
|
158
|
+
for (const std::string &part : parts) {
|
|
159
|
+
Color color;
|
|
160
|
+
std::string rest;
|
|
161
|
+
if (!parseGradientColor(part, &color, &rest)) continue;
|
|
162
|
+
BackgroundGradientStop stop;
|
|
163
|
+
stop.color = color;
|
|
164
|
+
stop.offset = -1.0f;
|
|
165
|
+
if (!rest.empty()) {
|
|
166
|
+
std::vector<std::string> offsets = splitTokens(rest);
|
|
167
|
+
if (!offsets.empty()) {
|
|
168
|
+
float first = 0;
|
|
169
|
+
if (parseGradientOffset(offsets[0], &first)) {
|
|
170
|
+
stop.offset = first;
|
|
171
|
+
if (stop.offset < last) stop.offset = last;
|
|
172
|
+
last = stop.offset;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
stops->push_back(stop);
|
|
177
|
+
}
|
|
178
|
+
if (stops->size() < 2) return false;
|
|
179
|
+
/* Fill implicit offsets by distributing them between their neighbours. */
|
|
180
|
+
size_t i = 0;
|
|
181
|
+
while (i < stops->size()) {
|
|
182
|
+
if ((*stops)[i].offset >= 0) {
|
|
183
|
+
i++;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
size_t j = i;
|
|
187
|
+
while (j < stops->size() && (*stops)[j].offset < 0) j++;
|
|
188
|
+
float before = (i == 0) ? 0.0f : (*stops)[i - 1].offset;
|
|
189
|
+
float after = (j >= stops->size()) ? 1.0f : (*stops)[j].offset;
|
|
190
|
+
size_t count = j - i;
|
|
191
|
+
for (size_t k = 0; k < count; k++)
|
|
192
|
+
(*stops)[i + k].offset = before + (after - before) * (float)(k + 1) / (float)(count + 1);
|
|
193
|
+
i = j;
|
|
194
|
+
}
|
|
195
|
+
stops->front().offset = 0.0f;
|
|
196
|
+
stops->back().offset = 1.0f;
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** CSS gradient direction/angle. Returns true when `text` was a direction. */
|
|
201
|
+
bool parseDirection(const std::string &text, float *angle) {
|
|
202
|
+
std::string value = xt_css_lower(xt_css_trim(text));
|
|
203
|
+
if (value.rfind("to ", 0) == 0) {
|
|
204
|
+
std::string side = xt_css_trim(value.substr(3));
|
|
205
|
+
if (side == "top") *angle = 0;
|
|
206
|
+
else if (side == "right") *angle = 90;
|
|
207
|
+
else if (side == "bottom") *angle = 180;
|
|
208
|
+
else if (side == "left") *angle = 270;
|
|
209
|
+
else if (side == "top right" || side == "right top") *angle = 45;
|
|
210
|
+
else if (side == "bottom right" || side == "right bottom") *angle = 135;
|
|
211
|
+
else if (side == "bottom left" || side == "left bottom") *angle = 225;
|
|
212
|
+
else if (side == "top left" || side == "left top") *angle = 315;
|
|
213
|
+
else return false;
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
const char *units[] = {"deg", "grad", "rad", "turn"};
|
|
217
|
+
for (const char *unit : units) {
|
|
218
|
+
size_t length = std::string(unit).size();
|
|
219
|
+
if (value.size() <= length || value.compare(value.size() - length, length, unit) != 0) continue;
|
|
220
|
+
float number = 0;
|
|
221
|
+
if (!xt_css_parse_number(value.substr(0, value.size() - length), &number)) return false;
|
|
222
|
+
if (std::string(unit) == "deg") *angle = number;
|
|
223
|
+
else if (std::string(unit) == "grad") *angle = number * 0.9f;
|
|
224
|
+
else if (std::string(unit) == "rad") *angle = (float)(number * 180.0 / 3.141592653589793);
|
|
225
|
+
else *angle = number * 360.0f;
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** `linear-gradient([<direction>,] <stops>)`. */
|
|
232
|
+
bool parseLinearGradient(const std::string &body, BackgroundLayer *layer) {
|
|
233
|
+
std::vector<std::string> parts = splitCommas(body);
|
|
234
|
+
if (parts.size() < 2) return false;
|
|
235
|
+
std::string stopsText;
|
|
236
|
+
float angle = 180.0f;
|
|
237
|
+
if (parseDirection(parts.front(), &angle)) {
|
|
238
|
+
stopsText = body.substr(body.find(',') + 1);
|
|
239
|
+
} else {
|
|
240
|
+
stopsText = body;
|
|
241
|
+
}
|
|
242
|
+
if (!parseStops(stopsText, &layer->stops)) return false;
|
|
243
|
+
layer->kind = BackgroundKind::Linear;
|
|
244
|
+
layer->angle = angle;
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Resolve one position token (keyword or length). */
|
|
249
|
+
bool parsePositionToken(const std::string &raw, Length *out, bool *vertical) {
|
|
250
|
+
std::string text = xt_css_lower(xt_css_trim(raw));
|
|
251
|
+
if (text == "left") { *out = {0.0f, Unit::Percent}; *vertical = false; return true; }
|
|
252
|
+
if (text == "right") { *out = {100.0f, Unit::Percent}; *vertical = false; return true; }
|
|
253
|
+
if (text == "top") { *out = {0.0f, Unit::Percent}; *vertical = true; return true; }
|
|
254
|
+
if (text == "bottom") { *out = {100.0f, Unit::Percent}; *vertical = true; return true; }
|
|
255
|
+
if (text == "center") { *out = {50.0f, Unit::Percent}; *vertical = false; return true; }
|
|
256
|
+
Length length = xt_css_parse_length(text);
|
|
257
|
+
if (length.unit == Unit::Invalid || length.unit == Unit::Auto || length.unit == Unit::None)
|
|
258
|
+
return false;
|
|
259
|
+
*out = length;
|
|
260
|
+
*vertical = false;
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** True for a radial-gradient prefix (`circle 80px at 20% 30%`, `10px 20px`). */
|
|
265
|
+
bool looksLikeRadialConfig(const std::string &part) {
|
|
266
|
+
std::string lower = xt_css_lower(part);
|
|
267
|
+
if (lower.rfind("circle", 0) == 0 || lower.rfind("ellipse", 0) == 0 ||
|
|
268
|
+
lower.rfind("closest-", 0) == 0 || lower.rfind("farthest-", 0) == 0 ||
|
|
269
|
+
lower.rfind("at ", 0) == 0 || lower.find(" at ") != std::string::npos)
|
|
270
|
+
return true;
|
|
271
|
+
std::vector<std::string> tokens = splitTokens(part);
|
|
272
|
+
bool any = false;
|
|
273
|
+
for (const std::string &raw : tokens) {
|
|
274
|
+
std::string token = xt_css_lower(raw);
|
|
275
|
+
if (token == "circle" || token == "ellipse" || token == "at" ||
|
|
276
|
+
token == "closest-side" || token == "closest-corner" ||
|
|
277
|
+
token == "farthest-side" || token == "farthest-corner" ||
|
|
278
|
+
token == "left" || token == "right" || token == "top" || token == "bottom" ||
|
|
279
|
+
token == "center") {
|
|
280
|
+
any = true;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
Length length = xt_css_parse_length(token);
|
|
284
|
+
if (length.unit == Unit::Invalid || length.unit == Unit::Auto || length.unit == Unit::None)
|
|
285
|
+
return false;
|
|
286
|
+
any = true;
|
|
287
|
+
}
|
|
288
|
+
return any;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** `radial-gradient([<shape> || <size>] [at <position>]?, <stops>)`. */
|
|
292
|
+
bool parseRadialGradient(const std::string &body, BackgroundLayer *layer) {
|
|
293
|
+
std::vector<std::string> top = splitCommas(body);
|
|
294
|
+
size_t firstStop = 0;
|
|
295
|
+
std::vector<std::string> config;
|
|
296
|
+
/* The configuration is everything before the first component that starts with
|
|
297
|
+
* a colour. That is the first segment unless it is a shape/size/at clause. */
|
|
298
|
+
for (size_t i = 0; i < top.size(); i++) {
|
|
299
|
+
if (looksLikeRadialConfig(top[i])) {
|
|
300
|
+
config.push_back(top[i]);
|
|
301
|
+
firstStop = i + 1;
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
firstStop = i;
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
if (firstStop >= top.size()) return false;
|
|
308
|
+
std::string configText;
|
|
309
|
+
for (const std::string &part : config) configText += part + " ";
|
|
310
|
+
std::vector<std::string> tokens = splitTokens(configText);
|
|
311
|
+
Length cx{50.0f, Unit::Percent};
|
|
312
|
+
Length cy{50.0f, Unit::Percent};
|
|
313
|
+
Length rx{0.0f, Unit::Auto};
|
|
314
|
+
Length ry{0.0f, Unit::Auto};
|
|
315
|
+
bool explicitRadius = false;
|
|
316
|
+
bool circle = false;
|
|
317
|
+
bool hasShape = false;
|
|
318
|
+
RadialSize size = RadialSize::FarthestCorner;
|
|
319
|
+
for (size_t i = 0; i < tokens.size(); i++) {
|
|
320
|
+
std::string token = xt_css_lower(tokens[i]);
|
|
321
|
+
if (token == "circle") {
|
|
322
|
+
circle = true;
|
|
323
|
+
hasShape = true;
|
|
324
|
+
} else if (token == "ellipse") {
|
|
325
|
+
circle = false;
|
|
326
|
+
hasShape = true;
|
|
327
|
+
} else if (token == "closest-side") {
|
|
328
|
+
size = RadialSize::ClosestSide;
|
|
329
|
+
} else if (token == "farthest-side") {
|
|
330
|
+
size = RadialSize::FarthestSide;
|
|
331
|
+
} else if (token == "closest-corner") {
|
|
332
|
+
size = RadialSize::ClosestCorner;
|
|
333
|
+
} else if (token == "farthest-corner") {
|
|
334
|
+
size = RadialSize::FarthestCorner;
|
|
335
|
+
} else if (token == "at") {
|
|
336
|
+
Length values[2];
|
|
337
|
+
bool vertical[2] = {false, false};
|
|
338
|
+
size_t used = 0;
|
|
339
|
+
while (i + 1 < tokens.size() && used < 2) {
|
|
340
|
+
i++;
|
|
341
|
+
if (!parsePositionToken(tokens[i], &values[used], &vertical[used])) {
|
|
342
|
+
i--;
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
used++;
|
|
346
|
+
}
|
|
347
|
+
if (used == 1) {
|
|
348
|
+
if (vertical[0]) {
|
|
349
|
+
cy = values[0];
|
|
350
|
+
cx = {50.0f, Unit::Percent};
|
|
351
|
+
} else {
|
|
352
|
+
cx = values[0];
|
|
353
|
+
cy = {50.0f, Unit::Percent};
|
|
354
|
+
}
|
|
355
|
+
} else if (used == 2) {
|
|
356
|
+
if (vertical[0] && !vertical[1]) {
|
|
357
|
+
cx = values[1];
|
|
358
|
+
cy = values[0];
|
|
359
|
+
} else {
|
|
360
|
+
cx = values[0];
|
|
361
|
+
cy = values[1];
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
Length length = xt_css_parse_length(token);
|
|
366
|
+
if (length.unit == Unit::Invalid || length.unit == Unit::Auto ||
|
|
367
|
+
length.unit == Unit::None)
|
|
368
|
+
continue;
|
|
369
|
+
if (!explicitRadius) {
|
|
370
|
+
rx = length;
|
|
371
|
+
explicitRadius = true;
|
|
372
|
+
} else if (ry.unit == Unit::Auto) {
|
|
373
|
+
ry = length;
|
|
374
|
+
}
|
|
375
|
+
size = RadialSize::Explicit;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
std::string stopsText;
|
|
379
|
+
for (size_t i = firstStop; i < top.size(); i++) {
|
|
380
|
+
if (!stopsText.empty()) stopsText += ", ";
|
|
381
|
+
stopsText += top[i];
|
|
382
|
+
}
|
|
383
|
+
if (!parseStops(stopsText, &layer->stops)) return false;
|
|
384
|
+
layer->kind = BackgroundKind::Radial;
|
|
385
|
+
layer->circle = circle;
|
|
386
|
+
layer->has_shape = hasShape;
|
|
387
|
+
layer->size = size;
|
|
388
|
+
layer->center_x = cx;
|
|
389
|
+
layer->center_y = cy;
|
|
390
|
+
layer->radius_x = rx;
|
|
391
|
+
layer->radius_y = explicitRadius ? (ry.unit == Unit::Auto ? rx : ry) : Length{0.0f, Unit::Auto};
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Parse one layer, appending it when it is a supported gradient. */
|
|
396
|
+
bool parseLayer(const std::string &part, BackgroundLayer *layer) {
|
|
397
|
+
std::string body;
|
|
398
|
+
if (findCall(part, "linear-gradient", &body) ||
|
|
399
|
+
findCall(part, "repeating-linear-gradient", &body)) {
|
|
400
|
+
return parseLinearGradient(body, layer);
|
|
401
|
+
}
|
|
402
|
+
if (findCall(part, "radial-gradient", &body) ||
|
|
403
|
+
findCall(part, "repeating-radial-gradient", &body)) {
|
|
404
|
+
return parseRadialGradient(body, layer);
|
|
405
|
+
}
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** Parse the layer list. `allowColor` is true for the `background` shorthand. */
|
|
410
|
+
bool parseLayers(const std::string &value, std::vector<BackgroundLayer> *layers, Color *color,
|
|
411
|
+
bool *hasColor, bool allowColor) {
|
|
412
|
+
layers->clear();
|
|
413
|
+
std::string text = xt_css_trim(value);
|
|
414
|
+
std::string lower = xt_css_lower(text);
|
|
415
|
+
if (lower == "none") return true;
|
|
416
|
+
for (const std::string &part : splitCommas(text)) {
|
|
417
|
+
BackgroundLayer layer;
|
|
418
|
+
if (parseLayer(part, &layer)) {
|
|
419
|
+
layers->push_back(layer);
|
|
420
|
+
/* `background: linear-gradient(...) #fff` mixes the final colour with the
|
|
421
|
+
* image layer; the gradient itself is one token, so a colour scan cannot
|
|
422
|
+
* pick up its stops. */
|
|
423
|
+
if (allowColor) {
|
|
424
|
+
for (const std::string &token : splitTokens(part)) {
|
|
425
|
+
Color parsed = xt_css_parse_color(token);
|
|
426
|
+
if (parsed.valid) {
|
|
427
|
+
*color = parsed;
|
|
428
|
+
*hasColor = true;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
if (!allowColor) continue;
|
|
435
|
+
Color parsed = xt_css_parse_color(part);
|
|
436
|
+
if (!parsed.valid) {
|
|
437
|
+
for (const std::string &token : splitTokens(part)) {
|
|
438
|
+
parsed = xt_css_parse_color(token);
|
|
439
|
+
if (parsed.valid) break;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (parsed.valid) {
|
|
443
|
+
*color = parsed;
|
|
444
|
+
*hasColor = true;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
} // namespace
|
|
451
|
+
|
|
452
|
+
void xt_style_apply_background_image(XtStyle *style, const std::string &value) {
|
|
453
|
+
Color color;
|
|
454
|
+
bool hasColor = false;
|
|
455
|
+
std::vector<BackgroundLayer> layers;
|
|
456
|
+
parseLayers(value, &layers, &color, &hasColor, false);
|
|
457
|
+
style->background_layers = std::move(layers);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
void xt_style_apply_background(XtStyle *style, const std::string &value) {
|
|
461
|
+
/* The shorthand resets every background sub-property, so a later
|
|
462
|
+
* `background: linear-gradient(...)` drops an earlier `background-color`. */
|
|
463
|
+
style->has_background = false;
|
|
464
|
+
style->background = Color();
|
|
465
|
+
Color color;
|
|
466
|
+
bool hasColor = false;
|
|
467
|
+
std::vector<BackgroundLayer> layers;
|
|
468
|
+
parseLayers(value, &layers, &color, &hasColor, true);
|
|
469
|
+
style->background_layers = std::move(layers);
|
|
470
|
+
if (hasColor) {
|
|
471
|
+
style->background = color;
|
|
472
|
+
style->has_background = true;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
} // namespace xtgui
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — CSS Grid and alignment declarations.
|
|
3
|
+
*
|
|
4
|
+
* Kept apart from `style_values.cpp` so the declaration dispatcher stays under
|
|
5
|
+
* the repository's per-file line budget.
|
|
6
|
+
*/
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <vector>
|
|
9
|
+
|
|
10
|
+
#include "style_internal.h"
|
|
11
|
+
#include "style_values_internal.h"
|
|
12
|
+
|
|
13
|
+
namespace xtgui {
|
|
14
|
+
|
|
15
|
+
/* -- grid values ---------------------------------------------------------- */
|
|
16
|
+
|
|
17
|
+
/** Split a track list on whitespace, ignoring spaces inside `minmax(...)` /
|
|
18
|
+
* `repeat(...)`. */
|
|
19
|
+
static std::vector<std::string> splitTrackTokens(const std::string &text) {
|
|
20
|
+
std::vector<std::string> tokens;
|
|
21
|
+
std::string current;
|
|
22
|
+
int depth = 0;
|
|
23
|
+
for (char c : text) {
|
|
24
|
+
if (c == '(') depth++;
|
|
25
|
+
if (c == ')') depth--;
|
|
26
|
+
if ((c == ' ' || c == '\t' || c == '\n' || c == '\r') && depth == 0) {
|
|
27
|
+
if (!current.empty()) {
|
|
28
|
+
tokens.push_back(current);
|
|
29
|
+
current.clear();
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
current.push_back(c);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (!current.empty()) tokens.push_back(current);
|
|
36
|
+
return tokens;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static bool parseFr(const std::string &text, float *out) {
|
|
40
|
+
std::string token = xt_css_lower(xt_css_trim(text));
|
|
41
|
+
if (token.size() < 3 || token.compare(token.size() - 2, 2, "fr") != 0) return false;
|
|
42
|
+
float value = 0;
|
|
43
|
+
if (!xt_css_parse_number(token.substr(0, token.size() - 2), &value)) return false;
|
|
44
|
+
*out = value;
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static bool parseTrackBound(const std::string &text, bool is_max, GridTrack *track) {
|
|
49
|
+
std::string token = xt_css_lower(xt_css_trim(text));
|
|
50
|
+
float fr = 0;
|
|
51
|
+
if (parseFr(token, &fr)) {
|
|
52
|
+
track->fr = fr;
|
|
53
|
+
if (!is_max) track->min = {0.0f, Unit::Px};
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (token == "auto") {
|
|
57
|
+
if (is_max) track->max = {0.0f, Unit::Auto};
|
|
58
|
+
else track->min = {0.0f, Unit::Auto};
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
Length length = xt_css_parse_length(token);
|
|
62
|
+
if (length.unit == Unit::Invalid) return false;
|
|
63
|
+
if (is_max) track->max = length;
|
|
64
|
+
else track->min = length;
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static bool parseOneTrack(const std::string &raw, GridTrack *track) {
|
|
69
|
+
std::string token = xt_css_lower(xt_css_trim(raw));
|
|
70
|
+
if (token == "auto") {
|
|
71
|
+
*track = GridTrack{};
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
if (token.size() > 7 && token.compare(0, 7, "minmax(") == 0 && token.back() == ')') {
|
|
75
|
+
std::string inner = token.substr(7, token.size() - 8);
|
|
76
|
+
size_t comma = inner.find(',');
|
|
77
|
+
if (comma == std::string::npos) return false;
|
|
78
|
+
*track = GridTrack{};
|
|
79
|
+
if (!parseTrackBound(inner.substr(0, comma), false, track)) return false;
|
|
80
|
+
if (!parseTrackBound(inner.substr(comma + 1), true, track)) return false;
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
*track = GridTrack{};
|
|
84
|
+
return parseTrackBound(token, true, track);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
static bool parseGridTracks(const std::string &value, std::vector<GridTrack> *out) {
|
|
88
|
+
std::string text = xt_css_lower(xt_css_trim(value));
|
|
89
|
+
out->clear();
|
|
90
|
+
if (text.empty() || text == "none") return true;
|
|
91
|
+
std::vector<std::string> tokens = splitTrackTokens(text);
|
|
92
|
+
for (const std::string &token : tokens) {
|
|
93
|
+
if (token.size() > 7 && token.compare(0, 7, "repeat(") == 0 && token.back() == ')') {
|
|
94
|
+
std::string inner = token.substr(7, token.size() - 8);
|
|
95
|
+
size_t comma = inner.find(',');
|
|
96
|
+
if (comma == std::string::npos) return false;
|
|
97
|
+
float repeatCount = 0;
|
|
98
|
+
if (!xt_css_parse_number(xt_css_trim(inner.substr(0, comma)), &repeatCount)) return false;
|
|
99
|
+
int count = (int)repeatCount;
|
|
100
|
+
if (count < 1) count = 1;
|
|
101
|
+
if (count > 64) count = 64;
|
|
102
|
+
std::vector<GridTrack> repeated;
|
|
103
|
+
for (const std::string &part : splitTrackTokens(inner.substr(comma + 1))) {
|
|
104
|
+
GridTrack track;
|
|
105
|
+
if (!parseOneTrack(part, &track)) return false;
|
|
106
|
+
repeated.push_back(track);
|
|
107
|
+
}
|
|
108
|
+
for (int i = 0; i < count; i++)
|
|
109
|
+
for (const GridTrack &track : repeated) out->push_back(track);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
GridTrack track;
|
|
113
|
+
if (!parseOneTrack(token, &track)) return false;
|
|
114
|
+
out->push_back(track);
|
|
115
|
+
}
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static bool parseGridLineNumber(const std::string &text, int *out) {
|
|
120
|
+
std::string token = xt_css_lower(xt_css_trim(text));
|
|
121
|
+
if (token == "auto" || token.empty()) {
|
|
122
|
+
*out = 0;
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
if (token.compare(0, 5, "span ") == 0) {
|
|
126
|
+
float span = 0;
|
|
127
|
+
if (!xt_css_parse_number(xt_css_trim(token.substr(5)), &span)) return false;
|
|
128
|
+
*out = (int)span;
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
float number = 0;
|
|
132
|
+
if (!xt_css_parse_number(token, &number)) return false;
|
|
133
|
+
*out = (int)number;
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static void parseGridLine(const std::string &value, int *start, int *end) {
|
|
138
|
+
std::string text = xt_css_trim(value);
|
|
139
|
+
size_t slash = text.find('/');
|
|
140
|
+
*start = 0;
|
|
141
|
+
*end = 0;
|
|
142
|
+
if (slash == std::string::npos) {
|
|
143
|
+
parseGridLineNumber(text, start);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
parseGridLineNumber(text.substr(0, slash), start);
|
|
147
|
+
parseGridLineNumber(text.substr(slash + 1), end);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static bool parseAlignKeyword(const std::string &value, AlignItems *out) {
|
|
151
|
+
std::string text = xt_css_lower(xt_css_trim(value));
|
|
152
|
+
if (text == "stretch") *out = AlignItems::Stretch;
|
|
153
|
+
else if (text == "flex-start" || text == "start" || text == "self-start") *out = AlignItems::FlexStart;
|
|
154
|
+
else if (text == "flex-end" || text == "end" || text == "self-end") *out = AlignItems::FlexEnd;
|
|
155
|
+
else if (text == "center") *out = AlignItems::Center;
|
|
156
|
+
else if (text == "baseline") *out = AlignItems::Baseline;
|
|
157
|
+
else return false;
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
bool xt_style_apply_grid_property(XtStyle *style, const std::string &property,
|
|
163
|
+
const std::string &value) {
|
|
164
|
+
if (property == "align-items") {
|
|
165
|
+
parseAlignKeyword(value, &style->align_items);
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
if (property == "justify-items") {
|
|
169
|
+
parseAlignKeyword(value, &style->justify_items);
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
if (property == "align-self") {
|
|
173
|
+
std::string text = xt_css_lower(xt_css_trim(value));
|
|
174
|
+
if (text == "auto") {
|
|
175
|
+
style->has_align_self = false;
|
|
176
|
+
} else if (parseAlignKeyword(text, &style->align_self)) {
|
|
177
|
+
style->has_align_self = true;
|
|
178
|
+
}
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
if (property == "justify-self") {
|
|
182
|
+
std::string text = xt_css_lower(xt_css_trim(value));
|
|
183
|
+
if (text == "auto") {
|
|
184
|
+
style->has_justify_self = false;
|
|
185
|
+
} else if (parseAlignKeyword(text, &style->justify_self)) {
|
|
186
|
+
style->has_justify_self = true;
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
if (property == "place-items") {
|
|
191
|
+
std::vector<std::string> parts = xt_css_split_whitespace(value);
|
|
192
|
+
if (!parts.empty()) {
|
|
193
|
+
parseAlignKeyword(parts[0], &style->align_items);
|
|
194
|
+
style->justify_items = style->align_items;
|
|
195
|
+
if (parts.size() > 1) parseAlignKeyword(parts[1], &style->justify_items);
|
|
196
|
+
}
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
if (property == "place-self") {
|
|
200
|
+
std::vector<std::string> parts = xt_css_split_whitespace(value);
|
|
201
|
+
if (!parts.empty()) {
|
|
202
|
+
if (parseAlignKeyword(parts[0], &style->align_self)) style->has_align_self = true;
|
|
203
|
+
style->justify_self = style->align_self;
|
|
204
|
+
style->has_justify_self = style->has_align_self;
|
|
205
|
+
if (parts.size() > 1 && parseAlignKeyword(parts[1], &style->justify_self))
|
|
206
|
+
style->has_justify_self = true;
|
|
207
|
+
}
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
if (property == "grid-template-columns") {
|
|
211
|
+
parseGridTracks(value, &style->grid_columns);
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
if (property == "grid-template-rows") {
|
|
215
|
+
parseGridTracks(value, &style->grid_rows);
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
if (property == "grid-column") {
|
|
219
|
+
parseGridLine(value, &style->grid_column_start, &style->grid_column_end);
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
if (property == "grid-row") {
|
|
223
|
+
parseGridLine(value, &style->grid_row_start, &style->grid_row_end);
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
} // namespace xtgui
|