xbintsc 0.3.14 → 0.3.16
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 +22 -22
- package/README.zh-CN.md +20 -19
- package/bin/xbintsc.js +1 -2
- package/dist/src/cli/main.js +8 -5
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/state.js +8 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +5 -6
- package/dist/src/codegen/generator/tables.js +13 -6
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/graph.d.ts +8 -0
- package/dist/src/driver/bundler/graph.js +85 -0
- package/dist/src/driver/bundler/graph.js.map +1 -0
- package/dist/src/driver/bundler/merge.d.ts +11 -0
- package/dist/src/driver/bundler/merge.js +288 -0
- package/dist/src/driver/bundler/merge.js.map +1 -0
- package/dist/src/driver/bundler/resolve.d.ts +12 -0
- package/dist/src/driver/bundler/resolve.js +246 -0
- package/dist/src/driver/bundler/resolve.js.map +1 -0
- package/dist/src/driver/bundler/symbols.d.ts +15 -0
- package/dist/src/driver/bundler/symbols.js +51 -0
- package/dist/src/driver/bundler/symbols.js.map +1 -0
- package/dist/src/driver/bundler/types.d.ts +36 -0
- package/dist/src/driver/bundler/types.js +5 -0
- package/dist/src/driver/bundler/types.js.map +1 -0
- package/dist/src/driver/index.d.ts +1 -2
- package/dist/src/driver/index.js +1 -2
- package/dist/src/driver/index.js.map +1 -1
- package/dist/src/driver/modules.d.ts +9 -16
- package/dist/src/driver/modules.js +8 -644
- package/dist/src/driver/modules.js.map +1 -1
- package/dist/src/driver/paths.d.ts +0 -9
- package/dist/src/driver/paths.js +0 -31
- package/dist/src/driver/paths.js.map +1 -1
- package/dist/src/driver/toolchain-provider.d.ts +10 -10
- package/dist/src/driver/toolchain-provider.js +9 -38
- package/dist/src/driver/toolchain-provider.js.map +1 -1
- package/dist/src/driver/toolchain.js +8 -2
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/extensions/catalog.js +2 -1
- package/dist/src/extensions/catalog.js.map +1 -1
- package/dist/src/extensions/gui/index.d.ts +24 -0
- package/dist/src/extensions/gui/index.js +101 -0
- package/dist/src/extensions/gui/index.js.map +1 -0
- package/dist/src/extensions/native.d.ts +2 -3
- package/dist/src/extensions/native.js +2 -3
- package/dist/src/extensions/native.js.map +1 -1
- package/dist/tests/driver/paths.test.js +1 -9
- package/dist/tests/driver/paths.test.js.map +1 -1
- package/dist/tests/driver/toolchain-provider.test.js +12 -79
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/e2e/compile-basics.test.d.ts +4 -0
- package/dist/tests/e2e/compile-basics.test.js +134 -0
- package/dist/tests/e2e/compile-basics.test.js.map +1 -0
- package/dist/tests/e2e/compile-builtins.test.d.ts +4 -0
- package/dist/tests/e2e/compile-builtins.test.js +191 -0
- package/dist/tests/e2e/compile-builtins.test.js.map +1 -0
- package/dist/tests/e2e/compile-control-flow.test.d.ts +4 -0
- package/dist/tests/e2e/compile-control-flow.test.js +190 -0
- package/dist/tests/e2e/compile-control-flow.test.js.map +1 -0
- package/dist/tests/e2e/compile-modules.test.d.ts +4 -0
- package/dist/tests/e2e/compile-modules.test.js +48 -0
- package/dist/tests/e2e/compile-modules.test.js.map +1 -0
- package/dist/tests/e2e/compile-objects-classes.test.d.ts +4 -0
- package/dist/tests/e2e/compile-objects-classes.test.js +362 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -0
- package/dist/tests/e2e/differential-helpers.d.ts +12 -0
- package/dist/tests/e2e/differential-helpers.js +16 -0
- package/dist/tests/e2e/differential-helpers.js.map +1 -0
- package/dist/tests/e2e/differential-language.test.d.ts +4 -0
- package/dist/tests/e2e/{differential.test.js → differential-language.test.js} +5 -287
- package/dist/tests/e2e/differential-language.test.js.map +1 -0
- package/dist/tests/e2e/differential-symbols.test.d.ts +4 -0
- package/dist/tests/e2e/differential-symbols.test.js +92 -0
- package/dist/tests/e2e/differential-symbols.test.js.map +1 -0
- package/dist/tests/e2e/differential-values.test.d.ts +4 -0
- package/dist/tests/e2e/differential-values.test.js +197 -0
- package/dist/tests/e2e/differential-values.test.js.map +1 -0
- package/dist/tests/e2e/gui.test.d.ts +12 -0
- package/dist/tests/e2e/gui.test.js +493 -0
- package/dist/tests/e2e/gui.test.js.map +1 -0
- package/dist/tests/e2e/native-extensions.test.d.ts +4 -5
- package/dist/tests/e2e/native-extensions.test.js +6 -7
- package/dist/tests/e2e/native-extensions.test.js.map +1 -1
- package/dist/tests/e2e/runtime-coverage-buffer.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-buffer.test.js +97 -0
- package/dist/tests/e2e/runtime-coverage-buffer.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js +149 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-node-modules.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-node-modules.test.js +177 -0
- package/dist/tests/e2e/runtime-coverage-node-modules.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-streams.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-streams.test.js +161 -0
- package/dist/tests/e2e/runtime-coverage-streams.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-system.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-system.test.js +91 -0
- package/dist/tests/e2e/runtime-coverage-system.test.js.map +1 -0
- package/dist/tests/e2e/timers.test.d.ts +7 -0
- package/dist/tests/e2e/timers.test.js +63 -0
- package/dist/tests/e2e/timers.test.js.map +1 -0
- package/package.json +2 -2
- package/runtime/ext_gui/css.cpp +743 -0
- package/runtime/ext_gui/css.h +98 -0
- package/runtime/ext_gui/document.cpp +264 -0
- package/runtime/ext_gui/document.h +89 -0
- package/runtime/ext_gui/dom.cpp +472 -0
- package/runtime/ext_gui/dom.h +71 -0
- package/runtime/ext_gui/gui.cpp +457 -0
- package/runtime/ext_gui/gui_engine.h +81 -0
- package/runtime/ext_gui/image.cpp +125 -0
- package/runtime/ext_gui/image.h +42 -0
- package/runtime/ext_gui/layout.cpp +748 -0
- package/runtime/ext_gui/layout.h +93 -0
- package/runtime/ext_gui/paint.cpp +137 -0
- package/runtime/ext_gui/paint.h +75 -0
- package/runtime/ext_gui/renderer.cpp +959 -0
- package/runtime/ext_gui/renderer.h +68 -0
- package/runtime/ext_gui/stb_image.h +7988 -0
- package/runtime/ext_gui/style.cpp +771 -0
- package/runtime/ext_gui/style.h +117 -0
- package/runtime/ext_gui/text.cpp +378 -0
- package/runtime/ext_gui/text.h +78 -0
- package/runtime/ext_gui/window.cpp +405 -0
- package/runtime/ext_node/fs/fs_common.h +13 -527
- package/runtime/ext_node/fs/meta_ops.c +3 -3
- package/runtime/ext_node/fs/parts/buffer-bridge.inc +6 -0
- package/runtime/ext_node/fs/parts/builtins.inc +69 -0
- package/runtime/ext_node/fs/parts/dir.inc +47 -0
- package/runtime/ext_node/fs/parts/encoding.inc +186 -0
- package/runtime/ext_node/fs/parts/errors.inc +66 -0
- package/runtime/ext_node/fs/parts/flags.inc +39 -0
- package/runtime/ext_node/fs/parts/platform.inc +86 -0
- package/runtime/ext_node/fs/parts/promisify.inc +15 -0
- package/runtime/ext_node/fs/parts/time.inc +9 -0
- package/runtime/rt.h +36 -4
- package/runtime/rt_internal.h +10 -23
- package/runtime/xt_builtins.c +4 -0
- package/runtime/xt_containers/access.inc +168 -0
- package/runtime/xt_containers/arrays.inc +108 -0
- package/runtime/xt_containers/boxes.inc +11 -0
- package/runtime/xt_containers/objects.inc +326 -0
- package/runtime/xt_containers.c +8 -618
- package/runtime/xt_ext.h +0 -1
- package/runtime/xt_io/base64.inc +87 -0
- package/runtime/xt_io/console.inc +119 -0
- package/runtime/xt_io/exception.inc +142 -0
- package/runtime/xt_io/inspect.inc +212 -0
- package/runtime/xt_io.c +11 -556
- package/runtime/xt_loop.c +262 -24
- package/runtime/xt_regex/api.inc +58 -0
- package/runtime/xt_regex/helpers.inc +68 -0
- package/runtime/xt_regex/matcher.inc +248 -0
- package/runtime/xt_regex/parser.inc +236 -0
- package/runtime/xt_regex/types.inc +58 -0
- package/runtime/xt_regex.h +9 -672
- package/runtime/xt_stdlib2/helpers.inc +2 -4
- package/runtime/xt_values/arithmetic.inc +150 -0
- package/runtime/xt_values/comparison.inc +112 -0
- package/runtime/xt_values/constructors.inc +41 -0
- package/runtime/xt_values/conversions.inc +115 -0
- package/runtime/xt_values/number-format.inc +180 -0
- package/runtime/xt_values/strings.inc +22 -0
- package/runtime/xt_values.c +10 -626
- package/scripts/build-gui.ts +317 -0
- package/scripts/build-runtime.ts +7 -8
- package/scripts/coverage-runtime.ts +4 -4
- package/scripts/package-release.ts +12 -84
- package/src/cli/main.ts +8 -4
- package/src/codegen/generator/state.ts +8 -0
- package/src/codegen/generator/tables.ts +13 -6
- package/src/driver/bundler/graph.ts +95 -0
- package/src/driver/bundler/merge.ts +312 -0
- package/src/driver/bundler/resolve.ts +240 -0
- package/src/driver/bundler/symbols.ts +55 -0
- package/src/driver/bundler/types.ts +37 -0
- package/src/driver/index.ts +1 -9
- package/src/driver/modules.ts +9 -693
- package/src/driver/paths.ts +0 -31
- package/src/driver/toolchain-provider.ts +12 -45
- package/src/driver/toolchain.ts +7 -2
- package/src/extensions/catalog.ts +2 -1
- package/src/extensions/gui/index.ts +107 -0
- package/src/extensions/native.ts +2 -3
- package/dist/src/driver/toolchain-download.d.ts +0 -50
- package/dist/src/driver/toolchain-download.js +0 -102
- package/dist/src/driver/toolchain-download.js.map +0 -1
- package/dist/tests/driver/toolchain-download.test.d.ts +0 -1
- package/dist/tests/driver/toolchain-download.test.js +0 -53
- package/dist/tests/driver/toolchain-download.test.js.map +0 -1
- package/dist/tests/e2e/compile.test.d.ts +0 -8
- package/dist/tests/e2e/compile.test.js +0 -893
- package/dist/tests/e2e/compile.test.js.map +0 -1
- package/dist/tests/e2e/differential.test.d.ts +0 -7
- package/dist/tests/e2e/differential.test.js.map +0 -1
- package/dist/tests/e2e/runtime-coverage.test.d.ts +0 -11
- package/dist/tests/e2e/runtime-coverage.test.js +0 -646
- package/dist/tests/e2e/runtime-coverage.test.js.map +0 -1
- package/scripts/fetch-toolchain.ts +0 -101
- package/src/driver/toolchain-download.ts +0 -135
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — CSS parsing, selector matching and value parsing.
|
|
3
|
+
*
|
|
4
|
+
* The CSS subset is the practical slice a UI needs: type/class/id/attribute and
|
|
5
|
+
* a few structural pseudo-class selectors, descendant and child combinators,
|
|
6
|
+
* and the value types used by the cascade (lengths, percentages, colors,
|
|
7
|
+
* keywords). At-rules are skipped.
|
|
8
|
+
*/
|
|
9
|
+
#ifndef XT_GUI_CSS_H
|
|
10
|
+
#define XT_GUI_CSS_H
|
|
11
|
+
|
|
12
|
+
#include <string>
|
|
13
|
+
#include <utility>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
#include "dom.h"
|
|
17
|
+
|
|
18
|
+
namespace xtgui {
|
|
19
|
+
|
|
20
|
+
enum class Unit { Auto, None, Px, Percent, Em, Rem, Vw, Vh, Number, Invalid };
|
|
21
|
+
|
|
22
|
+
struct Length {
|
|
23
|
+
float value = 0.0f;
|
|
24
|
+
Unit unit = Unit::Auto;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
struct Color {
|
|
28
|
+
float r = 0.0f;
|
|
29
|
+
float g = 0.0f;
|
|
30
|
+
float b = 0.0f;
|
|
31
|
+
float a = 1.0f;
|
|
32
|
+
bool valid = false;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
struct Declaration {
|
|
36
|
+
std::string property;
|
|
37
|
+
std::string value;
|
|
38
|
+
bool important = false;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** One `[name op value]` attribute selector. `op` empty = existence test. */
|
|
42
|
+
struct AttrSelector {
|
|
43
|
+
std::string name;
|
|
44
|
+
std::string value;
|
|
45
|
+
std::string op;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** One compound selector: `div.card#main[data-x="1"]:first-child`. */
|
|
49
|
+
struct CompoundSelector {
|
|
50
|
+
std::string tag; // empty = universal (`*`)
|
|
51
|
+
std::string id; // empty = none
|
|
52
|
+
std::vector<std::string> classes;
|
|
53
|
+
std::vector<AttrSelector> attrs;
|
|
54
|
+
std::vector<std::string> pseudoClasses;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
enum class Combinator { Descendant, Child, Adjacent, Sibling };
|
|
58
|
+
|
|
59
|
+
struct ComplexSelector {
|
|
60
|
+
std::vector<CompoundSelector> compounds; // left -> right
|
|
61
|
+
std::vector<Combinator> combinators; // compounds.size() - 1 entries
|
|
62
|
+
int specificity = 0; // packed (a<<20)|(b<<10)|c
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
struct Rule {
|
|
66
|
+
std::vector<ComplexSelector> selectors;
|
|
67
|
+
std::vector<Declaration> declarations;
|
|
68
|
+
int order = 0;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
struct StyleSheet {
|
|
72
|
+
std::vector<Rule> rules;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/** Dynamic element state consulted by `:hover` / `:focus` while matching. */
|
|
76
|
+
struct MatchState {
|
|
77
|
+
const Node *hover = nullptr;
|
|
78
|
+
const Node *focus = nullptr;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
StyleSheet xt_css_parse_stylesheet(const std::string &text);
|
|
82
|
+
std::vector<ComplexSelector> xt_css_parse_selector_list(const std::string &text);
|
|
83
|
+
std::vector<Declaration> xt_css_parse_declarations(const std::string &text);
|
|
84
|
+
|
|
85
|
+
bool xt_css_match(const Node *node, const ComplexSelector &selector);
|
|
86
|
+
bool xt_css_match(const Node *node, const ComplexSelector &selector, const MatchState &state);
|
|
87
|
+
|
|
88
|
+
Length xt_css_parse_length(const std::string &value);
|
|
89
|
+
Color xt_css_parse_color(const std::string &value);
|
|
90
|
+
bool xt_css_parse_number(const std::string &value, float *out);
|
|
91
|
+
|
|
92
|
+
std::string xt_css_trim(const std::string &text);
|
|
93
|
+
std::vector<std::string> xt_css_split_whitespace(const std::string &text);
|
|
94
|
+
std::string xt_css_lower(const std::string &text);
|
|
95
|
+
|
|
96
|
+
} // namespace xtgui
|
|
97
|
+
|
|
98
|
+
#endif /* XT_GUI_CSS_H */
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — document model implementation.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
#include "document.h"
|
|
6
|
+
|
|
7
|
+
namespace xtgui {
|
|
8
|
+
|
|
9
|
+
void XtDocument::collectStyleText(const Node *node, std::string *out) const {
|
|
10
|
+
if (node->isElement() && node->isTag("style")) {
|
|
11
|
+
for (const std::unique_ptr<Node> &child : node->children) {
|
|
12
|
+
if (child->isText()) {
|
|
13
|
+
*out += child->text;
|
|
14
|
+
*out += "\n";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
/* A <link rel="stylesheet"> would need I/O; skip it (and everything else). */
|
|
20
|
+
for (const std::unique_ptr<Node> &child : node->children) {
|
|
21
|
+
collectStyleText(child.get(), out);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
void XtDocument::load(const std::string &html, float viewportWidth, float viewportHeight) {
|
|
26
|
+
root_ = xt_html_parse(html);
|
|
27
|
+
hover_ = nullptr;
|
|
28
|
+
focus_ = nullptr;
|
|
29
|
+
styles_.clear();
|
|
30
|
+
display_.clear();
|
|
31
|
+
transitions_.clear();
|
|
32
|
+
clock_ms_ = 0.0;
|
|
33
|
+
std::string css;
|
|
34
|
+
collectStyleText(root_.get(), &css);
|
|
35
|
+
sheet_ = xt_css_parse_stylesheet(css);
|
|
36
|
+
restyle(viewportWidth, viewportHeight);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
void XtDocument::restyle(float viewportWidth, float viewportHeight) {
|
|
40
|
+
viewportWidth_ = viewportWidth;
|
|
41
|
+
viewportHeight_ = viewportHeight;
|
|
42
|
+
if (root_ != nullptr) {
|
|
43
|
+
MatchState state;
|
|
44
|
+
state.hover = hover_;
|
|
45
|
+
state.focus = focus_;
|
|
46
|
+
xt_style_compute(root_.get(), sheet_, styles_, viewportWidth, viewportHeight, state);
|
|
47
|
+
startTransitions();
|
|
48
|
+
updateDisplay();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
namespace {
|
|
53
|
+
|
|
54
|
+
float easeProgress(float t, TimingFunction timing) {
|
|
55
|
+
if (t <= 0.0f) return 0.0f;
|
|
56
|
+
if (t >= 1.0f) return 1.0f;
|
|
57
|
+
switch (timing) {
|
|
58
|
+
case TimingFunction::Linear:
|
|
59
|
+
return t;
|
|
60
|
+
case TimingFunction::EaseIn:
|
|
61
|
+
return t * t * t;
|
|
62
|
+
case TimingFunction::EaseOut: {
|
|
63
|
+
float u = 1.0f - t;
|
|
64
|
+
return 1.0f - u * u * u;
|
|
65
|
+
}
|
|
66
|
+
case TimingFunction::EaseInOut:
|
|
67
|
+
return t < 0.5f ? 4.0f * t * t * t : 1.0f - 4.0f * (1.0f - t) * (1.0f - t) * (1.0f - t);
|
|
68
|
+
case TimingFunction::Ease:
|
|
69
|
+
default:
|
|
70
|
+
return t * t * (3.0f - 2.0f * t); // smoothstep approximation of `ease`
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
float mix(float a, float b, float t) { return a + (b - a) * t; }
|
|
75
|
+
|
|
76
|
+
const float *colorOf(const XtStyle &style, const std::string &property) {
|
|
77
|
+
static const float transparent[4] = {0, 0, 0, 0};
|
|
78
|
+
if (property == "background-color") {
|
|
79
|
+
return style.has_background ? &style.background.r : transparent;
|
|
80
|
+
}
|
|
81
|
+
if (property == "border-color") {
|
|
82
|
+
return style.has_border_color ? &style.border_color.r : transparent;
|
|
83
|
+
}
|
|
84
|
+
return &style.color.r;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
float numberOf(const XtStyle &style, const std::string &property) {
|
|
88
|
+
if (property == "border-radius") return style.border_radius;
|
|
89
|
+
return 0.0f;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
bool isNumberProperty(const std::string &property) { return property == "border-radius"; }
|
|
93
|
+
|
|
94
|
+
bool specMatches(const std::string &spec, const std::string &property) {
|
|
95
|
+
return spec == "all" || spec == property;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
void applyColor(XtStyle *style, const std::string &property, const float *rgba) {
|
|
99
|
+
if (property == "background-color") {
|
|
100
|
+
style->background = Color{rgba[0], rgba[1], rgba[2], rgba[3], true};
|
|
101
|
+
style->has_background = rgba[3] > 0.0f;
|
|
102
|
+
} else if (property == "border-color") {
|
|
103
|
+
style->border_color = Color{rgba[0], rgba[1], rgba[2], rgba[3], true};
|
|
104
|
+
style->has_border_color = rgba[3] > 0.0f;
|
|
105
|
+
} else {
|
|
106
|
+
style->color = Color{rgba[0], rgba[1], rgba[2], rgba[3], true};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
void applyNumber(XtStyle *style, const std::string &property, float value) {
|
|
111
|
+
if (property == "border-radius") style->border_radius = value;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
} // namespace
|
|
115
|
+
|
|
116
|
+
void XtDocument::startTransitions() {
|
|
117
|
+
if (display_.empty()) return; // first style computation: nothing to animate from
|
|
118
|
+
static const char *const kProperties[] = {"background-color", "color", "border-color", "border-radius"};
|
|
119
|
+
for (auto &entry : styles_) {
|
|
120
|
+
const Node *node = entry.first;
|
|
121
|
+
const XtStyle &target = entry.second;
|
|
122
|
+
if (target.transitions.empty()) continue;
|
|
123
|
+
auto prev_it = display_.find(node);
|
|
124
|
+
if (prev_it == display_.end()) continue;
|
|
125
|
+
const XtStyle ¤t = prev_it->second;
|
|
126
|
+
for (const TransitionSpec &spec : target.transitions) {
|
|
127
|
+
if (spec.duration <= 0.0f) continue;
|
|
128
|
+
for (const char *property : kProperties) {
|
|
129
|
+
if (!specMatches(spec.property, property)) continue;
|
|
130
|
+
ActiveTransition running;
|
|
131
|
+
running.node = node;
|
|
132
|
+
running.property = property;
|
|
133
|
+
running.timing = spec.timing;
|
|
134
|
+
running.duration_ms = spec.duration * 1000.0f;
|
|
135
|
+
running.delay_ms = spec.delay * 1000.0f;
|
|
136
|
+
running.start_ms = (float)clock_ms_ + running.delay_ms;
|
|
137
|
+
if (isNumberProperty(property)) {
|
|
138
|
+
float from = numberOf(current, property);
|
|
139
|
+
float to = numberOf(target, property);
|
|
140
|
+
if (from == to) continue;
|
|
141
|
+
running.is_color = false;
|
|
142
|
+
running.from_number = from;
|
|
143
|
+
running.to_number = to;
|
|
144
|
+
} else {
|
|
145
|
+
const float *from = colorOf(current, property);
|
|
146
|
+
const float *to = colorOf(target, property);
|
|
147
|
+
if (from[0] == to[0] && from[1] == to[1] && from[2] == to[2] && from[3] == to[3]) continue;
|
|
148
|
+
running.is_color = true;
|
|
149
|
+
for (int i = 0; i < 4; i++) {
|
|
150
|
+
running.from_color[i] = from[i];
|
|
151
|
+
running.to_color[i] = to[i];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/* Retarget an existing transition (mid-flight) instead of stacking. */
|
|
155
|
+
bool replaced = false;
|
|
156
|
+
for (ActiveTransition &existing : transitions_) {
|
|
157
|
+
if (existing.node == node && existing.property == property) {
|
|
158
|
+
existing = running;
|
|
159
|
+
replaced = true;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (!replaced) transitions_.push_back(running);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
void XtDocument::updateDisplay() {
|
|
170
|
+
display_ = styles_;
|
|
171
|
+
for (size_t i = 0; i < transitions_.size();) {
|
|
172
|
+
ActiveTransition &transition = transitions_[i];
|
|
173
|
+
auto node_it = display_.find(transition.node);
|
|
174
|
+
if (node_it == display_.end()) {
|
|
175
|
+
transitions_.erase(transitions_.begin() + (long)i);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
float elapsed = (float)clock_ms_ - transition.start_ms;
|
|
179
|
+
float progress = transition.duration_ms > 0.0f ? elapsed / transition.duration_ms : 1.0f;
|
|
180
|
+
if (progress > 1.0f) progress = 1.0f;
|
|
181
|
+
float eased = easeProgress(progress, transition.timing);
|
|
182
|
+
XtStyle &style = node_it->second;
|
|
183
|
+
if (transition.is_color) {
|
|
184
|
+
float rgba[4];
|
|
185
|
+
for (int c = 0; c < 4; c++) rgba[c] = mix(transition.from_color[c], transition.to_color[c], eased);
|
|
186
|
+
applyColor(&style, transition.property, rgba);
|
|
187
|
+
} else {
|
|
188
|
+
applyNumber(&style, transition.property, mix(transition.from_number, transition.to_number, eased));
|
|
189
|
+
}
|
|
190
|
+
if (progress >= 1.0f) {
|
|
191
|
+
transitions_.erase(transitions_.begin() + (long)i);
|
|
192
|
+
} else {
|
|
193
|
+
i++;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
layout_.compute(root_.get(), display_, viewportWidth_, viewportHeight_);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
bool XtDocument::advance(double delta_ms) {
|
|
200
|
+
clock_ms_ += delta_ms;
|
|
201
|
+
if (transitions_.empty()) return false;
|
|
202
|
+
updateDisplay();
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
bool XtDocument::setHover(const Node *node) {
|
|
207
|
+
if (hover_ == node) return false;
|
|
208
|
+
hover_ = node;
|
|
209
|
+
restyle(viewportWidth_, viewportHeight_);
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
bool XtDocument::setFocus(const Node *node) {
|
|
214
|
+
if (focus_ == node) return false;
|
|
215
|
+
focus_ = node;
|
|
216
|
+
restyle(viewportWidth_, viewportHeight_);
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const XtStyle *XtDocument::styleOf(const Node *node) const {
|
|
221
|
+
auto it = display_.find(node);
|
|
222
|
+
if (it != display_.end()) return &it->second;
|
|
223
|
+
it = styles_.find(node);
|
|
224
|
+
return (it == styles_.end()) ? nullptr : &it->second;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const LayoutBox *XtDocument::boxOf(const Node *node) const { return layout_.find(node); }
|
|
228
|
+
|
|
229
|
+
std::vector<const Node *> XtDocument::querySelectorAll(const std::string &selectorText) const {
|
|
230
|
+
std::vector<const Node *> matches;
|
|
231
|
+
if (root_ == nullptr) return matches;
|
|
232
|
+
std::vector<ComplexSelector> selectors = xt_css_parse_selector_list(selectorText);
|
|
233
|
+
if (selectors.empty()) return matches;
|
|
234
|
+
std::vector<const Node *> stack;
|
|
235
|
+
stack.push_back(root_.get());
|
|
236
|
+
while (!stack.empty()) {
|
|
237
|
+
const Node *node = stack.back();
|
|
238
|
+
stack.pop_back();
|
|
239
|
+
if (node->isElement()) {
|
|
240
|
+
for (const ComplexSelector &selector : selectors) {
|
|
241
|
+
if (xt_css_match(node, selector)) {
|
|
242
|
+
matches.push_back(node);
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/* Push children in reverse so the traversal stays in document order. */
|
|
248
|
+
for (size_t i = node->children.size(); i-- > 0;) {
|
|
249
|
+
stack.push_back(node->children[i].get());
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return matches;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const Node *XtDocument::querySelector(const std::string &selectorText) const {
|
|
256
|
+
std::vector<const Node *> matches = querySelectorAll(selectorText);
|
|
257
|
+
return matches.empty() ? nullptr : matches.front();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
std::string XtDocument::toDebugString() const {
|
|
261
|
+
return root_ == nullptr ? std::string() : xt_dom_to_string(root_.get());
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
} // namespace xtgui
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — the document model.
|
|
3
|
+
*
|
|
4
|
+
* An `XtDocument` owns the DOM tree, the author stylesheet gathered from the
|
|
5
|
+
* document's `<style>` elements, and the computed style of every element. It is
|
|
6
|
+
* the hand-off point from parsing to layout.
|
|
7
|
+
*/
|
|
8
|
+
#ifndef XT_GUI_DOCUMENT_H
|
|
9
|
+
#define XT_GUI_DOCUMENT_H
|
|
10
|
+
|
|
11
|
+
#include <memory>
|
|
12
|
+
#include <string>
|
|
13
|
+
#include <unordered_map>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
#include "css.h"
|
|
17
|
+
#include "dom.h"
|
|
18
|
+
#include "layout.h"
|
|
19
|
+
#include "style.h"
|
|
20
|
+
|
|
21
|
+
namespace xtgui {
|
|
22
|
+
|
|
23
|
+
/** A running CSS transition (only paint-affecting properties are supported). */
|
|
24
|
+
struct ActiveTransition {
|
|
25
|
+
const Node *node = nullptr;
|
|
26
|
+
std::string property;
|
|
27
|
+
bool is_color = false;
|
|
28
|
+
float from_color[4] = {0, 0, 0, 0};
|
|
29
|
+
float to_color[4] = {0, 0, 0, 0};
|
|
30
|
+
float from_number = 0;
|
|
31
|
+
float to_number = 0;
|
|
32
|
+
float start_ms = 0; // when interpolation begins (after `delay`)
|
|
33
|
+
float duration_ms = 0;
|
|
34
|
+
float delay_ms = 0;
|
|
35
|
+
TimingFunction timing = TimingFunction::Ease;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
class XtDocument {
|
|
39
|
+
public:
|
|
40
|
+
/** Parse `html`, gather `<style>` rules and compute styles for the viewport. */
|
|
41
|
+
void load(const std::string &html, float viewportWidth, float viewportHeight);
|
|
42
|
+
/** Recompute styles (e.g. after the viewport size changed). */
|
|
43
|
+
void restyle(float viewportWidth, float viewportHeight);
|
|
44
|
+
/** Advance the animation clock by `delta_ms`; recomputes the displayed styles
|
|
45
|
+
* and layout when a transition is running. Returns true when anything
|
|
46
|
+
* transitioned (the caller should repaint). */
|
|
47
|
+
bool advance(double delta_ms);
|
|
48
|
+
/** True while at least one transition is running. */
|
|
49
|
+
bool animating() const { return !transitions_.empty(); }
|
|
50
|
+
/** Set the hovered/focused element; restyles when it changed. Returns true
|
|
51
|
+
* when the dynamic state (and therefore the styles) changed. */
|
|
52
|
+
bool setHover(const Node *node);
|
|
53
|
+
bool setFocus(const Node *node);
|
|
54
|
+
const Node *hover() const { return hover_; }
|
|
55
|
+
const Node *focus() const { return focus_; }
|
|
56
|
+
|
|
57
|
+
const Node *root() const { return root_.get(); }
|
|
58
|
+
const StyleSheet &sheet() const { return sheet_; }
|
|
59
|
+
const XtStyle *styleOf(const Node *node) const;
|
|
60
|
+
const LayoutBox *boxOf(const Node *node) const;
|
|
61
|
+
const LayoutTree &layout() const { return layout_; }
|
|
62
|
+
/** First element matching a CSS selector (a small querySelector). */
|
|
63
|
+
const Node *querySelector(const std::string &selectorText) const;
|
|
64
|
+
/** Elements matching a CSS selector, in document order. */
|
|
65
|
+
std::vector<const Node *> querySelectorAll(const std::string &selectorText) const;
|
|
66
|
+
/** Serialized DOM, for diagnostics. */
|
|
67
|
+
std::string toDebugString() const;
|
|
68
|
+
|
|
69
|
+
private:
|
|
70
|
+
void collectStyleText(const Node *node, std::string *out) const;
|
|
71
|
+
void startTransitions();
|
|
72
|
+
void updateDisplay();
|
|
73
|
+
|
|
74
|
+
std::unique_ptr<Node> root_;
|
|
75
|
+
StyleSheet sheet_;
|
|
76
|
+
std::unordered_map<const Node *, XtStyle> styles_; // target (cascade)
|
|
77
|
+
std::unordered_map<const Node *, XtStyle> display_; // after transitions
|
|
78
|
+
std::vector<ActiveTransition> transitions_;
|
|
79
|
+
double clock_ms_ = 0.0;
|
|
80
|
+
LayoutTree layout_;
|
|
81
|
+
const Node *hover_ = nullptr;
|
|
82
|
+
const Node *focus_ = nullptr;
|
|
83
|
+
float viewportWidth_ = 0.0f;
|
|
84
|
+
float viewportHeight_ = 0.0f;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
} // namespace xtgui
|
|
88
|
+
|
|
89
|
+
#endif /* XT_GUI_DOCUMENT_H */
|