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.
- package/AGENTS.md +1 -1
- package/README.md +5 -4
- package/README.zh-CN.md +4 -3
- package/dist/src/cli/extensions.d.ts +24 -0
- package/dist/src/cli/extensions.js +62 -0
- package/dist/src/cli/extensions.js.map +1 -0
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/cli/help.js +3 -1
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/hints.js +8 -3
- package/dist/src/cli/hints.js.map +1 -1
- package/dist/src/cli/main.js +21 -28
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/diagnostics/diagnostic.d.ts +3 -1
- package/dist/src/diagnostics/diagnostic.js +2 -0
- package/dist/src/diagnostics/diagnostic.js.map +1 -1
- package/dist/src/driver/build-key.d.ts +2 -0
- package/dist/src/driver/build-key.js +1 -0
- package/dist/src/driver/build-key.js.map +1 -1
- package/dist/src/driver/compiler.js +61 -8
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/config.d.ts +4 -0
- package/dist/src/driver/config.js +2 -0
- package/dist/src/driver/config.js.map +1 -1
- package/dist/src/driver/link-features.d.ts +37 -0
- package/dist/src/driver/link-features.js +123 -0
- package/dist/src/driver/link-features.js.map +1 -0
- package/dist/src/driver/options.d.ts +10 -0
- package/dist/src/driver/runtime-objects.d.ts +5 -0
- package/dist/src/driver/runtime-objects.js +20 -1
- package/dist/src/driver/runtime-objects.js.map +1 -1
- package/dist/src/driver/usage.d.ts +23 -0
- package/dist/src/driver/usage.js +353 -0
- package/dist/src/driver/usage.js.map +1 -0
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/catalog.js +1 -2
- package/dist/src/extensions/catalog.js.map +1 -1
- package/dist/src/extensions/gui/features.d.ts +34 -0
- package/dist/src/extensions/gui/features.js +165 -0
- package/dist/src/extensions/gui/features.js.map +1 -0
- package/dist/src/extensions/gui/index.js +10 -4
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/link-feature.d.ts +70 -0
- package/dist/src/extensions/link-feature.js +18 -0
- package/dist/src/extensions/link-feature.js.map +1 -0
- package/dist/src/extensions/registry.d.ts +24 -4
- package/dist/src/extensions/registry.js +26 -3
- package/dist/src/extensions/registry.js.map +1 -1
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.js +0 -1
- package/dist/src/index.js.map +1 -1
- package/dist/tests/cli/hints.test.js +5 -7
- package/dist/tests/cli/hints.test.js.map +1 -1
- package/dist/tests/driver/link-features.test.js +105 -0
- package/dist/tests/driver/link-features.test.js.map +1 -0
- package/dist/tests/driver/usage.test.d.ts +1 -0
- package/dist/tests/driver/usage.test.js +88 -0
- package/dist/tests/driver/usage.test.js.map +1 -0
- package/dist/tests/e2e/gui-audio.test.js +4 -4
- package/dist/tests/e2e/gui-audio.test.js.map +1 -1
- package/dist/tests/e2e/gui-css-coverage.test.d.ts +9 -0
- package/dist/tests/e2e/gui-css-coverage.test.js +333 -0
- package/dist/tests/e2e/gui-css-coverage.test.js.map +1 -0
- package/dist/tests/e2e/gui-css-features.test.d.ts +6 -0
- package/dist/tests/e2e/gui-css-features.test.js +270 -0
- package/dist/tests/e2e/gui-css-features.test.js.map +1 -0
- package/dist/tests/e2e/gui-effects.test.js +92 -11
- package/dist/tests/e2e/gui-effects.test.js.map +1 -1
- package/dist/tests/e2e/gui-feature-linking.test.d.ts +9 -0
- package/dist/tests/e2e/gui-feature-linking.test.js +68 -0
- package/dist/tests/e2e/gui-feature-linking.test.js.map +1 -0
- package/dist/tests/e2e/gui-font-face.test.d.ts +10 -0
- package/dist/tests/e2e/gui-font-face.test.js +142 -0
- package/dist/tests/e2e/gui-font-face.test.js.map +1 -0
- package/dist/tests/e2e/gui-fonts.test.d.ts +10 -0
- package/dist/tests/e2e/gui-fonts.test.js +150 -0
- package/dist/tests/e2e/gui-fonts.test.js.map +1 -0
- package/dist/tests/e2e/gui-helpers.d.ts +10 -1
- package/dist/tests/e2e/gui-helpers.js +6 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -1
- package/dist/tests/e2e/gui-layout.test.js +7 -3
- package/dist/tests/e2e/gui-layout.test.js.map +1 -1
- package/dist/tests/e2e/gui-media-disabled.test.d.ts +5 -4
- package/dist/tests/e2e/gui-media-disabled.test.js +7 -6
- package/dist/tests/e2e/gui-media-disabled.test.js.map +1 -1
- package/dist/tests/e2e/gui-media.test.d.ts +5 -4
- package/dist/tests/e2e/gui-media.test.js +9 -9
- package/dist/tests/e2e/gui-media.test.js.map +1 -1
- package/dist/tests/e2e/gui-paint.test.js +5 -2
- package/dist/tests/e2e/gui-paint.test.js.map +1 -1
- package/dist/tests/e2e/gui-shaping.test.d.ts +5 -0
- package/dist/tests/e2e/gui-shaping.test.js +47 -0
- package/dist/tests/e2e/gui-shaping.test.js.map +1 -0
- package/dist/tests/e2e/gui-track-sizing.test.d.ts +6 -0
- package/dist/tests/e2e/gui-track-sizing.test.js +173 -0
- package/dist/tests/e2e/gui-track-sizing.test.js.map +1 -0
- package/dist/tests/e2e/gui-transform.test.js +3 -2
- package/dist/tests/e2e/gui-transform.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl-pipeline-cache.test.d.ts +18 -0
- package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js +120 -0
- package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js.map +1 -0
- package/dist/tests/extensions/gui-features.test.d.ts +1 -0
- package/dist/tests/extensions/gui-features.test.js +175 -0
- package/dist/tests/extensions/gui-features.test.js.map +1 -0
- package/doc/ai/cli.md +5 -1
- package/doc/ai/extensions.md +56 -26
- package/doc/ai/troubleshooting.md +10 -7
- package/doc/ai/zh-CN/cli.md +5 -1
- package/doc/ai/zh-CN/extensions.md +48 -22
- package/doc/ai/zh-CN/troubleshooting.md +6 -5
- package/doc/gui/animation.md +39 -9
- package/doc/gui/api.md +2 -1
- package/doc/gui/build.md +137 -19
- package/doc/gui/canvas.md +1 -1
- package/doc/gui/dom.md +20 -4
- package/doc/gui/known-issues.md +63 -26
- package/doc/gui/layout.md +31 -8
- package/doc/gui/media.md +13 -7
- package/doc/gui/rendering.md +114 -39
- package/doc/gui/runtime.md +5 -0
- package/doc/gui/webgl.md +24 -6
- package/doc/gui.md +110 -47
- package/doc/implemented/cli.md +2 -0
- package/doc/implemented/extensions.md +9 -3
- package/doc/npm-package.md +4 -4
- package/doc/xbintsc.config.schema.json +10 -0
- package/doc/zh-CN/gui/animation.md +33 -10
- package/doc/zh-CN/gui/api.md +2 -1
- package/doc/zh-CN/gui/build.md +110 -16
- package/doc/zh-CN/gui/canvas.md +1 -1
- package/doc/zh-CN/gui/dom.md +16 -3
- package/doc/zh-CN/gui/known-issues.md +45 -15
- package/doc/zh-CN/gui/layout.md +23 -6
- package/doc/zh-CN/gui/media.md +11 -6
- package/doc/zh-CN/gui/rendering.md +84 -26
- package/doc/zh-CN/gui/runtime.md +3 -0
- package/doc/zh-CN/gui/webgl.md +3 -3
- package/doc/zh-CN/gui.md +92 -41
- package/doc/zh-CN/implemented/cli.md +2 -0
- package/doc/zh-CN/implemented/extensions.md +9 -3
- package/doc/zh-CN/npm-package.md +4 -3
- package/package.json +7 -7
- package/runtime/ext_gui/animation_values.cpp +425 -0
- package/runtime/ext_gui/animation_values.h +64 -0
- package/runtime/ext_gui/browser.cpp +22 -15
- package/runtime/ext_gui/browser_loop.cpp +13 -1
- package/runtime/ext_gui/canvas.h +1 -1
- package/runtime/ext_gui/canvas_raster.cpp +1 -1
- package/runtime/ext_gui/canvas_text.cpp +1 -1
- package/runtime/ext_gui/css.cpp +172 -21
- package/runtime/ext_gui/css.h +75 -4
- package/runtime/ext_gui/css_match.cpp +4 -0
- package/runtime/ext_gui/css_sheet.cpp +218 -2
- package/runtime/ext_gui/document.cpp +42 -192
- package/runtime/ext_gui/document.h +25 -12
- package/runtime/ext_gui/document_animation.cpp +428 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +15 -8
- package/runtime/ext_gui/dom_api_canvas_extra.cpp +44 -20
- package/runtime/ext_gui/dom_api_canvas_image.cpp +3 -1
- package/runtime/ext_gui/dom_api_node_read.cpp +10 -0
- package/runtime/ext_gui/dom_api_offscreen.cpp +3 -1
- package/runtime/ext_gui/dom_api_style.cpp +7 -0
- package/runtime/ext_gui/dom_api_webgl_internal.h +4 -0
- package/runtime/ext_gui/dom_api_webgl_source.cpp +5 -2
- package/runtime/ext_gui/effect_values.cpp +245 -21
- package/runtime/ext_gui/gpu_bridge.c +28 -0
- package/runtime/ext_gui/gpu_feature.cpp +30 -0
- package/runtime/ext_gui/gpu_shader.cpp +22 -0
- package/runtime/ext_gui/gpu_shader_api.h +50 -0
- package/runtime/ext_gui/gradient_values.cpp +24 -0
- package/runtime/ext_gui/grid_values.cpp +23 -0
- package/runtime/ext_gui/gui.cpp +6 -37
- package/runtime/ext_gui/gui_canvas.cpp +14 -6
- package/runtime/ext_gui/gui_cursor.cpp +50 -0
- package/runtime/ext_gui/gui_engine.h +1 -18
- package/runtime/ext_gui/gui_events.cpp +19 -6
- package/runtime/ext_gui/gui_internal.h +3 -0
- package/runtime/ext_gui/gui_webgl.cpp +25 -10
- package/runtime/ext_gui/image_api.cpp +18 -0
- package/runtime/ext_gui/image_api.h +43 -0
- package/runtime/ext_gui/image_bitmap_browser.cpp +20 -11
- package/runtime/ext_gui/image_bridge.c +22 -0
- package/runtime/ext_gui/image_feature.cpp +107 -0
- package/runtime/ext_gui/inline_svg.cpp +95 -8
- package/runtime/ext_gui/inline_svg.h +5 -1
- package/runtime/ext_gui/layout.cpp +15 -9
- package/runtime/ext_gui/layout.h +8 -2
- package/runtime/ext_gui/layout_flex.cpp +134 -29
- package/runtime/ext_gui/layout_flow.cpp +58 -7
- package/runtime/ext_gui/layout_flow_internal.h +39 -4
- package/runtime/ext_gui/layout_grid.cpp +31 -0
- package/runtime/ext_gui/layout_inline.cpp +26 -2
- package/runtime/ext_gui/layout_vertical.cpp +196 -0
- package/runtime/ext_gui/media.cpp +4 -2
- package/runtime/ext_gui/media.h +3 -3
- package/runtime/ext_gui/media_api.h +7 -6
- package/runtime/ext_gui/paint.cpp +67 -9
- package/runtime/ext_gui/paint.h +61 -8
- package/runtime/ext_gui/paint_backdrop.cpp +211 -0
- package/runtime/ext_gui/paint_background.cpp +175 -97
- package/runtime/ext_gui/paint_effects.cpp +12 -6
- package/runtime/ext_gui/paint_internal.h +27 -0
- package/runtime/ext_gui/paint_outline.cpp +44 -0
- package/runtime/ext_gui/paint_shadow.cpp +368 -0
- package/runtime/ext_gui/renderer.cpp +15 -7
- package/runtime/ext_gui/renderer.h +11 -2
- package/runtime/ext_gui/renderer_backdrop.cpp +374 -0
- package/runtime/ext_gui/renderer_bitmap.cpp +82 -0
- package/runtime/ext_gui/renderer_gradient.cpp +51 -17
- package/runtime/ext_gui/renderer_image.cpp +19 -7
- package/runtime/ext_gui/renderer_internal.h +21 -0
- package/runtime/ext_gui/renderer_layer.cpp +37 -6
- package/runtime/ext_gui/renderer_text.cpp +2 -1
- package/runtime/ext_gui/stb_truetype.h +5077 -0
- package/runtime/ext_gui/style.cpp +62 -8
- package/runtime/ext_gui/style.h +111 -4
- package/runtime/ext_gui/style_cascade.cpp +83 -0
- package/runtime/ext_gui/style_internal.h +13 -1
- package/runtime/ext_gui/style_length.cpp +92 -0
- package/runtime/ext_gui/style_print.cpp +83 -2
- package/runtime/ext_gui/style_svg.cpp +40 -0
- package/runtime/ext_gui/style_text.cpp +302 -0
- package/runtime/ext_gui/style_ui.cpp +150 -0
- package/runtime/ext_gui/style_values.cpp +76 -62
- package/runtime/ext_gui/style_values_internal.h +23 -0
- package/runtime/ext_gui/text.cpp +27 -499
- package/runtime/ext_gui/text.h +18 -9
- package/runtime/ext_gui/text_api.h +45 -0
- package/runtime/ext_gui/text_bridge.c +23 -0
- package/runtime/ext_gui/text_cache.cpp +12 -3
- package/runtime/ext_gui/text_font_db.cpp +427 -0
- package/runtime/ext_gui/text_font_paths.cpp +250 -0
- package/runtime/ext_gui/text_font_paths.h +39 -0
- package/runtime/ext_gui/text_font_sfnt.cpp +396 -0
- package/runtime/ext_gui/text_font_sfnt.h +44 -0
- package/runtime/ext_gui/text_fonts.cpp +348 -0
- package/runtime/ext_gui/text_fonts.h +78 -0
- package/runtime/ext_gui/text_fonts_internal.h +77 -0
- package/runtime/ext_gui/text_glyph_cache.cpp +2 -2
- package/runtime/ext_gui/text_hb.cpp +438 -0
- package/runtime/ext_gui/text_stb.cpp +369 -0
- package/runtime/ext_gui/transform.cpp +107 -9
- package/runtime/ext_gui/transform.h +5 -0
- package/runtime/ext_gui/webgl_api.cpp +19 -0
- package/runtime/ext_gui/webgl_api.h +53 -0
- package/runtime/ext_gui/webgl_bridge.c +25 -0
- package/runtime/ext_gui/webgl_draw_setup.cpp +42 -1
- package/runtime/ext_gui/webgl_feature.cpp +82 -0
- package/runtime/ext_gui/webgl_feature.h +28 -0
- package/runtime/ext_gui/webgl_msl.cpp +41 -0
- package/runtime/ext_gui/webgl_msl.h +6 -0
- package/runtime/ext_gui/webgl_shader.cpp +40 -32
- package/runtime/ext_gui/window.cpp +7 -7
- package/runtime/ext_media/decode_audio.cpp +1 -1
- package/runtime/ext_media/media.cpp +1 -1
- package/runtime/ext_media/media_bridge.c +7 -7
- package/scripts/build-gui.ts +97 -98
- package/scripts/check-file-length.ts +1 -0
- package/scripts/gui-build-options.ts +122 -0
- package/src/cli/extensions.ts +72 -0
- package/src/cli/help.ts +3 -1
- package/src/cli/hints.ts +9 -3
- package/src/cli/main.ts +21 -27
- package/src/diagnostics/diagnostic.ts +2 -0
- package/src/driver/build-key.ts +3 -0
- package/src/driver/compiler.ts +63 -8
- package/src/driver/config.ts +6 -0
- package/src/driver/link-features.ts +163 -0
- package/src/driver/options.ts +10 -0
- package/src/driver/runtime-objects.ts +26 -1
- package/src/driver/usage.ts +376 -0
- package/src/driver/version.ts +1 -1
- package/src/extensions/catalog.ts +1 -2
- package/src/extensions/gui/features.ts +166 -0
- package/src/extensions/gui/index.ts +10 -4
- package/src/extensions/link-feature.ts +72 -0
- package/src/extensions/registry.ts +45 -4
- package/src/index.ts +3 -2
- package/dist/src/extensions/media/index.d.ts +0 -28
- package/dist/src/extensions/media/index.js +0 -59
- package/dist/src/extensions/media/index.js.map +0 -1
- package/dist/tests/extensions/media.test.js +0 -63
- package/dist/tests/extensions/media.test.js.map +0 -1
- package/src/extensions/media/index.ts +0 -65
- /package/dist/tests/{extensions/media.test.d.ts → driver/link-features.test.d.ts} +0 -0
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
#define XT_GUI_INLINE_SVG_H
|
|
20
20
|
|
|
21
21
|
#include <string>
|
|
22
|
+
#include <unordered_map>
|
|
22
23
|
|
|
23
24
|
#include "dom.h"
|
|
25
|
+
#include "style.h"
|
|
24
26
|
#include "svg.h"
|
|
25
27
|
|
|
26
28
|
namespace xtgui {
|
|
@@ -34,7 +36,9 @@ bool xt_inline_svg_is_root(const Node *node);
|
|
|
34
36
|
* expects. Text outside `<style>` is dropped (NanoSVG renders shapes, not
|
|
35
37
|
* `<text>`), and HTML-only attributes are skipped. Returns an empty string when
|
|
36
38
|
* `node` is not an `<svg>` element. */
|
|
37
|
-
std::string xt_inline_svg_serialize(
|
|
39
|
+
std::string xt_inline_svg_serialize(
|
|
40
|
+
const Node *node,
|
|
41
|
+
const std::unordered_map<const Node *, XtStyle> *styles = nullptr);
|
|
38
42
|
|
|
39
43
|
} // namespace xtgui
|
|
40
44
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - Block flow: consecutive inline-level children form an anonymous inline
|
|
7
7
|
* formatting context; block-level children are stacked vertically.
|
|
8
8
|
* - Inline flow: greedy, word-based line breaking with `text-align`; text is
|
|
9
|
-
* measured with the
|
|
9
|
+
* measured with the stb_truetype stack in `text.h`.
|
|
10
10
|
* - Flexbox: single-line row/column with grow/shrink, gap, justify-content
|
|
11
11
|
* and align-items.
|
|
12
12
|
* Margins do not collapse yet, and only static positioning is honoured.
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
#include "layout_internal.h"
|
|
16
16
|
|
|
17
|
+
#include "image_api.h"
|
|
18
|
+
|
|
17
19
|
#include <algorithm>
|
|
18
20
|
#include <cstdio>
|
|
19
21
|
#include <cstring>
|
|
@@ -38,7 +40,8 @@ std::unique_ptr<LayoutBox> buildBoxTree(const Node *node,
|
|
|
38
40
|
auto box = std::make_unique<LayoutBox>();
|
|
39
41
|
box->node = node;
|
|
40
42
|
box->is_text = true;
|
|
41
|
-
box->text = node->text
|
|
43
|
+
box->text = inherited != nullptr ? xt_apply_text_transform(node->text, inherited->text_transform)
|
|
44
|
+
: node->text;
|
|
42
45
|
box->style = inherited;
|
|
43
46
|
box->display = Display::Inline;
|
|
44
47
|
return box;
|
|
@@ -51,6 +54,7 @@ std::unique_ptr<LayoutBox> buildBoxTree(const Node *node,
|
|
|
51
54
|
box->node = node;
|
|
52
55
|
box->style = style;
|
|
53
56
|
box->display = style != nullptr ? style->display : Display::Block;
|
|
57
|
+
const XtImageApi *images = xt_image_api();
|
|
54
58
|
if (node->isElement() && node->isTag("img")) {
|
|
55
59
|
box->is_image = true;
|
|
56
60
|
const std::string *src = node->attr("src");
|
|
@@ -58,7 +62,7 @@ std::unique_ptr<LayoutBox> buildBoxTree(const Node *node,
|
|
|
58
62
|
box->image_src = *src;
|
|
59
63
|
int width = 0;
|
|
60
64
|
int height = 0;
|
|
61
|
-
if (
|
|
65
|
+
if (images != nullptr && images->size(*src, &width, &height)) {
|
|
62
66
|
box->intrinsic_width = (float)width;
|
|
63
67
|
box->intrinsic_height = (float)height;
|
|
64
68
|
}
|
|
@@ -113,12 +117,14 @@ std::unique_ptr<LayoutBox> buildBoxTree(const Node *node,
|
|
|
113
117
|
* let paint/rasterisation handle the children — the normal box walk must
|
|
114
118
|
* not descend into them. */
|
|
115
119
|
box->is_inline_svg = true;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
box->
|
|
121
|
-
|
|
120
|
+
if (images != nullptr) {
|
|
121
|
+
box->svg_markup = images->inline_serialize(node, &styles);
|
|
122
|
+
int width = 0;
|
|
123
|
+
int height = 0;
|
|
124
|
+
if (images->svg_markup_size(box->svg_markup, &width, &height)) {
|
|
125
|
+
box->intrinsic_width = (float)width;
|
|
126
|
+
box->intrinsic_height = (float)height;
|
|
127
|
+
}
|
|
122
128
|
}
|
|
123
129
|
if (box->intrinsic_width <= 0.0f) box->intrinsic_width = 300.0f;
|
|
124
130
|
if (box->intrinsic_height <= 0.0f) box->intrinsic_height = 150.0f;
|
package/runtime/ext_gui/layout.h
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* `LayoutTree::compute` turns the styled DOM into a tree of `LayoutBox`es with
|
|
5
5
|
* absolute (viewport-relative) geometry. It implements the slice a UI needs:
|
|
6
6
|
* block flow, a simple inline formatting context with greedy line breaking
|
|
7
|
-
* (text metrics come from the
|
|
7
|
+
* (text metrics come from the stb_truetype stack) and single-line
|
|
8
8
|
* Flexbox. `<img>` is a replaced element sized from its intrinsic dimensions.
|
|
9
9
|
*
|
|
10
10
|
* Coordinates are absolute: `x`/`y` is the border-box origin measured from the
|
|
@@ -51,6 +51,10 @@ struct TextFragment {
|
|
|
51
51
|
float width = 0;
|
|
52
52
|
float height = 0;
|
|
53
53
|
std::string text;
|
|
54
|
+
/** A vertical-writing-mode run: `x`/`y`/`width`/`height` are its column box.
|
|
55
|
+
* `upright` is a CJK-style glyph; otherwise the run is rotated 90° clockwise. */
|
|
56
|
+
bool vertical = false;
|
|
57
|
+
bool upright = false;
|
|
54
58
|
};
|
|
55
59
|
|
|
56
60
|
/** A viewport-relative rectangle (`getBoundingClientRect` result). */
|
|
@@ -65,6 +69,8 @@ struct LayoutBox {
|
|
|
65
69
|
bool is_text = false;
|
|
66
70
|
std::string text; // only for text boxes
|
|
67
71
|
std::vector<TextFragment> fragments;
|
|
72
|
+
/** `border-radius` resolved against this border box (percent supported). */
|
|
73
|
+
float border_radius = 0.0f;
|
|
68
74
|
|
|
69
75
|
/* Replaced element (currently `<img>`): source + intrinsic pixel size. */
|
|
70
76
|
bool is_image = false;
|
|
@@ -140,7 +146,7 @@ class LayoutTree {
|
|
|
140
146
|
XtStyle root_style_;
|
|
141
147
|
};
|
|
142
148
|
|
|
143
|
-
/** Text width under `style` (shaped with the
|
|
149
|
+
/** Text width under `style` (shaped with the stb_truetype stack). */
|
|
144
150
|
float xt_layout_text_width(const std::string &text, const XtStyle &style);
|
|
145
151
|
|
|
146
152
|
/** The box's border box in device space: its layout rectangle pushed through
|
|
@@ -12,6 +12,37 @@
|
|
|
12
12
|
namespace xtgui {
|
|
13
13
|
namespace layout_flow_detail {
|
|
14
14
|
|
|
15
|
+
/** Give the line's free main-axis space to `auto` margins (CSS flexbox). Returns
|
|
16
|
+
* true when any item had one; the caller then skips `justify-content`. */
|
|
17
|
+
static bool applyAutoMainMargins(const std::vector<LayoutBox *> &items,
|
|
18
|
+
const std::vector<size_t> &line, bool row, float freeSpace) {
|
|
19
|
+
int count = 0;
|
|
20
|
+
for (size_t i : line) {
|
|
21
|
+
const XtStyle &style = *items[i]->style;
|
|
22
|
+
if (row) {
|
|
23
|
+
if (style.margin_left_auto) count++;
|
|
24
|
+
if (style.margin_right_auto) count++;
|
|
25
|
+
} else {
|
|
26
|
+
if (style.margin_top_auto) count++;
|
|
27
|
+
if (style.margin_bottom_auto) count++;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (count == 0) return false;
|
|
31
|
+
if (freeSpace < 0.0f) freeSpace = 0.0f;
|
|
32
|
+
float share = freeSpace / (float)count;
|
|
33
|
+
for (size_t i : line) {
|
|
34
|
+
const XtStyle &style = *items[i]->style;
|
|
35
|
+
if (row) {
|
|
36
|
+
if (style.margin_left_auto) items[i]->margin_left = share;
|
|
37
|
+
if (style.margin_right_auto) items[i]->margin_right = share;
|
|
38
|
+
} else {
|
|
39
|
+
if (style.margin_top_auto) items[i]->margin_top = share;
|
|
40
|
+
if (style.margin_bottom_auto) items[i]->margin_bottom = share;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
15
46
|
/** Partition `basis` (outer main sizes) into lines, breaking before an item
|
|
16
47
|
* that no longer fits on the current line. A single line is produced when
|
|
17
48
|
* `wrap` is false. */
|
|
@@ -37,8 +68,19 @@ static std::vector<std::vector<size_t>> breakIntoLines(const std::vector<float>
|
|
|
37
68
|
|
|
38
69
|
float Layouter::layoutFlex(LayoutBox *container, float contentWidth, float definiteHeight) const {
|
|
39
70
|
const XtStyle &containerStyle = *container->style;
|
|
40
|
-
|
|
41
|
-
|
|
71
|
+
/* In a vertical writing mode the inline axis is vertical, so a `row` flex
|
|
72
|
+
* container stacks its items top-to-bottom (and `column` lays them out
|
|
73
|
+
* horizontally). */
|
|
74
|
+
FlexDirection direction = containerStyle.flex_direction;
|
|
75
|
+
if (containerStyle.writing_mode != WritingMode::HorizontalTB) {
|
|
76
|
+
switch (direction) {
|
|
77
|
+
case FlexDirection::Row: direction = FlexDirection::Column; break;
|
|
78
|
+
case FlexDirection::RowReverse: direction = FlexDirection::ColumnReverse; break;
|
|
79
|
+
case FlexDirection::Column: direction = FlexDirection::Row; break;
|
|
80
|
+
case FlexDirection::ColumnReverse: direction = FlexDirection::RowReverse; break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
bool isRow = direction == FlexDirection::Row || direction == FlexDirection::RowReverse;
|
|
42
84
|
|
|
43
85
|
/* An absolutely positioned child is out of flow: it takes no part in the flex
|
|
44
86
|
* algorithm and is placed against the container's padding box instead. */
|
|
@@ -58,15 +100,17 @@ float Layouter::layoutFlex(LayoutBox *container, float contentWidth, float defin
|
|
|
58
100
|
px(isRow ? containerStyle.row_gap : containerStyle.column_gap, contentWidth,
|
|
59
101
|
containerStyle.font_size, 0.0f);
|
|
60
102
|
if (isRow)
|
|
61
|
-
return layoutFlexRow(container, items, contentWidth, definiteHeight, mainGap, crossGap
|
|
62
|
-
|
|
103
|
+
return layoutFlexRow(container, items, contentWidth, definiteHeight, mainGap, crossGap,
|
|
104
|
+
direction);
|
|
105
|
+
return layoutFlexColumn(container, items, contentWidth, definiteHeight, mainGap, crossGap,
|
|
106
|
+
direction);
|
|
63
107
|
}
|
|
64
108
|
|
|
65
109
|
float Layouter::layoutFlexRow(LayoutBox *container, const std::vector<LayoutBox *> &items,
|
|
66
110
|
float contentWidth, float definiteHeight, float mainGap,
|
|
67
|
-
float crossGap) const {
|
|
111
|
+
float crossGap, FlexDirection direction) const {
|
|
68
112
|
const XtStyle &containerStyle = *container->style;
|
|
69
|
-
bool reverse =
|
|
113
|
+
bool reverse = direction == FlexDirection::RowReverse;
|
|
70
114
|
bool wrap = containerStyle.flex_wrap != FlexWrap::NoWrap;
|
|
71
115
|
bool wrapReverse = containerStyle.flex_wrap == FlexWrap::WrapReverse;
|
|
72
116
|
size_t count = items.size();
|
|
@@ -169,12 +213,14 @@ float Layouter::layoutFlexRow(LayoutBox *container, const std::vector<LayoutBox
|
|
|
169
213
|
float freeSpace = mainSize - (sumMain + mainGap * (float)(line.size() - 1));
|
|
170
214
|
float offset = 0;
|
|
171
215
|
float between = mainGap;
|
|
216
|
+
if (applyAutoMainMargins(items, line, true, freeSpace)) freeSpace = 0.0f;
|
|
172
217
|
applyJustify(containerStyle.justify_content, freeSpace, line.size(), &offset, &between);
|
|
173
218
|
float cursor = container->content_x + offset;
|
|
174
219
|
for (size_t k = 0; k < line.size(); k++) {
|
|
175
220
|
size_t i = reverse ? line[line.size() - 1 - k] : line[k];
|
|
176
|
-
|
|
177
|
-
|
|
221
|
+
AlignItems align = items[i]->style->has_align_self ? items[i]->style->align_self
|
|
222
|
+
: containerStyle.align_items;
|
|
223
|
+
bool stretch = align == AlignItems::Stretch && isAuto(items[i]->style->height) &&
|
|
178
224
|
(definiteHeight >= 0 || lines.size() > 1);
|
|
179
225
|
if (stretch) {
|
|
180
226
|
float contentH = lineCross[li] - verticalEdges(items[i]);
|
|
@@ -184,12 +230,15 @@ float Layouter::layoutFlexRow(LayoutBox *container, const std::vector<LayoutBox
|
|
|
184
230
|
definiteHeight, contentW, contentH);
|
|
185
231
|
cross[i] = lineCross[li];
|
|
186
232
|
}
|
|
187
|
-
float crossOffset = crossOffsetFor(
|
|
233
|
+
float crossOffset = crossOffsetFor(align, lineCross[li], cross[i]);
|
|
188
234
|
float dx = cursor + items[i]->margin_left - items[i]->x;
|
|
189
235
|
float dy =
|
|
190
236
|
container->content_y + lineStart + crossOffset + items[i]->margin_top - items[i]->y;
|
|
191
237
|
translate(items[i], dx, dy);
|
|
192
|
-
|
|
238
|
+
float auto_extra = 0.0f;
|
|
239
|
+
if (items[i]->style->margin_left_auto) auto_extra += items[i]->margin_left;
|
|
240
|
+
if (items[i]->style->margin_right_auto) auto_extra += items[i]->margin_right;
|
|
241
|
+
cursor += mainSizes[i] + between + auto_extra;
|
|
193
242
|
}
|
|
194
243
|
lineOffset += lineCross[li] + lineStep;
|
|
195
244
|
}
|
|
@@ -198,9 +247,9 @@ float Layouter::layoutFlexRow(LayoutBox *container, const std::vector<LayoutBox
|
|
|
198
247
|
|
|
199
248
|
float Layouter::layoutFlexColumn(LayoutBox *container, const std::vector<LayoutBox *> &items,
|
|
200
249
|
float contentWidth, float definiteHeight, float mainGap,
|
|
201
|
-
float crossGap) const {
|
|
250
|
+
float crossGap, FlexDirection direction) const {
|
|
202
251
|
const XtStyle &containerStyle = *container->style;
|
|
203
|
-
bool reverse =
|
|
252
|
+
bool reverse = direction == FlexDirection::ColumnReverse;
|
|
204
253
|
bool wrap = containerStyle.flex_wrap != FlexWrap::NoWrap && definiteHeight >= 0;
|
|
205
254
|
bool wrapReverse = containerStyle.flex_wrap == FlexWrap::WrapReverse;
|
|
206
255
|
size_t count = items.size();
|
|
@@ -213,57 +262,104 @@ float Layouter::layoutFlexColumn(LayoutBox *container, const std::vector<LayoutB
|
|
|
213
262
|
outer[i] = verticalEdges(items[i]) + items[i]->height;
|
|
214
263
|
}
|
|
215
264
|
|
|
265
|
+
/* Main-axis basis: `flex-basis` when set, otherwise the measured height. A
|
|
266
|
+
* percentage basis needs a definite container height; with `height: auto` it
|
|
267
|
+
* behaves like `auto`, as on the row axis. */
|
|
268
|
+
std::vector<float> basis(count, 0.0f);
|
|
269
|
+
for (size_t i = 0; i < count; i++) {
|
|
270
|
+
const XtStyle &style = *items[i]->style;
|
|
271
|
+
float base = outer[i];
|
|
272
|
+
if (!isAuto(style.flex_basis)) {
|
|
273
|
+
float resolved = -1.0f;
|
|
274
|
+
if (style.flex_basis.unit != Unit::Percent || definiteHeight >= 0)
|
|
275
|
+
resolved = px(style.flex_basis, definiteHeight >= 0 ? definiteHeight : contentWidth,
|
|
276
|
+
style.font_size, -1.0f);
|
|
277
|
+
if (resolved >= 0) base = resolved + verticalEdges(items[i]);
|
|
278
|
+
}
|
|
279
|
+
basis[i] = base;
|
|
280
|
+
}
|
|
281
|
+
|
|
216
282
|
if (!wrap) {
|
|
217
283
|
float mainSize = definiteHeight >= 0 ? definiteHeight : -1;
|
|
218
|
-
std::vector<float> mainSizes =
|
|
284
|
+
std::vector<float> mainSizes = basis;
|
|
219
285
|
if (mainSize >= 0) {
|
|
220
|
-
float free = mainSize - sumOf(
|
|
286
|
+
float free = mainSize - sumOf(basis) - mainGap * (float)(count - 1);
|
|
221
287
|
if (free > 0) {
|
|
222
288
|
float totalGrow = 0;
|
|
223
289
|
for (LayoutBox *item : items) totalGrow += item->style->flex_grow;
|
|
224
|
-
if (totalGrow > 0)
|
|
225
|
-
for (size_t i = 0; i < count; i++)
|
|
290
|
+
if (totalGrow > 0)
|
|
291
|
+
for (size_t i = 0; i < count; i++)
|
|
226
292
|
mainSizes[i] += free * items[i]->style->flex_grow / totalGrow;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
293
|
+
} else if (free < 0) {
|
|
294
|
+
float totalShrink = 0;
|
|
295
|
+
for (size_t i = 0; i < count; i++)
|
|
296
|
+
totalShrink += items[i]->style->flex_shrink * basis[i];
|
|
297
|
+
if (totalShrink > 0)
|
|
298
|
+
for (size_t i = 0; i < count; i++) {
|
|
299
|
+
mainSizes[i] += free * (items[i]->style->flex_shrink * basis[i]) / totalShrink;
|
|
300
|
+
if (mainSizes[i] < 0) mainSizes[i] = 0;
|
|
231
301
|
}
|
|
232
|
-
}
|
|
233
302
|
}
|
|
234
303
|
} else {
|
|
235
|
-
mainSize = sumOf(
|
|
304
|
+
mainSize = sumOf(basis) + mainGap * (float)(count - 1);
|
|
305
|
+
}
|
|
306
|
+
/* Re-lay every item out at its used height: the basis, grow and shrink
|
|
307
|
+
* passes above all change it. */
|
|
308
|
+
for (size_t i = 0; i < count; i++) {
|
|
309
|
+
float contentH = mainSizes[i] - verticalEdges(items[i]);
|
|
310
|
+
if (contentH < 0) contentH = 0;
|
|
311
|
+
setContainingBlock(items[i], containingBlockAncestor(items[i]));
|
|
312
|
+
layoutBlock(items[i], container->content_x, container->content_y, contentWidth,
|
|
313
|
+
definiteHeight, -1, contentH);
|
|
236
314
|
}
|
|
237
315
|
float freeSpace = mainSize - (sumOf(mainSizes) + mainGap * (float)(count - 1));
|
|
238
316
|
float offset = 0;
|
|
239
317
|
float between = mainGap;
|
|
318
|
+
std::vector<size_t> all(count);
|
|
319
|
+
for (size_t i = 0; i < count; i++) all[i] = i;
|
|
320
|
+
if (applyAutoMainMargins(items, all, false, freeSpace)) freeSpace = 0.0f;
|
|
240
321
|
applyJustify(containerStyle.justify_content, freeSpace, count, &offset, &between);
|
|
241
322
|
float cursor = container->content_y + offset;
|
|
242
323
|
for (size_t k = 0; k < count; k++) {
|
|
243
324
|
size_t i = reverse ? (count - 1 - k) : k;
|
|
244
|
-
|
|
325
|
+
AlignItems align = items[i]->style->has_align_self ? items[i]->style->align_self
|
|
326
|
+
: containerStyle.align_items;
|
|
327
|
+
float crossSize = horizontalEdges(items[i]) + items[i]->width;
|
|
328
|
+
float crossOffset = crossOffsetFor(align, contentWidth, crossSize);
|
|
329
|
+
float dx = container->content_x + crossOffset + items[i]->margin_left - items[i]->x;
|
|
245
330
|
float dy = cursor + items[i]->margin_top - items[i]->y;
|
|
246
331
|
translate(items[i], dx, dy);
|
|
247
|
-
|
|
332
|
+
float auto_extra = 0.0f;
|
|
333
|
+
if (items[i]->style->margin_top_auto) auto_extra += items[i]->margin_top;
|
|
334
|
+
if (items[i]->style->margin_bottom_auto) auto_extra += items[i]->margin_bottom;
|
|
335
|
+
cursor += mainSizes[i] + between + auto_extra;
|
|
248
336
|
}
|
|
249
337
|
return mainSize;
|
|
250
338
|
}
|
|
251
339
|
|
|
252
340
|
/* Wrapped columns: lines run top-to-bottom and are placed side by side. */
|
|
253
|
-
std::vector<std::vector<size_t>> lines = breakIntoLines(
|
|
341
|
+
std::vector<std::vector<size_t>> lines = breakIntoLines(basis, definiteHeight, mainGap, true);
|
|
254
342
|
std::vector<float> mainSizes(count, 0.0f);
|
|
255
343
|
std::vector<float> lineCross(lines.size(), 0.0f);
|
|
256
344
|
for (size_t li = 0; li < lines.size(); li++) {
|
|
257
345
|
const std::vector<size_t> &line = lines[li];
|
|
258
346
|
float sumBase = 0;
|
|
259
|
-
for (size_t i : line) sumBase +=
|
|
260
|
-
for (size_t i : line) mainSizes[i] =
|
|
347
|
+
for (size_t i : line) sumBase += basis[i];
|
|
348
|
+
for (size_t i : line) mainSizes[i] = basis[i];
|
|
261
349
|
float free = definiteHeight - sumBase - mainGap * (float)(line.size() - 1);
|
|
262
350
|
if (free > 0) {
|
|
263
351
|
float totalGrow = 0;
|
|
264
352
|
for (size_t i : line) totalGrow += items[i]->style->flex_grow;
|
|
265
353
|
if (totalGrow > 0)
|
|
266
354
|
for (size_t i : line) mainSizes[i] += free * items[i]->style->flex_grow / totalGrow;
|
|
355
|
+
} else if (free < 0) {
|
|
356
|
+
float totalShrink = 0;
|
|
357
|
+
for (size_t i : line) totalShrink += items[i]->style->flex_shrink * basis[i];
|
|
358
|
+
if (totalShrink > 0)
|
|
359
|
+
for (size_t i : line) {
|
|
360
|
+
mainSizes[i] += free * (items[i]->style->flex_shrink * basis[i]) / totalShrink;
|
|
361
|
+
if (mainSizes[i] < 0) mainSizes[i] = 0;
|
|
362
|
+
}
|
|
267
363
|
}
|
|
268
364
|
for (size_t i : line) {
|
|
269
365
|
float contentH = mainSizes[i] - verticalEdges(items[i]);
|
|
@@ -308,14 +404,23 @@ float Layouter::layoutFlexColumn(LayoutBox *container, const std::vector<LayoutB
|
|
|
308
404
|
float freeSpace = definiteHeight - (sumMain + mainGap * (float)(line.size() - 1));
|
|
309
405
|
float offset = 0;
|
|
310
406
|
float between = mainGap;
|
|
407
|
+
if (applyAutoMainMargins(items, line, false, freeSpace)) freeSpace = 0.0f;
|
|
311
408
|
applyJustify(containerStyle.justify_content, freeSpace, line.size(), &offset, &between);
|
|
312
409
|
float cursor = container->content_y + offset;
|
|
313
410
|
for (size_t k = 0; k < line.size(); k++) {
|
|
314
411
|
size_t i = reverse ? line[line.size() - 1 - k] : line[k];
|
|
315
|
-
|
|
412
|
+
AlignItems align = items[i]->style->has_align_self ? items[i]->style->align_self
|
|
413
|
+
: containerStyle.align_items;
|
|
414
|
+
float crossSize = horizontalEdges(items[i]) + items[i]->width;
|
|
415
|
+
float crossOffset = crossOffsetFor(align, lineCross[li], crossSize);
|
|
416
|
+
float dx =
|
|
417
|
+
container->content_x + lineStart + crossOffset + items[i]->margin_left - items[i]->x;
|
|
316
418
|
float dy = cursor + items[i]->margin_top - items[i]->y;
|
|
317
419
|
translate(items[i], dx, dy);
|
|
318
|
-
|
|
420
|
+
float auto_extra = 0.0f;
|
|
421
|
+
if (items[i]->style->margin_top_auto) auto_extra += items[i]->margin_top;
|
|
422
|
+
if (items[i]->style->margin_bottom_auto) auto_extra += items[i]->margin_bottom;
|
|
423
|
+
cursor += mainSizes[i] + between + auto_extra;
|
|
319
424
|
}
|
|
320
425
|
lineOffset += lineCross[li] + lineStep;
|
|
321
426
|
}
|
|
@@ -46,13 +46,19 @@ float Layouter::preferredContentWidth(const LayoutBox *box, float available) con
|
|
|
46
46
|
if (!isAuto(style.width)) return px(style.width, available, style.font_size, 0.0f);
|
|
47
47
|
if ((box->is_image || box->is_canvas || box->is_inline_svg) && box->intrinsic_width > 0)
|
|
48
48
|
return box->intrinsic_width;
|
|
49
|
-
if (box->is_text)
|
|
49
|
+
if (box->is_text) {
|
|
50
|
+
if (style.writing_mode != WritingMode::HorizontalTB) return lineHeightOf(style);
|
|
51
|
+
return textWidth(box->text, style);
|
|
52
|
+
}
|
|
50
53
|
/* A row flex container's intrinsic width is the sum of its items (and gaps);
|
|
51
54
|
* taking the widest child would make a shrink-to-fit toolbar narrower than
|
|
52
55
|
* its buttons and then shrink every button to zero. */
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
bool flex_row = style.flex_direction == FlexDirection::Row ||
|
|
57
|
+
style.flex_direction == FlexDirection::RowReverse;
|
|
58
|
+
/* In a vertical writing mode the flex row axis is vertical, so the intrinsic
|
|
59
|
+
* width is the widest item rather than the sum of the row. */
|
|
60
|
+
if (style.writing_mode != WritingMode::HorizontalTB) flex_row = !flex_row;
|
|
61
|
+
if (style.display == Display::Flex && flex_row) {
|
|
56
62
|
float total = 0;
|
|
57
63
|
int count = 0;
|
|
58
64
|
for (const std::unique_ptr<LayoutBox> &child : box->children) {
|
|
@@ -70,7 +76,11 @@ float Layouter::preferredContentWidth(const LayoutBox *box, float available) con
|
|
|
70
76
|
/* Out-of-flow children do not contribute to the parent's intrinsic width. */
|
|
71
77
|
if (isAbsoluteChild(child.get())) continue;
|
|
72
78
|
if (child->is_text) {
|
|
73
|
-
|
|
79
|
+
if (child->style->writing_mode != WritingMode::HorizontalTB) {
|
|
80
|
+
best = std::max(best, lineHeightOf(*child->style));
|
|
81
|
+
} else {
|
|
82
|
+
best = std::max(best, textWidth(child->text, *child->style));
|
|
83
|
+
}
|
|
74
84
|
} else {
|
|
75
85
|
best = std::max(best,
|
|
76
86
|
preferredContentWidth(child.get(), available) + horizontalEdges(child.get()));
|
|
@@ -204,6 +214,9 @@ float Layouter::layoutBlock(LayoutBox *box, float x, float y, float availableWid
|
|
|
204
214
|
* `display:grid`), and `layoutChildren` would otherwise give them no geometry
|
|
205
215
|
* or fragment at all — the glyph then silently disappears from paint. */
|
|
206
216
|
if (box->is_text) {
|
|
217
|
+
if (style.writing_mode != WritingMode::HorizontalTB) {
|
|
218
|
+
return layoutVerticalTextBox(box, x, y, lineHeightOf(style));
|
|
219
|
+
}
|
|
207
220
|
float textW = textWidth(box->text, style);
|
|
208
221
|
float textH = lineHeightOf(style);
|
|
209
222
|
box->x = x;
|
|
@@ -260,6 +273,24 @@ float Layouter::layoutBlock(LayoutBox *box, float x, float y, float availableWid
|
|
|
260
273
|
box->content_width = contentWidth;
|
|
261
274
|
box->width =
|
|
262
275
|
box->border_left + box->padding_left + contentWidth + box->padding_right + box->border_right;
|
|
276
|
+
/* Block-level horizontal auto margins absorb the leftover inline space; two
|
|
277
|
+
* auto margins centre the box. */
|
|
278
|
+
bool autoLeft = style.margin_left_auto;
|
|
279
|
+
bool autoRight = style.margin_right_auto;
|
|
280
|
+
if (!outOfFlow && (autoLeft || autoRight)) {
|
|
281
|
+
float free = availableWidth - box->width - (autoLeft ? 0.0f : box->margin_left) -
|
|
282
|
+
(autoRight ? 0.0f : box->margin_right);
|
|
283
|
+
if (free < 0.0f) free = 0.0f;
|
|
284
|
+
if (autoLeft && autoRight) {
|
|
285
|
+
box->margin_left = box->margin_right = free / 2.0f;
|
|
286
|
+
} else if (autoLeft) {
|
|
287
|
+
box->margin_left = free;
|
|
288
|
+
} else {
|
|
289
|
+
box->margin_right = free;
|
|
290
|
+
}
|
|
291
|
+
box->x = x + box->margin_left;
|
|
292
|
+
box->content_x = box->x + box->border_left + box->padding_left;
|
|
293
|
+
}
|
|
263
294
|
|
|
264
295
|
float definiteHeight = -1;
|
|
265
296
|
if (!isAuto(style.height) && !(style.height.unit == Unit::Percent && availableHeight < 0)) {
|
|
@@ -278,6 +309,17 @@ float Layouter::layoutBlock(LayoutBox *box, float x, float y, float availableWid
|
|
|
278
309
|
definiteHeight = box->cb_height - top - bottom - verticalEdges(box);
|
|
279
310
|
if (definiteHeight < 0) definiteHeight = 0;
|
|
280
311
|
}
|
|
312
|
+
/* `aspect-ratio` derives the content height from the used width when the
|
|
313
|
+
* height is auto. (Height-to-width derivation is not implemented: no page in
|
|
314
|
+
* the target corpus needs it.) */
|
|
315
|
+
if (definiteHeight < 0 && forcedContentHeight < 0 && style.aspect_ratio > 0.0f) {
|
|
316
|
+
float borderWidth = box->border_left + box->padding_left + contentWidth +
|
|
317
|
+
box->padding_right + box->border_right;
|
|
318
|
+
float borderHeight = borderWidth / style.aspect_ratio;
|
|
319
|
+
definiteHeight = borderHeight - box->border_top - box->border_bottom - box->padding_top -
|
|
320
|
+
box->padding_bottom;
|
|
321
|
+
if (definiteHeight < 0) definiteHeight = 0;
|
|
322
|
+
}
|
|
281
323
|
|
|
282
324
|
float contentHeight;
|
|
283
325
|
if (forcedContentHeight >= 0) {
|
|
@@ -304,6 +346,8 @@ float Layouter::layoutBlock(LayoutBox *box, float x, float y, float availableWid
|
|
|
304
346
|
box->content_height = contentHeight;
|
|
305
347
|
box->height =
|
|
306
348
|
box->border_top + box->padding_top + contentHeight + box->padding_bottom + box->border_bottom;
|
|
349
|
+
box->border_radius = xt_style_border_radius_px(style.border_radius, box->width, box->height,
|
|
350
|
+
em, rootFontSize, vw, vh);
|
|
307
351
|
box->baseline = box->height;
|
|
308
352
|
/* Now that this box has its final size, its padding box is the containing
|
|
309
353
|
* block its own children resolve against — and only now can an out-of-flow
|
|
@@ -318,8 +362,11 @@ float Layouter::layoutBlock(LayoutBox *box, float x, float y, float availableWid
|
|
|
318
362
|
}
|
|
319
363
|
|
|
320
364
|
bool Layouter::isAbsoluteChild(const LayoutBox *box) {
|
|
321
|
-
|
|
322
|
-
|
|
365
|
+
/* A text box shares its element's style pointer, but a text node can never be
|
|
366
|
+
* out of flow: treating it as absolute dropped direct text out of an
|
|
367
|
+
* absolutely positioned box (and made a shrink-to-fit width zero). */
|
|
368
|
+
return box != nullptr && !box->is_text && box->style != nullptr &&
|
|
369
|
+
(box->style->position == Position::Absolute || box->style->position == Position::Fixed);
|
|
323
370
|
}
|
|
324
371
|
|
|
325
372
|
void Layouter::layoutAbsoluteChildren(LayoutBox *block, float contentWidth,
|
|
@@ -423,6 +470,10 @@ float Layouter::layoutChildren(LayoutBox *block, float contentWidth, float avail
|
|
|
423
470
|
}
|
|
424
471
|
if (display == Display::Block || display == Display::ListItem || display == Display::Flex ||
|
|
425
472
|
display == Display::Grid || child->style == nullptr) {
|
|
473
|
+
if (childStyle != nullptr && childStyle->inline_level) {
|
|
474
|
+
inlineRun.push_back(child.get());
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
426
477
|
flush();
|
|
427
478
|
resolveEdges(child.get(), contentWidth);
|
|
428
479
|
/* Adjacent vertical margins collapse: the gap is the collapsed value of
|
|
@@ -40,9 +40,15 @@ struct InlineItem {
|
|
|
40
40
|
float width = 0;
|
|
41
41
|
float height = 0;
|
|
42
42
|
float ascent = 0;
|
|
43
|
+
/** Atomic inline boxes: `vertical-align` shift (positive raises). */
|
|
44
|
+
float shift = 0.0f;
|
|
43
45
|
bool is_space = false;
|
|
44
46
|
bool atomic = false; // inline-block
|
|
45
47
|
bool force_break = false; // explicit newline (white-space: pre)
|
|
48
|
+
/** A character in a vertical writing mode: `width` is the column width and
|
|
49
|
+
* `height` the advance along the column. */
|
|
50
|
+
bool vertical = false;
|
|
51
|
+
bool upright = false;
|
|
46
52
|
};
|
|
47
53
|
|
|
48
54
|
/** One float taking part in the current block's formatting context. Floats are
|
|
@@ -132,6 +138,23 @@ inline float toPx(const Length &length, float base, float em, float root, float
|
|
|
132
138
|
}
|
|
133
139
|
return first ? fallback : best;
|
|
134
140
|
}
|
|
141
|
+
case Unit::Calc: {
|
|
142
|
+
if (length.args.size() < 2) return fallback;
|
|
143
|
+
float left = toPx(length.args[0], base, em, root, vw, vh, fallback);
|
|
144
|
+
float right = toPx(length.args[1], base, em, root, vw, vh, fallback);
|
|
145
|
+
switch (length.op) {
|
|
146
|
+
case '+':
|
|
147
|
+
return left + right;
|
|
148
|
+
case '-':
|
|
149
|
+
return left - right;
|
|
150
|
+
case '*':
|
|
151
|
+
return left * right;
|
|
152
|
+
case '/':
|
|
153
|
+
return right == 0.0f ? fallback : left / right;
|
|
154
|
+
default:
|
|
155
|
+
return left;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
135
158
|
case Unit::Px:
|
|
136
159
|
case Unit::Number:
|
|
137
160
|
return length.value;
|
|
@@ -174,6 +197,7 @@ inline FontSpec specOf(const XtStyle &style) {
|
|
|
174
197
|
spec.pixel_size = style.font_size;
|
|
175
198
|
spec.weight = style.font_weight;
|
|
176
199
|
spec.italic = style.font_style == FontStyle::Italic;
|
|
200
|
+
spec.letter_spacing = style.letter_spacing.unit == Unit::Px ? style.letter_spacing.value : 0.0f;
|
|
177
201
|
return spec;
|
|
178
202
|
}
|
|
179
203
|
|
|
@@ -259,6 +283,9 @@ struct Layouter {
|
|
|
259
283
|
|
|
260
284
|
void flattenInline(LayoutBox *box, float contentWidth, std::vector<InlineItem> &out) const;
|
|
261
285
|
|
|
286
|
+
/** Flatten a text box in a vertical writing mode into per-character items. */
|
|
287
|
+
void flattenVerticalText(LayoutBox *box, std::vector<InlineItem> &out) const;
|
|
288
|
+
|
|
262
289
|
void computeInlineGeometry(LayoutBox *box) const;
|
|
263
290
|
|
|
264
291
|
/** Lay out an inline formatting context; returns its consumed height. When
|
|
@@ -267,6 +294,14 @@ struct Layouter {
|
|
|
267
294
|
float layoutInlineRun(LayoutBox *container, const std::vector<LayoutBox *> &run,
|
|
268
295
|
float contentWidth, float y, const FloatContext *floats = nullptr) const;
|
|
269
296
|
|
|
297
|
+
/** Vertical writing mode: place already-flattened character items in columns
|
|
298
|
+
* (right-to-left for `vertical-rl`). Returns the consumed height. */
|
|
299
|
+
float layoutVerticalRun(LayoutBox *container, const std::vector<InlineItem> &items,
|
|
300
|
+
float contentWidth, float y) const;
|
|
301
|
+
|
|
302
|
+
/** Lay out a text box whose style has a vertical writing mode. */
|
|
303
|
+
float layoutVerticalTextBox(LayoutBox *box, float x, float y, float width) const;
|
|
304
|
+
|
|
270
305
|
float layoutFlex(LayoutBox *container, float contentWidth, float definiteHeight) const;
|
|
271
306
|
|
|
272
307
|
/** Grid formatting context (explicit/implicit tracks, gaps, auto-placement
|
|
@@ -276,13 +311,13 @@ struct Layouter {
|
|
|
276
311
|
/** Flex row main-axis layout, wrapping items into lines when `flex-wrap`
|
|
277
312
|
* allows it. Returns the used cross size. */
|
|
278
313
|
float layoutFlexRow(LayoutBox *container, const std::vector<LayoutBox *> &items,
|
|
279
|
-
float contentWidth, float definiteHeight, float mainGap,
|
|
280
|
-
|
|
314
|
+
float contentWidth, float definiteHeight, float mainGap, float crossGap,
|
|
315
|
+
FlexDirection direction) const;
|
|
281
316
|
|
|
282
317
|
/** Flex column main-axis layout (wrapping needs a definite height). */
|
|
283
318
|
float layoutFlexColumn(LayoutBox *container, const std::vector<LayoutBox *> &items,
|
|
284
|
-
float contentWidth, float definiteHeight, float mainGap,
|
|
285
|
-
|
|
319
|
+
float contentWidth, float definiteHeight, float mainGap, float crossGap,
|
|
320
|
+
FlexDirection direction) const;
|
|
286
321
|
|
|
287
322
|
static float sumOf(const std::vector<float> &values);
|
|
288
323
|
|
|
@@ -301,6 +301,37 @@ float Layouter::layoutGrid(LayoutBox *container, float contentWidth,
|
|
|
301
301
|
}
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
+
/* A definite container height is the free space an `fr` row divides, exactly
|
|
305
|
+
* like an `fr` column above. Size the fixed/auto rows first, then share what
|
|
306
|
+
* is left by flex fraction; without this a grid whose only height comes from
|
|
307
|
+
* `aspect-ratio` collapses its `repeat(n, 1fr)` rows to their content. */
|
|
308
|
+
float rowFr = 0;
|
|
309
|
+
for (const GridTrack &row : rows) rowFr += row.fr;
|
|
310
|
+
if (rowFr > 0 && definiteHeight >= 0) {
|
|
311
|
+
float fixedRows = gapY * (float)(rows.size() - 1);
|
|
312
|
+
for (size_t r = 0; r < rows.size(); r++)
|
|
313
|
+
if (rows[r].fr <= 0) fixedRows += rowHeight[r];
|
|
314
|
+
float remaining = definiteHeight - fixedRows;
|
|
315
|
+
if (remaining < 0) remaining = 0;
|
|
316
|
+
for (int pass = 0; pass < 2; pass++) {
|
|
317
|
+
float used = 0;
|
|
318
|
+
for (size_t r = 0; r < rows.size(); r++) {
|
|
319
|
+
if (rows[r].fr <= 0) continue;
|
|
320
|
+
float share = remaining * rows[r].fr / rowFr;
|
|
321
|
+
float minimum = trackPx(*this, rows[r], false, contentWidth, style.font_size);
|
|
322
|
+
if (minimum > share) share = minimum;
|
|
323
|
+
rowHeight[r] = share;
|
|
324
|
+
used += share;
|
|
325
|
+
}
|
|
326
|
+
if (used <= remaining + 0.01f) break;
|
|
327
|
+
/* An intrinsic minimum forced a deficit; shrink the fr share back. */
|
|
328
|
+
float scale = remaining / used;
|
|
329
|
+
for (size_t r = 0; r < rows.size(); r++)
|
|
330
|
+
if (rows[r].fr > 0) rowHeight[r] *= scale;
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
304
335
|
float totalHeight = gapY * (float)(rows.size() - 1);
|
|
305
336
|
for (float value : rowHeight) totalHeight += value;
|
|
306
337
|
if (rows.size() == 1 && definiteHeight >= 0 && definiteHeight > totalHeight)
|