xbintsc 0.5.45 → 0.5.52

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.
Files changed (286) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +5 -4
  3. package/README.zh-CN.md +4 -3
  4. package/dist/src/cli/extensions.d.ts +24 -0
  5. package/dist/src/cli/extensions.js +62 -0
  6. package/dist/src/cli/extensions.js.map +1 -0
  7. package/dist/src/cli/help.d.ts +1 -1
  8. package/dist/src/cli/help.js +3 -1
  9. package/dist/src/cli/help.js.map +1 -1
  10. package/dist/src/cli/hints.js +8 -3
  11. package/dist/src/cli/hints.js.map +1 -1
  12. package/dist/src/cli/main.js +21 -28
  13. package/dist/src/cli/main.js.map +1 -1
  14. package/dist/src/diagnostics/diagnostic.d.ts +3 -1
  15. package/dist/src/diagnostics/diagnostic.js +2 -0
  16. package/dist/src/diagnostics/diagnostic.js.map +1 -1
  17. package/dist/src/driver/build-key.d.ts +2 -0
  18. package/dist/src/driver/build-key.js +1 -0
  19. package/dist/src/driver/build-key.js.map +1 -1
  20. package/dist/src/driver/compiler.js +61 -8
  21. package/dist/src/driver/compiler.js.map +1 -1
  22. package/dist/src/driver/config.d.ts +4 -0
  23. package/dist/src/driver/config.js +2 -0
  24. package/dist/src/driver/config.js.map +1 -1
  25. package/dist/src/driver/link-features.d.ts +37 -0
  26. package/dist/src/driver/link-features.js +123 -0
  27. package/dist/src/driver/link-features.js.map +1 -0
  28. package/dist/src/driver/options.d.ts +10 -0
  29. package/dist/src/driver/runtime-objects.d.ts +5 -0
  30. package/dist/src/driver/runtime-objects.js +20 -1
  31. package/dist/src/driver/runtime-objects.js.map +1 -1
  32. package/dist/src/driver/usage.d.ts +23 -0
  33. package/dist/src/driver/usage.js +353 -0
  34. package/dist/src/driver/usage.js.map +1 -0
  35. package/dist/src/driver/version.d.ts +1 -1
  36. package/dist/src/driver/version.js +1 -1
  37. package/dist/src/extensions/catalog.js +1 -2
  38. package/dist/src/extensions/catalog.js.map +1 -1
  39. package/dist/src/extensions/gui/features.d.ts +34 -0
  40. package/dist/src/extensions/gui/features.js +165 -0
  41. package/dist/src/extensions/gui/features.js.map +1 -0
  42. package/dist/src/extensions/gui/index.js +10 -4
  43. package/dist/src/extensions/gui/index.js.map +1 -1
  44. package/dist/src/extensions/link-feature.d.ts +70 -0
  45. package/dist/src/extensions/link-feature.js +18 -0
  46. package/dist/src/extensions/link-feature.js.map +1 -0
  47. package/dist/src/extensions/registry.d.ts +24 -4
  48. package/dist/src/extensions/registry.js +26 -3
  49. package/dist/src/extensions/registry.js.map +1 -1
  50. package/dist/src/index.d.ts +3 -2
  51. package/dist/src/index.js +0 -1
  52. package/dist/src/index.js.map +1 -1
  53. package/dist/tests/cli/hints.test.js +5 -7
  54. package/dist/tests/cli/hints.test.js.map +1 -1
  55. package/dist/tests/driver/link-features.test.js +105 -0
  56. package/dist/tests/driver/link-features.test.js.map +1 -0
  57. package/dist/tests/driver/usage.test.d.ts +1 -0
  58. package/dist/tests/driver/usage.test.js +88 -0
  59. package/dist/tests/driver/usage.test.js.map +1 -0
  60. package/dist/tests/e2e/gui-audio.test.js +4 -4
  61. package/dist/tests/e2e/gui-audio.test.js.map +1 -1
  62. package/dist/tests/e2e/gui-css-coverage.test.d.ts +9 -0
  63. package/dist/tests/e2e/gui-css-coverage.test.js +333 -0
  64. package/dist/tests/e2e/gui-css-coverage.test.js.map +1 -0
  65. package/dist/tests/e2e/gui-css-features.test.d.ts +6 -0
  66. package/dist/tests/e2e/gui-css-features.test.js +270 -0
  67. package/dist/tests/e2e/gui-css-features.test.js.map +1 -0
  68. package/dist/tests/e2e/gui-effects.test.js +92 -11
  69. package/dist/tests/e2e/gui-effects.test.js.map +1 -1
  70. package/dist/tests/e2e/gui-feature-linking.test.d.ts +9 -0
  71. package/dist/tests/e2e/gui-feature-linking.test.js +68 -0
  72. package/dist/tests/e2e/gui-feature-linking.test.js.map +1 -0
  73. package/dist/tests/e2e/gui-font-face.test.d.ts +10 -0
  74. package/dist/tests/e2e/gui-font-face.test.js +142 -0
  75. package/dist/tests/e2e/gui-font-face.test.js.map +1 -0
  76. package/dist/tests/e2e/gui-fonts.test.d.ts +10 -0
  77. package/dist/tests/e2e/gui-fonts.test.js +150 -0
  78. package/dist/tests/e2e/gui-fonts.test.js.map +1 -0
  79. package/dist/tests/e2e/gui-helpers.d.ts +10 -1
  80. package/dist/tests/e2e/gui-helpers.js +6 -0
  81. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  82. package/dist/tests/e2e/gui-layout.test.js +7 -3
  83. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  84. package/dist/tests/e2e/gui-media-disabled.test.d.ts +5 -4
  85. package/dist/tests/e2e/gui-media-disabled.test.js +7 -6
  86. package/dist/tests/e2e/gui-media-disabled.test.js.map +1 -1
  87. package/dist/tests/e2e/gui-media.test.d.ts +5 -4
  88. package/dist/tests/e2e/gui-media.test.js +9 -9
  89. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  90. package/dist/tests/e2e/gui-paint.test.js +5 -2
  91. package/dist/tests/e2e/gui-paint.test.js.map +1 -1
  92. package/dist/tests/e2e/gui-shaping.test.d.ts +5 -0
  93. package/dist/tests/e2e/gui-shaping.test.js +47 -0
  94. package/dist/tests/e2e/gui-shaping.test.js.map +1 -0
  95. package/dist/tests/e2e/gui-track-sizing.test.d.ts +6 -0
  96. package/dist/tests/e2e/gui-track-sizing.test.js +173 -0
  97. package/dist/tests/e2e/gui-track-sizing.test.js.map +1 -0
  98. package/dist/tests/e2e/gui-transform.test.js +3 -2
  99. package/dist/tests/e2e/gui-transform.test.js.map +1 -1
  100. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.d.ts +18 -0
  101. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js +120 -0
  102. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js.map +1 -0
  103. package/dist/tests/extensions/gui-features.test.d.ts +1 -0
  104. package/dist/tests/extensions/gui-features.test.js +175 -0
  105. package/dist/tests/extensions/gui-features.test.js.map +1 -0
  106. package/doc/ai/cli.md +5 -1
  107. package/doc/ai/extensions.md +56 -26
  108. package/doc/ai/troubleshooting.md +10 -7
  109. package/doc/ai/zh-CN/cli.md +5 -1
  110. package/doc/ai/zh-CN/extensions.md +48 -22
  111. package/doc/ai/zh-CN/troubleshooting.md +6 -5
  112. package/doc/gui/animation.md +39 -9
  113. package/doc/gui/api.md +2 -1
  114. package/doc/gui/build.md +137 -19
  115. package/doc/gui/canvas.md +1 -1
  116. package/doc/gui/dom.md +20 -4
  117. package/doc/gui/known-issues.md +63 -26
  118. package/doc/gui/layout.md +31 -8
  119. package/doc/gui/media.md +13 -7
  120. package/doc/gui/rendering.md +114 -39
  121. package/doc/gui/runtime.md +5 -0
  122. package/doc/gui/webgl.md +24 -6
  123. package/doc/gui.md +110 -47
  124. package/doc/implemented/cli.md +2 -0
  125. package/doc/implemented/extensions.md +9 -3
  126. package/doc/npm-package.md +4 -4
  127. package/doc/xbintsc.config.schema.json +10 -0
  128. package/doc/zh-CN/gui/animation.md +33 -10
  129. package/doc/zh-CN/gui/api.md +2 -1
  130. package/doc/zh-CN/gui/build.md +110 -16
  131. package/doc/zh-CN/gui/canvas.md +1 -1
  132. package/doc/zh-CN/gui/dom.md +16 -3
  133. package/doc/zh-CN/gui/known-issues.md +45 -15
  134. package/doc/zh-CN/gui/layout.md +23 -6
  135. package/doc/zh-CN/gui/media.md +11 -6
  136. package/doc/zh-CN/gui/rendering.md +84 -26
  137. package/doc/zh-CN/gui/runtime.md +3 -0
  138. package/doc/zh-CN/gui/webgl.md +3 -3
  139. package/doc/zh-CN/gui.md +92 -41
  140. package/doc/zh-CN/implemented/cli.md +2 -0
  141. package/doc/zh-CN/implemented/extensions.md +9 -3
  142. package/doc/zh-CN/npm-package.md +4 -3
  143. package/package.json +7 -7
  144. package/runtime/ext_gui/animation_values.cpp +425 -0
  145. package/runtime/ext_gui/animation_values.h +64 -0
  146. package/runtime/ext_gui/browser.cpp +22 -15
  147. package/runtime/ext_gui/browser_loop.cpp +13 -1
  148. package/runtime/ext_gui/canvas.h +1 -1
  149. package/runtime/ext_gui/canvas_raster.cpp +1 -1
  150. package/runtime/ext_gui/canvas_text.cpp +1 -1
  151. package/runtime/ext_gui/css.cpp +172 -21
  152. package/runtime/ext_gui/css.h +75 -4
  153. package/runtime/ext_gui/css_match.cpp +4 -0
  154. package/runtime/ext_gui/css_sheet.cpp +218 -2
  155. package/runtime/ext_gui/document.cpp +42 -192
  156. package/runtime/ext_gui/document.h +25 -12
  157. package/runtime/ext_gui/document_animation.cpp +428 -0
  158. package/runtime/ext_gui/dom_api_canvas.cpp +15 -8
  159. package/runtime/ext_gui/dom_api_canvas_extra.cpp +44 -20
  160. package/runtime/ext_gui/dom_api_canvas_image.cpp +3 -1
  161. package/runtime/ext_gui/dom_api_node_read.cpp +10 -0
  162. package/runtime/ext_gui/dom_api_offscreen.cpp +3 -1
  163. package/runtime/ext_gui/dom_api_style.cpp +7 -0
  164. package/runtime/ext_gui/dom_api_webgl_internal.h +4 -0
  165. package/runtime/ext_gui/dom_api_webgl_source.cpp +5 -2
  166. package/runtime/ext_gui/effect_values.cpp +245 -21
  167. package/runtime/ext_gui/gpu_bridge.c +28 -0
  168. package/runtime/ext_gui/gpu_feature.cpp +30 -0
  169. package/runtime/ext_gui/gpu_shader.cpp +22 -0
  170. package/runtime/ext_gui/gpu_shader_api.h +50 -0
  171. package/runtime/ext_gui/gradient_values.cpp +24 -0
  172. package/runtime/ext_gui/grid_values.cpp +23 -0
  173. package/runtime/ext_gui/gui.cpp +6 -37
  174. package/runtime/ext_gui/gui_canvas.cpp +14 -6
  175. package/runtime/ext_gui/gui_cursor.cpp +50 -0
  176. package/runtime/ext_gui/gui_engine.h +1 -18
  177. package/runtime/ext_gui/gui_events.cpp +19 -6
  178. package/runtime/ext_gui/gui_internal.h +3 -0
  179. package/runtime/ext_gui/gui_webgl.cpp +25 -10
  180. package/runtime/ext_gui/image_api.cpp +18 -0
  181. package/runtime/ext_gui/image_api.h +43 -0
  182. package/runtime/ext_gui/image_bitmap_browser.cpp +20 -11
  183. package/runtime/ext_gui/image_bridge.c +22 -0
  184. package/runtime/ext_gui/image_feature.cpp +107 -0
  185. package/runtime/ext_gui/inline_svg.cpp +95 -8
  186. package/runtime/ext_gui/inline_svg.h +5 -1
  187. package/runtime/ext_gui/layout.cpp +15 -9
  188. package/runtime/ext_gui/layout.h +8 -2
  189. package/runtime/ext_gui/layout_flex.cpp +134 -29
  190. package/runtime/ext_gui/layout_flow.cpp +58 -7
  191. package/runtime/ext_gui/layout_flow_internal.h +39 -4
  192. package/runtime/ext_gui/layout_grid.cpp +31 -0
  193. package/runtime/ext_gui/layout_inline.cpp +26 -2
  194. package/runtime/ext_gui/layout_vertical.cpp +196 -0
  195. package/runtime/ext_gui/media.cpp +4 -2
  196. package/runtime/ext_gui/media.h +3 -3
  197. package/runtime/ext_gui/media_api.h +7 -6
  198. package/runtime/ext_gui/paint.cpp +67 -9
  199. package/runtime/ext_gui/paint.h +61 -8
  200. package/runtime/ext_gui/paint_backdrop.cpp +211 -0
  201. package/runtime/ext_gui/paint_background.cpp +175 -97
  202. package/runtime/ext_gui/paint_effects.cpp +12 -6
  203. package/runtime/ext_gui/paint_internal.h +27 -0
  204. package/runtime/ext_gui/paint_outline.cpp +44 -0
  205. package/runtime/ext_gui/paint_shadow.cpp +368 -0
  206. package/runtime/ext_gui/renderer.cpp +15 -7
  207. package/runtime/ext_gui/renderer.h +11 -2
  208. package/runtime/ext_gui/renderer_backdrop.cpp +374 -0
  209. package/runtime/ext_gui/renderer_bitmap.cpp +82 -0
  210. package/runtime/ext_gui/renderer_gradient.cpp +51 -17
  211. package/runtime/ext_gui/renderer_image.cpp +19 -7
  212. package/runtime/ext_gui/renderer_internal.h +21 -0
  213. package/runtime/ext_gui/renderer_layer.cpp +37 -6
  214. package/runtime/ext_gui/renderer_text.cpp +2 -1
  215. package/runtime/ext_gui/stb_truetype.h +5077 -0
  216. package/runtime/ext_gui/style.cpp +62 -8
  217. package/runtime/ext_gui/style.h +111 -4
  218. package/runtime/ext_gui/style_cascade.cpp +83 -0
  219. package/runtime/ext_gui/style_internal.h +13 -1
  220. package/runtime/ext_gui/style_length.cpp +92 -0
  221. package/runtime/ext_gui/style_print.cpp +83 -2
  222. package/runtime/ext_gui/style_svg.cpp +40 -0
  223. package/runtime/ext_gui/style_text.cpp +302 -0
  224. package/runtime/ext_gui/style_ui.cpp +150 -0
  225. package/runtime/ext_gui/style_values.cpp +76 -62
  226. package/runtime/ext_gui/style_values_internal.h +23 -0
  227. package/runtime/ext_gui/text.cpp +27 -499
  228. package/runtime/ext_gui/text.h +18 -9
  229. package/runtime/ext_gui/text_api.h +45 -0
  230. package/runtime/ext_gui/text_bridge.c +23 -0
  231. package/runtime/ext_gui/text_cache.cpp +12 -3
  232. package/runtime/ext_gui/text_font_db.cpp +427 -0
  233. package/runtime/ext_gui/text_font_paths.cpp +250 -0
  234. package/runtime/ext_gui/text_font_paths.h +39 -0
  235. package/runtime/ext_gui/text_font_sfnt.cpp +396 -0
  236. package/runtime/ext_gui/text_font_sfnt.h +44 -0
  237. package/runtime/ext_gui/text_fonts.cpp +348 -0
  238. package/runtime/ext_gui/text_fonts.h +78 -0
  239. package/runtime/ext_gui/text_fonts_internal.h +77 -0
  240. package/runtime/ext_gui/text_glyph_cache.cpp +2 -2
  241. package/runtime/ext_gui/text_hb.cpp +438 -0
  242. package/runtime/ext_gui/text_stb.cpp +369 -0
  243. package/runtime/ext_gui/transform.cpp +107 -9
  244. package/runtime/ext_gui/transform.h +5 -0
  245. package/runtime/ext_gui/webgl_api.cpp +19 -0
  246. package/runtime/ext_gui/webgl_api.h +53 -0
  247. package/runtime/ext_gui/webgl_bridge.c +25 -0
  248. package/runtime/ext_gui/webgl_draw_setup.cpp +42 -1
  249. package/runtime/ext_gui/webgl_feature.cpp +82 -0
  250. package/runtime/ext_gui/webgl_feature.h +28 -0
  251. package/runtime/ext_gui/webgl_msl.cpp +41 -0
  252. package/runtime/ext_gui/webgl_msl.h +6 -0
  253. package/runtime/ext_gui/webgl_shader.cpp +40 -32
  254. package/runtime/ext_gui/window.cpp +7 -7
  255. package/runtime/ext_media/decode_audio.cpp +1 -1
  256. package/runtime/ext_media/media.cpp +1 -1
  257. package/runtime/ext_media/media_bridge.c +7 -7
  258. package/scripts/build-gui.ts +97 -98
  259. package/scripts/check-file-length.ts +1 -0
  260. package/scripts/gui-build-options.ts +122 -0
  261. package/src/cli/extensions.ts +72 -0
  262. package/src/cli/help.ts +3 -1
  263. package/src/cli/hints.ts +9 -3
  264. package/src/cli/main.ts +21 -27
  265. package/src/diagnostics/diagnostic.ts +2 -0
  266. package/src/driver/build-key.ts +3 -0
  267. package/src/driver/compiler.ts +63 -8
  268. package/src/driver/config.ts +6 -0
  269. package/src/driver/link-features.ts +163 -0
  270. package/src/driver/options.ts +10 -0
  271. package/src/driver/runtime-objects.ts +26 -1
  272. package/src/driver/usage.ts +376 -0
  273. package/src/driver/version.ts +1 -1
  274. package/src/extensions/catalog.ts +1 -2
  275. package/src/extensions/gui/features.ts +166 -0
  276. package/src/extensions/gui/index.ts +10 -4
  277. package/src/extensions/link-feature.ts +72 -0
  278. package/src/extensions/registry.ts +45 -4
  279. package/src/index.ts +3 -2
  280. package/dist/src/extensions/media/index.d.ts +0 -28
  281. package/dist/src/extensions/media/index.js +0 -59
  282. package/dist/src/extensions/media/index.js.map +0 -1
  283. package/dist/tests/extensions/media.test.js +0 -63
  284. package/dist/tests/extensions/media.test.js.map +0 -1
  285. package/src/extensions/media/index.ts +0 -65
  286. /package/dist/tests/{extensions/media.test.d.ts → driver/link-features.test.d.ts} +0 -0
@@ -0,0 +1,425 @@
1
+ /*
2
+ * xbintsc GUI engine — animatable property values (see animation_values.h).
3
+ */
4
+
5
+ #include "animation_values.h"
6
+
7
+ #include <cmath>
8
+ #include <cstring>
9
+
10
+ namespace xtgui {
11
+
12
+ namespace {
13
+
14
+ float mix(float from, float to, float progress) { return from + (to - from) * progress; }
15
+
16
+ Color mixColor(const Color &from, const Color &to, float progress) {
17
+ Color out;
18
+ out.r = mix(from.r, to.r, progress);
19
+ out.g = mix(from.g, to.g, progress);
20
+ out.b = mix(from.b, to.b, progress);
21
+ out.a = mix(from.a, to.a, progress);
22
+ out.valid = true;
23
+ return out;
24
+ }
25
+
26
+ bool colorEqual(const Color &a, const Color &b) {
27
+ return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
28
+ }
29
+
30
+ bool lengthEqual(const Length &a, const Length &b) {
31
+ if (a.unit != b.unit || a.value != b.value || a.op != b.op || a.args.size() != b.args.size())
32
+ return false;
33
+ for (size_t i = 0; i < a.args.size(); i++) {
34
+ if (!lengthEqual(a.args[i], b.args[i])) return false;
35
+ }
36
+ return true;
37
+ }
38
+
39
+ bool interpolateLength(const Length &from, const Length &to, float progress, Length *out) {
40
+ if (from.unit == to.unit) {
41
+ if (!from.args.empty() || !to.args.empty()) {
42
+ if (from.args.size() != to.args.size() || from.op != to.op) return false;
43
+ *out = from;
44
+ for (size_t i = 0; i < out->args.size(); i++) {
45
+ if (!interpolateLength(from.args[i], to.args[i], progress, &out->args[i])) return false;
46
+ }
47
+ return true;
48
+ }
49
+ *out = from;
50
+ out->value = mix(from.value, to.value, progress);
51
+ return true;
52
+ }
53
+ /* A unit change needs the used value, which only layout knows. CSS falls back
54
+ * to discrete interpolation for such pairs; so do we. */
55
+ if ((from.unit == Unit::Px || from.unit == Unit::Number) &&
56
+ (to.unit == Unit::Px || to.unit == Unit::Number)) {
57
+ *out = from;
58
+ out->unit = Unit::Px;
59
+ out->value = mix(from.value, to.value, progress);
60
+ return true;
61
+ }
62
+ return false;
63
+ }
64
+
65
+ bool shadowLengthsEqual(const Length &a, const Length &b) { return lengthEqual(a, b); }
66
+
67
+ bool interpolateTextShadows(const std::vector<TextShadow> &from, const std::vector<TextShadow> &to,
68
+ float progress, std::vector<TextShadow> *out) {
69
+ if (from.size() != to.size()) return false;
70
+ out->resize(from.size());
71
+ for (size_t i = 0; i < from.size(); i++) {
72
+ TextShadow shadow;
73
+ if (!interpolateLength(from[i].dx, to[i].dx, progress, &shadow.dx)) return false;
74
+ if (!interpolateLength(from[i].dy, to[i].dy, progress, &shadow.dy)) return false;
75
+ if (!interpolateLength(from[i].blur, to[i].blur, progress, &shadow.blur)) return false;
76
+ shadow.color = mixColor(from[i].color, to[i].color, progress);
77
+ shadow.has_color = true;
78
+ (*out)[i] = shadow;
79
+ }
80
+ return true;
81
+ }
82
+
83
+ bool interpolateBoxShadows(const std::vector<BoxShadow> &from, const std::vector<BoxShadow> &to,
84
+ float progress, std::vector<BoxShadow> *out) {
85
+ if (from.size() != to.size()) return false;
86
+ out->resize(from.size());
87
+ for (size_t i = 0; i < from.size(); i++) {
88
+ if (from[i].inset != to[i].inset) return false;
89
+ BoxShadow shadow;
90
+ if (!interpolateLength(from[i].dx, to[i].dx, progress, &shadow.dx)) return false;
91
+ if (!interpolateLength(from[i].dy, to[i].dy, progress, &shadow.dy)) return false;
92
+ if (!interpolateLength(from[i].blur, to[i].blur, progress, &shadow.blur)) return false;
93
+ if (!interpolateLength(from[i].spread, to[i].spread, progress, &shadow.spread)) return false;
94
+ shadow.color = mixColor(from[i].color, to[i].color, progress);
95
+ shadow.has_color = true;
96
+ shadow.inset = from[i].inset;
97
+ (*out)[i] = shadow;
98
+ }
99
+ return true;
100
+ }
101
+
102
+ TransformOp identityTransform(TransformKind kind) {
103
+ TransformOp op;
104
+ op.kind = kind;
105
+ switch (kind) {
106
+ case TransformKind::Translate:
107
+ op.x = Length{0.0f, Unit::Px};
108
+ op.y = Length{0.0f, Unit::Px};
109
+ break;
110
+ case TransformKind::TranslateX:
111
+ op.x = Length{0.0f, Unit::Px};
112
+ break;
113
+ case TransformKind::TranslateY:
114
+ op.y = Length{0.0f, Unit::Px};
115
+ break;
116
+ case TransformKind::Scale:
117
+ case TransformKind::ScaleX:
118
+ case TransformKind::ScaleY:
119
+ case TransformKind::Matrix:
120
+ break;
121
+ case TransformKind::Rotate:
122
+ op.angle = 0.0f;
123
+ break;
124
+ case TransformKind::Skew:
125
+ case TransformKind::SkewX:
126
+ case TransformKind::SkewY:
127
+ break;
128
+ }
129
+ return op;
130
+ }
131
+
132
+ /** CSS interpolation pads the shorter transform list with identity functions of
133
+ * the matching kinds (`none` animates to `translateX(0)` as if the missing
134
+ * list were the identity). */
135
+ bool normalizeTransforms(const std::vector<TransformOp> &from,
136
+ const std::vector<TransformOp> &to, std::vector<TransformOp> *out_from,
137
+ std::vector<TransformOp> *out_to) {
138
+ size_t common = from.size() < to.size() ? from.size() : to.size();
139
+ for (size_t i = 0; i < common; i++) {
140
+ if (from[i].kind != to[i].kind) return false;
141
+ }
142
+ *out_from = from;
143
+ *out_to = to;
144
+ for (size_t i = common; i < to.size(); i++) out_from->push_back(identityTransform(to[i].kind));
145
+ for (size_t i = common; i < from.size(); i++) out_to->push_back(identityTransform(from[i].kind));
146
+ return true;
147
+ }
148
+
149
+ bool interpolateFilters(const std::vector<FilterOp> &from, const std::vector<FilterOp> &to,
150
+ float progress, std::vector<FilterOp> *out) {
151
+ if (from.size() != to.size()) return false;
152
+ out->resize(from.size());
153
+ for (size_t i = 0; i < from.size(); i++) {
154
+ const FilterOp &left = from[i];
155
+ const FilterOp &right = to[i];
156
+ if (left.kind != right.kind) return false;
157
+ FilterOp op = left;
158
+ op.amount = mix(left.amount, right.amount, progress);
159
+ if (!interpolateLength(left.dx, right.dx, progress, &op.dx)) return false;
160
+ if (!interpolateLength(left.dy, right.dy, progress, &op.dy)) return false;
161
+ if (!interpolateLength(left.blur, right.blur, progress, &op.blur)) return false;
162
+ op.color = mixColor(left.color, right.color, progress);
163
+ op.has_color = left.has_color && right.has_color;
164
+ (*out)[i] = op;
165
+ }
166
+ return true;
167
+ }
168
+
169
+ bool filterEqual(const std::vector<FilterOp> &a, const std::vector<FilterOp> &b) {
170
+ if (a.size() != b.size()) return false;
171
+ for (size_t i = 0; i < a.size(); i++) {
172
+ const FilterOp &x = a[i];
173
+ const FilterOp &y = b[i];
174
+ if (x.kind != y.kind || x.amount != y.amount || !lengthEqual(x.dx, y.dx) ||
175
+ !lengthEqual(x.dy, y.dy) || !lengthEqual(x.blur, y.blur) ||
176
+ x.has_color != y.has_color || (x.has_color && !colorEqual(x.color, y.color)))
177
+ return false;
178
+ }
179
+ return true;
180
+ }
181
+
182
+ bool isColorProperty(const std::string &property) {
183
+ return property == "color" || property == "background-color" || property == "border-color";
184
+ }
185
+
186
+ bool isNumberProperty(const std::string &property) { return property == "opacity"; }
187
+
188
+ Length *lengthField(XtStyle *style, const std::string &property) {
189
+ if (property == "border-radius") return &style->border_radius;
190
+ if (property == "width") return &style->width;
191
+ if (property == "height") return &style->height;
192
+ if (property == "min-width") return &style->min_width;
193
+ if (property == "min-height") return &style->min_height;
194
+ if (property == "max-width") return &style->max_width;
195
+ if (property == "max-height") return &style->max_height;
196
+ if (property == "top") return &style->top;
197
+ if (property == "right") return &style->right;
198
+ if (property == "bottom") return &style->bottom;
199
+ if (property == "left") return &style->left;
200
+ if (property == "margin-top") return &style->margin.top;
201
+ if (property == "margin-right") return &style->margin.right;
202
+ if (property == "margin-bottom") return &style->margin.bottom;
203
+ if (property == "margin-left") return &style->margin.left;
204
+ if (property == "padding-top") return &style->padding.top;
205
+ if (property == "padding-right") return &style->padding.right;
206
+ if (property == "padding-bottom") return &style->padding.bottom;
207
+ if (property == "padding-left") return &style->padding.left;
208
+ if (property == "border-top-width") return &style->border_width.top;
209
+ if (property == "border-right-width") return &style->border_width.right;
210
+ if (property == "border-bottom-width") return &style->border_width.bottom;
211
+ if (property == "border-left-width") return &style->border_width.left;
212
+ if (property == "gap") return &style->gap;
213
+ if (property == "row-gap") return &style->row_gap;
214
+ if (property == "column-gap") return &style->column_gap;
215
+ if (property == "flex-basis") return &style->flex_basis;
216
+ return nullptr;
217
+ }
218
+
219
+ const Length *lengthField(const XtStyle &style, const std::string &property) {
220
+ return lengthField(const_cast<XtStyle *>(&style), property);
221
+ }
222
+
223
+ } // namespace
224
+
225
+ const std::vector<std::string> &animatableProperties() {
226
+ static const std::vector<std::string> properties = {
227
+ "background-color", "color", "border-color", "opacity",
228
+ "border-radius", "width", "height", "min-width",
229
+ "min-height", "max-width", "max-height", "top",
230
+ "right", "bottom", "left", "margin-top",
231
+ "margin-right", "margin-bottom", "margin-left", "padding-top",
232
+ "padding-right", "padding-bottom", "padding-left", "border-top-width",
233
+ "border-right-width", "border-bottom-width", "border-left-width", "gap",
234
+ "row-gap", "column-gap", "flex-basis", "transform",
235
+ "text-shadow", "box-shadow", "filter"};
236
+ return properties;
237
+ }
238
+
239
+ bool readAnimatable(const XtStyle &style, const std::string &property, AnimatableValue *out) {
240
+ *out = AnimatableValue();
241
+ if (isColorProperty(property)) {
242
+ out->kind = AnimatableKind::Color;
243
+ if (property == "color") out->color = style.color;
244
+ else if (property == "background-color")
245
+ out->color = style.has_background ? style.background : Color{0, 0, 0, 0, true};
246
+ else out->color = style.has_border_color ? style.border_color : Color{0, 0, 0, 0, true};
247
+ return true;
248
+ }
249
+ if (isNumberProperty(property)) {
250
+ out->kind = AnimatableKind::Number;
251
+ out->number = style.opacity;
252
+ return true;
253
+ }
254
+ if (const Length *length = lengthField(style, property)) {
255
+ out->kind = AnimatableKind::Length;
256
+ out->length = *length;
257
+ return true;
258
+ }
259
+ if (property == "font-size") {
260
+ out->kind = AnimatableKind::Length;
261
+ out->length = Length{style.font_size, Unit::Px};
262
+ return true;
263
+ }
264
+ if (property == "transform") {
265
+ out->kind = AnimatableKind::Transform;
266
+ out->transform = style.transform;
267
+ return true;
268
+ }
269
+ if (property == "text-shadow") {
270
+ out->kind = AnimatableKind::TextShadow;
271
+ out->text_shadow = style.text_shadow;
272
+ for (TextShadow &shadow : out->text_shadow) {
273
+ if (!shadow.has_color) {
274
+ shadow.color = style.color;
275
+ shadow.has_color = true;
276
+ }
277
+ }
278
+ return true;
279
+ }
280
+ if (property == "box-shadow") {
281
+ out->kind = AnimatableKind::BoxShadow;
282
+ out->box_shadow = style.box_shadow;
283
+ for (BoxShadow &shadow : out->box_shadow) {
284
+ if (!shadow.has_color) {
285
+ shadow.color = style.color;
286
+ shadow.has_color = true;
287
+ }
288
+ }
289
+ return true;
290
+ }
291
+ if (property == "filter") {
292
+ out->kind = AnimatableKind::Filter;
293
+ out->filter = style.filters;
294
+ for (FilterOp &op : out->filter) {
295
+ if (op.kind == FilterKind::DropShadow && !op.has_color) {
296
+ op.color = style.color;
297
+ op.has_color = true;
298
+ }
299
+ }
300
+ return true;
301
+ }
302
+ return false;
303
+ }
304
+
305
+ void writeAnimatable(XtStyle *style, const std::string &property, const AnimatableValue &value) {
306
+ switch (value.kind) {
307
+ case AnimatableKind::Color:
308
+ if (property == "color") {
309
+ style->color = value.color;
310
+ } else if (property == "background-color") {
311
+ style->background = value.color;
312
+ style->has_background = value.color.a > 0.0f;
313
+ } else if (property == "border-color") {
314
+ style->border_color = value.color;
315
+ style->has_border_color = value.color.a > 0.0f;
316
+ }
317
+ break;
318
+ case AnimatableKind::Number:
319
+ if (property == "opacity") style->opacity = value.number;
320
+ break;
321
+ case AnimatableKind::Length:
322
+ if (property == "font-size") {
323
+ style->font_size = value.length.unit == Unit::Px ? value.length.value : style->font_size;
324
+ } else if (Length *field = lengthField(style, property)) {
325
+ *field = value.length;
326
+ }
327
+ break;
328
+ case AnimatableKind::Transform:
329
+ if (property == "transform") style->transform = value.transform;
330
+ break;
331
+ case AnimatableKind::TextShadow:
332
+ if (property == "text-shadow") style->text_shadow = value.text_shadow;
333
+ break;
334
+ case AnimatableKind::BoxShadow:
335
+ if (property == "box-shadow") style->box_shadow = value.box_shadow;
336
+ break;
337
+ case AnimatableKind::Filter:
338
+ if (property == "filter") style->filters = value.filter;
339
+ break;
340
+ case AnimatableKind::None:
341
+ break;
342
+ }
343
+ }
344
+
345
+ bool interpolateAnimatable(const AnimatableValue &from, const AnimatableValue &to, float progress,
346
+ AnimatableValue *out) {
347
+ if (from.kind != to.kind) return false;
348
+ *out = from;
349
+ switch (from.kind) {
350
+ case AnimatableKind::None:
351
+ return false;
352
+ case AnimatableKind::Color:
353
+ out->color = mixColor(from.color, to.color, progress);
354
+ return true;
355
+ case AnimatableKind::Number:
356
+ out->number = mix(from.number, to.number, progress);
357
+ return true;
358
+ case AnimatableKind::Length:
359
+ return interpolateLength(from.length, to.length, progress, &out->length);
360
+ case AnimatableKind::Transform: {
361
+ std::vector<TransformOp> normalized_from;
362
+ std::vector<TransformOp> normalized_to;
363
+ if (!normalizeTransforms(from.transform, to.transform, &normalized_from, &normalized_to))
364
+ return false;
365
+ return xt_transform_interpolate(normalized_from, normalized_to, progress, &out->transform);
366
+ }
367
+ case AnimatableKind::TextShadow:
368
+ return interpolateTextShadows(from.text_shadow, to.text_shadow, progress,
369
+ &out->text_shadow);
370
+ case AnimatableKind::BoxShadow:
371
+ return interpolateBoxShadows(from.box_shadow, to.box_shadow, progress, &out->box_shadow);
372
+ case AnimatableKind::Filter:
373
+ return interpolateFilters(from.filter, to.filter, progress, &out->filter);
374
+ }
375
+ return false;
376
+ }
377
+
378
+ bool animatableEqual(const AnimatableValue &a, const AnimatableValue &b) {
379
+ if (a.kind != b.kind) return false;
380
+ switch (a.kind) {
381
+ case AnimatableKind::None:
382
+ return true;
383
+ case AnimatableKind::Color:
384
+ return colorEqual(a.color, b.color);
385
+ case AnimatableKind::Number:
386
+ return a.number == b.number;
387
+ case AnimatableKind::Length:
388
+ return lengthEqual(a.length, b.length);
389
+ case AnimatableKind::Transform: {
390
+ std::vector<TransformOp> normalized_a;
391
+ std::vector<TransformOp> normalized_b;
392
+ if (!normalizeTransforms(a.transform, b.transform, &normalized_a, &normalized_b))
393
+ return false;
394
+ return xt_transform_equal(normalized_a, normalized_b);
395
+ }
396
+ case AnimatableKind::TextShadow:
397
+ if (a.text_shadow.size() != b.text_shadow.size()) return false;
398
+ for (size_t i = 0; i < a.text_shadow.size(); i++) {
399
+ const TextShadow &x = a.text_shadow[i];
400
+ const TextShadow &y = b.text_shadow[i];
401
+ if (!shadowLengthsEqual(x.dx, y.dx) || !shadowLengthsEqual(x.dy, y.dy) ||
402
+ !shadowLengthsEqual(x.blur, y.blur) || !colorEqual(x.color, y.color) ||
403
+ x.has_color != y.has_color)
404
+ return false;
405
+ }
406
+ return true;
407
+ case AnimatableKind::Filter:
408
+ return filterEqual(a.filter, b.filter);
409
+ case AnimatableKind::BoxShadow:
410
+ if (a.box_shadow.size() != b.box_shadow.size()) return false;
411
+ for (size_t i = 0; i < a.box_shadow.size(); i++) {
412
+ const BoxShadow &x = a.box_shadow[i];
413
+ const BoxShadow &y = b.box_shadow[i];
414
+ if (x.inset != y.inset || !shadowLengthsEqual(x.dx, y.dx) ||
415
+ !shadowLengthsEqual(x.dy, y.dy) || !shadowLengthsEqual(x.blur, y.blur) ||
416
+ !shadowLengthsEqual(x.spread, y.spread) || !colorEqual(x.color, y.color) ||
417
+ x.has_color != y.has_color)
418
+ return false;
419
+ }
420
+ return true;
421
+ }
422
+ return false;
423
+ }
424
+
425
+ } // namespace xtgui
@@ -0,0 +1,64 @@
1
+ /*
2
+ * xbintsc GUI engine — interpolation of animatable CSS properties.
3
+ *
4
+ * CSS transitions and `@keyframes` both need a property value detached from
5
+ * `XtStyle`, interpolated at a progress, and written back. Keeping that switch
6
+ * in one place means a newly supported transition property automatically works
7
+ * in keyframes too (and vice versa).
8
+ */
9
+ #ifndef XT_GUI_ANIMATION_VALUES_H
10
+ #define XT_GUI_ANIMATION_VALUES_H
11
+
12
+ #include <string>
13
+ #include <vector>
14
+
15
+ #include "style.h"
16
+
17
+ namespace xtgui {
18
+
19
+ enum class AnimatableKind {
20
+ None,
21
+ Color,
22
+ Number,
23
+ Length,
24
+ Transform,
25
+ TextShadow,
26
+ BoxShadow,
27
+ Filter
28
+ };
29
+
30
+ struct AnimatableValue {
31
+ AnimatableKind kind = AnimatableKind::None;
32
+ Color color;
33
+ float number = 0.0f;
34
+ Length length;
35
+ std::vector<TransformOp> transform;
36
+ std::vector<TextShadow> text_shadow;
37
+ std::vector<BoxShadow> box_shadow;
38
+ std::vector<FilterOp> filter;
39
+ };
40
+
41
+ /** Every property the animation machinery can read, interpolate and write. The
42
+ * list drives `transition: all` as well. */
43
+ const std::vector<std::string> &animatableProperties();
44
+
45
+ /** Read `property` from `style`; false when it is not animatable. Colours that
46
+ * default to `currentColor` are resolved against `style.color`. */
47
+ bool readAnimatable(const XtStyle &style, const std::string &property, AnimatableValue *out);
48
+
49
+ /** Write an interpolated value back onto `style`. */
50
+ void writeAnimatable(XtStyle *style, const std::string &property, const AnimatableValue &value);
51
+
52
+ /** Interpolate two values at `progress` (0..1). Returns false when the values
53
+ * cannot be mixed (different kinds, list shapes or units); callers then fall
54
+ * back to a discrete switch at the halfway point, as CSS does. */
55
+ bool interpolateAnimatable(const AnimatableValue &from, const AnimatableValue &to, float progress,
56
+ AnimatableValue *out);
57
+
58
+ /** True when two values would paint identically (so no transition should
59
+ * start). */
60
+ bool animatableEqual(const AnimatableValue &a, const AnimatableValue &b);
61
+
62
+ } // namespace xtgui
63
+
64
+ #endif /* XT_GUI_ANIMATION_VALUES_H */
@@ -321,21 +321,28 @@ xt_value type_proto(const std::string &name) {
321
321
  return domapi::event_proto();
322
322
  }
323
323
  if (name == "AudioContext" || name == "webkitAudioContext") return xt_gui_audio_proto();
324
- if (name == "WebGLRenderingContext") return domapi::webgl_proto();
325
- if (name == "WebGL2RenderingContext") return domapi::webgl2_proto();
326
- if (name == "WebGLBuffer") return domapi::webgl_object_proto("buffer");
327
- if (name == "WebGLTexture") return domapi::webgl_object_proto("texture");
328
- if (name == "WebGLShader") return domapi::webgl_object_proto("shader");
329
- if (name == "WebGLProgram") return domapi::webgl_object_proto("program");
330
- if (name == "WebGLFramebuffer") return domapi::webgl_object_proto("framebuffer");
331
- if (name == "WebGLRenderbuffer") return domapi::webgl_object_proto("renderbuffer");
332
- if (name == "WebGLUniformLocation") return domapi::webgl_object_proto("uniform");
333
- if (name == "WebGLActiveInfo") return domapi::webgl_object_proto("info");
334
- if (name == "WebGLVertexArrayObject") return domapi::webgl_object_proto("vertexarray");
335
- if (name == "WebGLSampler") return domapi::webgl_object_proto("sampler");
336
- if (name == "WebGLQuery") return domapi::webgl_object_proto("query");
337
- if (name == "WebGLSync") return domapi::webgl_object_proto("sync");
338
- if (name == "WebGLTransformFeedback") return domapi::webgl_object_proto("transformfeedback");
324
+ if (name == "WebGLRenderingContext" || name == "WebGL2RenderingContext") {
325
+ const XtWebglApi *webgl = xt_webgl_api();
326
+ return webgl != nullptr ? webgl->proto(name == "WebGL2RenderingContext") : XT_UNDEFINED;
327
+ }
328
+ if (const XtWebglApi *webgl = xt_webgl_api(); webgl != nullptr) {
329
+ if (name == "WebGLBuffer") return webgl->object_proto("buffer");
330
+ if (name == "WebGLTexture") return webgl->object_proto("texture");
331
+ if (name == "WebGLShader") return webgl->object_proto("shader");
332
+ if (name == "WebGLProgram") return webgl->object_proto("program");
333
+ if (name == "WebGLFramebuffer") return webgl->object_proto("framebuffer");
334
+ if (name == "WebGLRenderbuffer") return webgl->object_proto("renderbuffer");
335
+ if (name == "WebGLUniformLocation") return webgl->object_proto("uniform");
336
+ if (name == "WebGLActiveInfo") return webgl->object_proto("info");
337
+ if (name == "WebGLVertexArrayObject") return webgl->object_proto("vertexarray");
338
+ if (name == "WebGLSampler") return webgl->object_proto("sampler");
339
+ if (name == "WebGLQuery") return webgl->object_proto("query");
340
+ if (name == "WebGLSync") return webgl->object_proto("sync");
341
+ if (name == "WebGLTransformFeedback") return webgl->object_proto("transformfeedback");
342
+ }
343
+ /* A WebGL host type with no linked WebGL feature is undefined, so feature
344
+ * probes take their fallback branch. */
345
+ if (name.rfind("WebGL", 0) == 0) return XT_UNDEFINED;
339
346
  if (name == "Path2D") return domapi::path2d_proto();
340
347
  if (name == "OffscreenCanvas") return domapi::offscreen_canvas_proto();
341
348
  if (name == "ImageBitmap") {
@@ -28,6 +28,8 @@ xt_value g_tick = XT_UNDEFINED;
28
28
  int g_tick_pending = 0;
29
29
  double g_tick_started_ms = 0;
30
30
  double g_tick_deadline_ms = 0;
31
+ /** When the frame being served started, for the next tick's delay. */
32
+ double g_tick_frame_started_ms = 0;
31
33
 
32
34
  xt_value browser_tick(xt_value self, xt_value env, int32_t argc, xt_value *argv);
33
35
 
@@ -39,7 +41,16 @@ void arm_tick(void) {
39
41
  g_tick = xt_closure_new((void *)browser_tick, 0, NULL);
40
42
  xt_gc_add_root(&g_tick);
41
43
  }
42
- xt_value args[2] = {g_tick, xt_number(16)};
44
+ /* Keep the frame *period* at 16 ms, measured from the start of the frame like
45
+ * a browser's vsync: scheduling 16 ms from the end made every frame cost its
46
+ * own duration on top of the delay, so a 10 ms frame ran at 38 fps and a
47
+ * 90 ms frame at 9 — with no way to ever catch up to the display. */
48
+ double elapsed = g_tick_frame_started_ms > 0.0
49
+ ? (double)SDL_GetTicks() - g_tick_frame_started_ms
50
+ : 16.0;
51
+ double delay = 16.0 - elapsed;
52
+ if (delay < 0.0) delay = 0.0;
53
+ xt_value args[2] = {g_tick, xt_number(delay)};
43
54
  g_tick_pending = 1;
44
55
  xt_set_timeout(2, args);
45
56
  }
@@ -50,6 +61,7 @@ xt_value browser_tick(xt_value self, xt_value env, int32_t argc, xt_value *argv)
50
61
  (void)argc;
51
62
  (void)argv;
52
63
  g_tick_pending = 0;
64
+ g_tick_frame_started_ms = (double)SDL_GetTicks();
53
65
  if (xt_gui_run_active()) return XT_UNDEFINED;
54
66
  if (g_tick_deadline_ms > 0 && (double)SDL_GetTicks() >= g_tick_deadline_ms) {
55
67
  /* Shared with `run()`: stop animating and let the program exit instead of
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * The rasteriser is deliberately small and self-written (no Skia/Cairo): it
11
11
  * fills rectangles, polygons and flattened arcs with sub-scanline coverage
12
- * anti-aliasing, and blits FreeType glyph bitmaps for `fillText`. The pieces
12
+ * anti-aliasing, and blits stb_truetype glyph bitmaps for `fillText`. The pieces
13
13
  * live in `canvas_raster.cpp`; this header only exposes the object model.
14
14
  *
15
15
  * Canvas pixels are *not* touched by the garbage collector: a canvas is owned
@@ -5,7 +5,7 @@
5
5
  * nonzero winding rule and vertical sub-scanline sampling (4 samples per pixel
6
6
  * row) plus analytic horizontal coverage, which gives cheap anti-aliasing
7
7
  * without a full analytic rasteriser. Rectangles take a faster axis-aligned
8
- * path, and glyphs are composited from their FreeType coverage masks.
8
+ * path, and glyphs are composited from their stb_truetype coverage masks.
9
9
  */
10
10
 
11
11
  #include "canvas_internal.h"
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * xbintsc GUI engine — Canvas2D text drawing.
3
3
  *
4
- * `fillText`/`strokeText` shape a run with HarfBuzz and blit FreeType glyph
4
+ * `fillText`/`strokeText` shape a run with the shaper and blit stb_truetype glyph
5
5
  * coverage through the rasteriser. The state and path live in `canvas.h`; this
6
6
  * unit only owns the text entry points and the optional `maxWidth` condense.
7
7
  */