xbintsc 0.3.69 → 0.3.81
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 +57 -4
- package/README.zh-CN.md +51 -2
- package/dist/src/driver/compiler.js +6 -1
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/extensions/gui/index.js +10 -2
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/tests/e2e/gui-advanced.test.d.ts +6 -0
- package/dist/tests/e2e/gui-advanced.test.js +261 -0
- package/dist/tests/e2e/gui-advanced.test.js.map +1 -0
- package/dist/tests/e2e/gui-canvas.test.d.ts +9 -0
- package/dist/tests/e2e/gui-canvas.test.js +233 -0
- package/dist/tests/e2e/gui-canvas.test.js.map +1 -0
- package/dist/tests/e2e/gui-example-svg.test.d.ts +21 -0
- package/dist/tests/e2e/gui-example-svg.test.js +105 -0
- package/dist/tests/e2e/gui-example-svg.test.js.map +1 -0
- package/dist/tests/e2e/gui-example.test.d.ts +1 -1
- package/dist/tests/e2e/gui-example.test.js +2 -2
- package/dist/tests/e2e/gui-helpers.d.ts +3 -0
- package/dist/tests/e2e/gui-helpers.js +5 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -1
- package/dist/tests/e2e/gui-layout.test.js +73 -0
- package/dist/tests/e2e/gui-layout.test.js.map +1 -1
- package/dist/tests/e2e/gui-media.test.d.ts +8 -0
- package/dist/tests/e2e/gui-media.test.js +119 -0
- package/dist/tests/e2e/gui-media.test.js.map +1 -0
- package/dist/tests/e2e/gui-paint.test.js +40 -0
- package/dist/tests/e2e/gui-paint.test.js.map +1 -1
- package/dist/tests/e2e/gui-transform.test.d.ts +7 -0
- package/dist/tests/e2e/gui-transform.test.js +198 -0
- package/dist/tests/e2e/gui-transform.test.js.map +1 -0
- package/doc/ai/cli.md +5 -0
- package/doc/ai/extensions.md +20 -3
- package/doc/ai/zh-CN/extensions.md +14 -3
- package/doc/gui/animation.md +97 -0
- package/doc/gui/api.md +92 -0
- package/doc/gui/build.md +91 -0
- package/doc/gui/canvas.md +98 -0
- package/doc/gui/dom.md +128 -0
- package/doc/gui/input.md +25 -0
- package/doc/gui/known-issues.md +78 -0
- package/doc/gui/layout.md +108 -0
- package/doc/gui/media.md +87 -0
- package/doc/gui/rendering.md +113 -0
- package/doc/gui/runtime.md +88 -0
- package/doc/gui-scripts.md +7 -3
- package/doc/gui.md +109 -612
- package/doc/implemented.md +1 -0
- package/doc/zh-CN/gui/animation.md +93 -0
- package/doc/zh-CN/gui/api.md +88 -0
- package/doc/zh-CN/gui/build.md +84 -0
- package/doc/zh-CN/gui/canvas.md +80 -0
- package/doc/zh-CN/gui/dom.md +119 -0
- package/doc/zh-CN/gui/input.md +23 -0
- package/doc/zh-CN/gui/known-issues.md +60 -0
- package/doc/zh-CN/gui/layout.md +91 -0
- package/doc/zh-CN/gui/media.md +71 -0
- package/doc/zh-CN/gui/rendering.md +92 -0
- package/doc/zh-CN/gui/runtime.md +76 -0
- package/doc/zh-CN/gui-scripts.md +6 -2
- package/doc/zh-CN/gui.md +89 -532
- package/doc/zh-CN/implemented.md +1 -0
- package/llms.txt +15 -2
- package/package.json +1 -1
- package/runtime/ext_gui/canvas.cpp +580 -0
- package/runtime/ext_gui/canvas.h +170 -0
- package/runtime/ext_gui/canvas_internal.h +59 -0
- package/runtime/ext_gui/canvas_raster.cpp +254 -0
- package/runtime/ext_gui/document.cpp +45 -4
- package/runtime/ext_gui/document.h +15 -1
- package/runtime/ext_gui/dom_api.cpp +3 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +548 -0
- package/runtime/ext_gui/dom_api_internal.h +73 -0
- package/runtime/ext_gui/dom_api_media.cpp +274 -0
- package/runtime/ext_gui/dom_api_node.cpp +6 -4
- package/runtime/ext_gui/dom_api_proto.cpp +80 -0
- package/runtime/ext_gui/gui.cpp +6 -0
- package/runtime/ext_gui/gui_canvas.cpp +95 -0
- package/runtime/ext_gui/gui_engine.h +49 -0
- package/runtime/ext_gui/gui_events.cpp +21 -5
- package/runtime/ext_gui/gui_media.cpp +193 -0
- package/runtime/ext_gui/image.cpp +31 -12
- package/runtime/ext_gui/image.h +20 -7
- package/runtime/ext_gui/layout.cpp +130 -6
- package/runtime/ext_gui/layout.h +54 -1
- package/runtime/ext_gui/layout_flex.cpp +263 -99
- package/runtime/ext_gui/layout_flow.cpp +85 -9
- package/runtime/ext_gui/layout_flow_internal.h +84 -5
- package/runtime/ext_gui/layout_inline.cpp +54 -37
- package/runtime/ext_gui/media.cpp +514 -0
- package/runtime/ext_gui/media.h +104 -0
- package/runtime/ext_gui/nanosvg.h +3278 -0
- package/runtime/ext_gui/nanosvgrast.h +1472 -0
- package/runtime/ext_gui/paint.cpp +156 -38
- package/runtime/ext_gui/paint.h +42 -6
- package/runtime/ext_gui/renderer.cpp +117 -12
- package/runtime/ext_gui/renderer.h +49 -9
- package/runtime/ext_gui/renderer_image.cpp +205 -78
- package/runtime/ext_gui/renderer_internal.h +12 -9
- package/runtime/ext_gui/renderer_text.cpp +46 -39
- package/runtime/ext_gui/style.h +21 -0
- package/runtime/ext_gui/style_print.cpp +17 -0
- package/runtime/ext_gui/style_ua.cpp +7 -0
- package/runtime/ext_gui/style_values.cpp +83 -1
- package/runtime/ext_gui/svg.cpp +182 -0
- package/runtime/ext_gui/svg.h +46 -0
- package/runtime/ext_gui/text.cpp +156 -25
- package/runtime/ext_gui/text.h +11 -2
- package/runtime/ext_gui/transform.cpp +412 -0
- package/runtime/ext_gui/transform.h +112 -0
- package/runtime/ext_gui/window.cpp +10 -5
- package/scripts/build-gui.ts +148 -6
- package/scripts/check-file-length.ts +5 -1
- package/scripts/install-gui-deps.sh +28 -0
- package/src/driver/compiler.ts +6 -1
- package/src/extensions/gui/index.ts +10 -2
|
@@ -9,13 +9,144 @@
|
|
|
9
9
|
#include <cstdio>
|
|
10
10
|
#include <cstring>
|
|
11
11
|
|
|
12
|
+
#include "canvas.h"
|
|
12
13
|
#include "image.h"
|
|
14
|
+
#include "media.h"
|
|
13
15
|
|
|
14
16
|
#include "renderer_shaders.h"
|
|
15
17
|
|
|
16
18
|
namespace xtgui {
|
|
17
19
|
namespace renderer_detail {
|
|
18
20
|
|
|
21
|
+
/** One live `<video>` texture. The texture is created once per pixel size and
|
|
22
|
+
* re-uploaded in place whenever the decoder advances (`generation`). */
|
|
23
|
+
struct MediaTexture {
|
|
24
|
+
SDL_GPUTexture *texture = nullptr;
|
|
25
|
+
int width = 0;
|
|
26
|
+
int height = 0;
|
|
27
|
+
uint64_t generation = ~0ull;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
static std::unordered_map<uint64_t, MediaTexture> g_media_textures;
|
|
31
|
+
|
|
32
|
+
/** One live `<canvas>` texture, keyed by the canvas id from `canvas.h`. It is
|
|
33
|
+
* re-uploaded in place whenever the canvas bumps its `generation`. */
|
|
34
|
+
static std::unordered_map<uint64_t, MediaTexture> g_canvas_textures;
|
|
35
|
+
|
|
36
|
+
/** Upload RGBA8 `pixels` into `texture` through a staging buffer, submitting
|
|
37
|
+
* its own command buffer. Because command buffers run in submission order and
|
|
38
|
+
* this one is submitted before the frame that samples the texture, re-uploading
|
|
39
|
+
* a texture that the previous frame drew is safe. */
|
|
40
|
+
static bool uploadPixels(SDL_GPUDevice *device, SDL_GPUTexture *texture, int width, int height,
|
|
41
|
+
const void *pixels, uint32_t bytes) {
|
|
42
|
+
if (bytes == 0) return false;
|
|
43
|
+
SDL_GPUTransferBufferCreateInfo transfer_info;
|
|
44
|
+
memset(&transfer_info, 0, sizeof(transfer_info));
|
|
45
|
+
transfer_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
|
|
46
|
+
transfer_info.size = bytes;
|
|
47
|
+
SDL_GPUTransferBuffer *transfer = SDL_CreateGPUTransferBuffer(device, &transfer_info);
|
|
48
|
+
if (transfer == nullptr) return false;
|
|
49
|
+
void *mapped = SDL_MapGPUTransferBuffer(device, transfer, false);
|
|
50
|
+
std::memcpy(mapped, pixels, bytes);
|
|
51
|
+
SDL_UnmapGPUTransferBuffer(device, transfer);
|
|
52
|
+
|
|
53
|
+
SDL_GPUCommandBuffer *cmd = SDL_AcquireGPUCommandBuffer(device);
|
|
54
|
+
if (cmd == nullptr) {
|
|
55
|
+
SDL_ReleaseGPUTransferBuffer(device, transfer);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
SDL_GPUTextureTransferInfo source;
|
|
59
|
+
memset(&source, 0, sizeof(source));
|
|
60
|
+
source.transfer_buffer = transfer;
|
|
61
|
+
source.offset = 0;
|
|
62
|
+
source.pixels_per_row = (Uint32)width;
|
|
63
|
+
source.rows_per_layer = (Uint32)height;
|
|
64
|
+
SDL_GPUTextureRegion destination;
|
|
65
|
+
memset(&destination, 0, sizeof(destination));
|
|
66
|
+
destination.texture = texture;
|
|
67
|
+
destination.mip_level = 0;
|
|
68
|
+
destination.layer = 0;
|
|
69
|
+
destination.w = (Uint32)width;
|
|
70
|
+
destination.h = (Uint32)height;
|
|
71
|
+
destination.d = 1;
|
|
72
|
+
SDL_GPUCopyPass *copy = SDL_BeginGPUCopyPass(cmd);
|
|
73
|
+
SDL_UploadToGPUTexture(copy, &source, &destination, false);
|
|
74
|
+
SDL_EndGPUCopyPass(copy);
|
|
75
|
+
SDL_SubmitGPUCommandBuffer(cmd);
|
|
76
|
+
SDL_ReleaseGPUTransferBuffer(device, transfer);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Ensure the texture for `media_id` exists and holds the decoder's newest
|
|
81
|
+
* frame. Returns `nullptr` before the first frame is decoded. */
|
|
82
|
+
static SDL_GPUTexture *ensureMediaTexture(SDL_GPUDevice *device, uint64_t media_id) {
|
|
83
|
+
xtgui::MediaPlayer *player = xtgui::xt_media_by_id(media_id);
|
|
84
|
+
if (player == nullptr) return nullptr;
|
|
85
|
+
const xtgui::Image *image = player->frame();
|
|
86
|
+
MediaTexture &entry = g_media_textures[media_id];
|
|
87
|
+
if (image == nullptr || image->width <= 0 || image->height <= 0) return entry.texture;
|
|
88
|
+
if (entry.texture == nullptr || entry.width != image->width || entry.height != image->height) {
|
|
89
|
+
if (entry.texture != nullptr) SDL_ReleaseGPUTexture(device, entry.texture);
|
|
90
|
+
SDL_GPUTextureCreateInfo info;
|
|
91
|
+
memset(&info, 0, sizeof(info));
|
|
92
|
+
info.type = SDL_GPU_TEXTURETYPE_2D;
|
|
93
|
+
info.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
|
94
|
+
info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
|
|
95
|
+
info.width = (Uint32)image->width;
|
|
96
|
+
info.height = (Uint32)image->height;
|
|
97
|
+
info.layer_count_or_depth = 1;
|
|
98
|
+
info.num_levels = 1;
|
|
99
|
+
info.sample_count = SDL_GPU_SAMPLECOUNT_1;
|
|
100
|
+
entry.texture = SDL_CreateGPUTexture(device, &info);
|
|
101
|
+
entry.width = image->width;
|
|
102
|
+
entry.height = image->height;
|
|
103
|
+
entry.generation = ~0ull;
|
|
104
|
+
if (entry.texture == nullptr) return nullptr;
|
|
105
|
+
}
|
|
106
|
+
if (entry.generation != player->frame_generation()) {
|
|
107
|
+
if (uploadPixels(device, entry.texture, entry.width, entry.height, image->pixels.data(),
|
|
108
|
+
(uint32_t)image->pixels.size())) {
|
|
109
|
+
entry.generation = player->frame_generation();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return entry.texture;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Ensure the texture for `canvas_id` exists and holds the canvas's newest
|
|
116
|
+
* pixels. Returns `nullptr` for an unknown or empty canvas. */
|
|
117
|
+
static SDL_GPUTexture *ensureCanvasTexture(SDL_GPUDevice *device, uint64_t canvas_id) {
|
|
118
|
+
xtgui::Canvas2D *canvas = xtgui::xt_canvas_by_id(canvas_id);
|
|
119
|
+
if (canvas == nullptr) return nullptr;
|
|
120
|
+
const xtgui::Image &image = canvas->image();
|
|
121
|
+
if (image.width <= 0 || image.height <= 0) return nullptr;
|
|
122
|
+
MediaTexture &entry = g_canvas_textures[canvas_id];
|
|
123
|
+
if (entry.texture == nullptr || entry.width != image.width || entry.height != image.height) {
|
|
124
|
+
if (entry.texture != nullptr) SDL_ReleaseGPUTexture(device, entry.texture);
|
|
125
|
+
SDL_GPUTextureCreateInfo info;
|
|
126
|
+
memset(&info, 0, sizeof(info));
|
|
127
|
+
info.type = SDL_GPU_TEXTURETYPE_2D;
|
|
128
|
+
info.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
|
129
|
+
info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
|
|
130
|
+
info.width = (Uint32)image.width;
|
|
131
|
+
info.height = (Uint32)image.height;
|
|
132
|
+
info.layer_count_or_depth = 1;
|
|
133
|
+
info.num_levels = 1;
|
|
134
|
+
info.sample_count = SDL_GPU_SAMPLECOUNT_1;
|
|
135
|
+
entry.texture = SDL_CreateGPUTexture(device, &info);
|
|
136
|
+
entry.width = image.width;
|
|
137
|
+
entry.height = image.height;
|
|
138
|
+
entry.generation = ~0ull;
|
|
139
|
+
if (entry.texture == nullptr) return nullptr;
|
|
140
|
+
}
|
|
141
|
+
if (entry.generation != canvas->generation()) {
|
|
142
|
+
if (uploadPixels(device, entry.texture, entry.width, entry.height, image.pixels.data(),
|
|
143
|
+
(uint32_t)image.pixels.size())) {
|
|
144
|
+
entry.generation = canvas->generation();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return entry.texture;
|
|
148
|
+
}
|
|
149
|
+
|
|
19
150
|
SDL_GPUGraphicsPipeline *createImagePipeline(SDL_GPUDevice *device, SDL_Window *reference) {
|
|
20
151
|
const ShaderSource image_vs = selectShader(device, ShaderProgram::Image, false);
|
|
21
152
|
const ShaderSource image_fs = selectShader(device, ShaderProgram::Image, true);
|
|
@@ -96,15 +227,22 @@ SDL_GPUGraphicsPipeline *createImagePipeline(SDL_GPUDevice *device, SDL_Window *
|
|
|
96
227
|
return pipeline;
|
|
97
228
|
}
|
|
98
229
|
|
|
99
|
-
/** Decode `src` (cached) and upload it as an RGBA texture
|
|
100
|
-
|
|
101
|
-
|
|
230
|
+
/** Decode `src` (cached) and upload it as an RGBA texture. Vector sources are
|
|
231
|
+
* rasterised at the drawn pixel size and cached by that size; bitmaps decode at
|
|
232
|
+
* their intrinsic size and are cached by path. */
|
|
233
|
+
SDL_GPUTexture *ensureImageTexture(SDL_GPUDevice *device, const std::string &src, int pixel_width,
|
|
234
|
+
int pixel_height) {
|
|
235
|
+
bool vector_source = xtgui::xt_image_is_vector(src);
|
|
236
|
+
std::string key =
|
|
237
|
+
vector_source ? src + "@" + std::to_string(pixel_width) + "x" + std::to_string(pixel_height)
|
|
238
|
+
: src;
|
|
239
|
+
auto found = g_image_textures.find(key);
|
|
102
240
|
if (found != g_image_textures.end()) return found->second;
|
|
103
241
|
|
|
104
|
-
const xtgui::Image *image = xtgui::xt_image_load(src);
|
|
242
|
+
const xtgui::Image *image = xtgui::xt_image_load(src, pixel_width, pixel_height);
|
|
105
243
|
if (image == nullptr || image->width <= 0 || image->height <= 0) {
|
|
106
244
|
fprintf(stderr, "xt_gui: could not decode image '%s'\n", src.c_str());
|
|
107
|
-
g_image_textures.emplace(
|
|
245
|
+
g_image_textures.emplace(key, nullptr);
|
|
108
246
|
return nullptr;
|
|
109
247
|
}
|
|
110
248
|
|
|
@@ -121,56 +259,18 @@ SDL_GPUTexture *ensureImageTexture(SDL_GPUDevice *device, const std::string &src
|
|
|
121
259
|
SDL_GPUTexture *texture = SDL_CreateGPUTexture(device, &info);
|
|
122
260
|
if (texture == nullptr) {
|
|
123
261
|
fprintf(stderr, "xt_gui: image texture creation failed: %s\n", SDL_GetError());
|
|
124
|
-
g_image_textures.emplace(
|
|
262
|
+
g_image_textures.emplace(key, nullptr);
|
|
125
263
|
return nullptr;
|
|
126
264
|
}
|
|
127
265
|
|
|
128
266
|
uint32_t bytes = (uint32_t)(image->pixels.size());
|
|
129
|
-
|
|
130
|
-
memset(&transfer_info, 0, sizeof(transfer_info));
|
|
131
|
-
transfer_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
|
|
132
|
-
transfer_info.size = bytes;
|
|
133
|
-
SDL_GPUTransferBuffer *transfer = SDL_CreateGPUTransferBuffer(device, &transfer_info);
|
|
134
|
-
if (transfer == nullptr) {
|
|
267
|
+
if (!uploadPixels(device, texture, image->width, image->height, image->pixels.data(), bytes)) {
|
|
135
268
|
SDL_ReleaseGPUTexture(device, texture);
|
|
136
|
-
g_image_textures.emplace(
|
|
269
|
+
g_image_textures.emplace(key, nullptr);
|
|
137
270
|
return nullptr;
|
|
138
271
|
}
|
|
139
|
-
void *mapped = SDL_MapGPUTransferBuffer(device, transfer, false);
|
|
140
|
-
std::memcpy(mapped, image->pixels.data(), bytes);
|
|
141
|
-
SDL_UnmapGPUTransferBuffer(device, transfer);
|
|
142
272
|
|
|
143
|
-
|
|
144
|
-
if (cmd == nullptr) {
|
|
145
|
-
SDL_ReleaseGPUTransferBuffer(device, transfer);
|
|
146
|
-
SDL_ReleaseGPUTexture(device, texture);
|
|
147
|
-
g_image_textures.emplace(src, nullptr);
|
|
148
|
-
return nullptr;
|
|
149
|
-
}
|
|
150
|
-
SDL_GPUTextureTransferInfo source;
|
|
151
|
-
memset(&source, 0, sizeof(source));
|
|
152
|
-
source.transfer_buffer = transfer;
|
|
153
|
-
source.offset = 0;
|
|
154
|
-
source.pixels_per_row = (Uint32)image->width;
|
|
155
|
-
source.rows_per_layer = (Uint32)image->height;
|
|
156
|
-
SDL_GPUTextureRegion destination;
|
|
157
|
-
memset(&destination, 0, sizeof(destination));
|
|
158
|
-
destination.texture = texture;
|
|
159
|
-
destination.mip_level = 0;
|
|
160
|
-
destination.layer = 0;
|
|
161
|
-
destination.x = 0;
|
|
162
|
-
destination.y = 0;
|
|
163
|
-
destination.z = 0;
|
|
164
|
-
destination.w = (Uint32)image->width;
|
|
165
|
-
destination.h = (Uint32)image->height;
|
|
166
|
-
destination.d = 1;
|
|
167
|
-
SDL_GPUCopyPass *copy = SDL_BeginGPUCopyPass(cmd);
|
|
168
|
-
SDL_UploadToGPUTexture(copy, &source, &destination, false);
|
|
169
|
-
SDL_EndGPUCopyPass(copy);
|
|
170
|
-
SDL_SubmitGPUCommandBuffer(cmd);
|
|
171
|
-
SDL_ReleaseGPUTransferBuffer(device, transfer);
|
|
172
|
-
|
|
173
|
-
g_image_textures.emplace(src, texture);
|
|
273
|
+
g_image_textures.emplace(key, texture);
|
|
174
274
|
return texture;
|
|
175
275
|
}
|
|
176
276
|
|
|
@@ -178,13 +278,15 @@ SDL_GPUTexture *ensureImageTexture(SDL_GPUDevice *device, const std::string &src
|
|
|
178
278
|
} // namespace xtgui
|
|
179
279
|
|
|
180
280
|
void xt_gui_image_upload(SDL_GPUDevice *device, XtGuiGeometry *geometry,
|
|
181
|
-
const xtgui::DisplayList &list) {
|
|
281
|
+
const xtgui::DisplayList &list, float scale) {
|
|
182
282
|
using namespace xtgui::renderer_detail;
|
|
183
|
-
|
|
283
|
+
geometry->op_counts.assign(list.images.size(), 0);
|
|
284
|
+
geometry->op_textures.assign(list.images.size(), nullptr);
|
|
184
285
|
if (g_image_pipeline == nullptr) {
|
|
185
286
|
geometry->vertex_count = 0;
|
|
186
287
|
return;
|
|
187
288
|
}
|
|
289
|
+
if (scale <= 0.0f) scale = 1.0f;
|
|
188
290
|
std::vector<TextVertex> verts;
|
|
189
291
|
verts.reserve(list.images.size() * 6);
|
|
190
292
|
xtgui::Color white{1.0f, 1.0f, 1.0f, 1.0f};
|
|
@@ -193,45 +295,70 @@ void xt_gui_image_upload(SDL_GPUDevice *device, XtGuiGeometry *geometry,
|
|
|
193
295
|
full.v0 = 0.0f;
|
|
194
296
|
full.u1 = 1.0f;
|
|
195
297
|
full.v1 = 1.0f;
|
|
196
|
-
for (
|
|
298
|
+
for (size_t index = 0; index < list.images.size(); index++) {
|
|
299
|
+
const xtgui::PaintImage &image = list.images[index];
|
|
197
300
|
if (image.width <= 0.0f || image.height <= 0.0f) continue;
|
|
198
|
-
SDL_GPUTexture *texture =
|
|
301
|
+
SDL_GPUTexture *texture = nullptr;
|
|
302
|
+
if (image.canvas_id != 0) {
|
|
303
|
+
texture = ensureCanvasTexture(device, image.canvas_id);
|
|
304
|
+
} else if (image.media_id != 0) {
|
|
305
|
+
texture = ensureMediaTexture(device, image.media_id);
|
|
306
|
+
} else {
|
|
307
|
+
/* Draw-time device pixels: an SVG is rasterised at this size so it stays
|
|
308
|
+
* crisp on HiDPI and when scaled. */
|
|
309
|
+
int pixel_width = (int)(image.width * scale + 0.5f);
|
|
310
|
+
int pixel_height = (int)(image.height * scale + 0.5f);
|
|
311
|
+
if (pixel_width < 1) pixel_width = 1;
|
|
312
|
+
if (pixel_height < 1) pixel_height = 1;
|
|
313
|
+
texture = ensureImageTexture(device, image.src, pixel_width, pixel_height);
|
|
314
|
+
}
|
|
199
315
|
if (texture == nullptr) continue;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
316
|
+
pushGlyph(verts, image.x, image.y, image.width, image.height, full, white,
|
|
317
|
+
image.transform);
|
|
318
|
+
geometry->op_counts[index] = 6;
|
|
319
|
+
geometry->op_textures[index] = texture;
|
|
203
320
|
}
|
|
204
321
|
uploadVertices(device, geometry, verts.data(), (uint32_t)verts.size(), sizeof(TextVertex));
|
|
205
322
|
}
|
|
206
323
|
|
|
207
|
-
void xt_gui_image_draw(SDL_GPUCommandBuffer *cmd, SDL_GPURenderPass *pass, XtGuiGeometry *geometry,
|
|
208
|
-
float viewport_width, float viewport_height) {
|
|
209
|
-
using namespace xtgui::renderer_detail;
|
|
210
|
-
if (g_image_pipeline == nullptr || g_sampler == nullptr) return;
|
|
211
|
-
if (geometry->buffer == nullptr || geometry->vertex_count == 0 || g_image_batches.empty()) return;
|
|
212
|
-
if (viewport_width <= 0.0f || viewport_height <= 0.0f) return;
|
|
213
|
-
SDL_BindGPUGraphicsPipeline(pass, g_image_pipeline);
|
|
214
|
-
SDL_GPUBufferBinding binding;
|
|
215
|
-
binding.buffer = geometry->buffer;
|
|
216
|
-
binding.offset = 0;
|
|
217
|
-
SDL_BindGPUVertexBuffers(pass, 0, &binding, 1);
|
|
218
|
-
float viewport[2] = {viewport_width, viewport_height};
|
|
219
|
-
SDL_PushGPUVertexUniformData(cmd, 0, viewport, sizeof(viewport));
|
|
220
|
-
for (const ImageBatch &batch : g_image_batches) {
|
|
221
|
-
if (batch.texture == nullptr) continue;
|
|
222
|
-
SDL_GPUTextureSamplerBinding texture_binding;
|
|
223
|
-
texture_binding.texture = batch.texture;
|
|
224
|
-
texture_binding.sampler = g_sampler;
|
|
225
|
-
SDL_BindGPUFragmentSamplers(pass, 0, &texture_binding, 1);
|
|
226
|
-
SDL_DrawGPUPrimitives(pass, batch.count, 1, batch.first, 0);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
324
|
void xt_gui_image_textures_destroy(SDL_GPUDevice *device) {
|
|
231
325
|
using namespace xtgui::renderer_detail;
|
|
232
326
|
for (auto &entry : g_image_textures) {
|
|
233
327
|
if (entry.second != nullptr) SDL_ReleaseGPUTexture(device, entry.second);
|
|
234
328
|
}
|
|
235
329
|
g_image_textures.clear();
|
|
236
|
-
|
|
330
|
+
for (auto &entry : g_media_textures) {
|
|
331
|
+
if (entry.second.texture != nullptr) SDL_ReleaseGPUTexture(device, entry.second.texture);
|
|
332
|
+
}
|
|
333
|
+
g_media_textures.clear();
|
|
334
|
+
for (auto &entry : g_canvas_textures) {
|
|
335
|
+
if (entry.second.texture != nullptr) SDL_ReleaseGPUTexture(device, entry.second.texture);
|
|
336
|
+
}
|
|
337
|
+
g_canvas_textures.clear();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
void xt_gui_media_upload_frames(SDL_GPUDevice *device) {
|
|
341
|
+
using namespace xtgui::renderer_detail;
|
|
342
|
+
for (auto &entry : g_media_textures) ensureMediaTexture(device, entry.first);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
void xt_gui_media_texture_release(SDL_GPUDevice *device, uint64_t media_id) {
|
|
346
|
+
using namespace xtgui::renderer_detail;
|
|
347
|
+
auto it = g_media_textures.find(media_id);
|
|
348
|
+
if (it == g_media_textures.end()) return;
|
|
349
|
+
if (it->second.texture != nullptr) SDL_ReleaseGPUTexture(device, it->second.texture);
|
|
350
|
+
g_media_textures.erase(it);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
void xt_gui_canvas_upload_frames(SDL_GPUDevice *device) {
|
|
354
|
+
using namespace xtgui::renderer_detail;
|
|
355
|
+
for (auto &entry : g_canvas_textures) ensureCanvasTexture(device, entry.first);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
void xt_gui_canvas_texture_release(SDL_GPUDevice *device, uint64_t canvas_id) {
|
|
359
|
+
using namespace xtgui::renderer_detail;
|
|
360
|
+
auto it = g_canvas_textures.find(canvas_id);
|
|
361
|
+
if (it == g_canvas_textures.end()) return;
|
|
362
|
+
if (it->second.texture != nullptr) SDL_ReleaseGPUTexture(device, it->second.texture);
|
|
363
|
+
g_canvas_textures.erase(it);
|
|
237
364
|
}
|
|
@@ -35,19 +35,12 @@ struct TextVertex {
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
/* Image quads are batched by texture; textures are cached by source path. */
|
|
38
|
-
struct ImageBatch {
|
|
39
|
-
SDL_GPUTexture *texture = nullptr;
|
|
40
|
-
uint32_t first = 0;
|
|
41
|
-
uint32_t count = 0;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
38
|
extern SDL_GPUGraphicsPipeline *g_pipeline;
|
|
45
39
|
extern SDL_GPUGraphicsPipeline *g_text_pipeline;
|
|
46
40
|
extern SDL_GPUGraphicsPipeline *g_image_pipeline;
|
|
47
41
|
extern SDL_GPUSampler *g_sampler;
|
|
48
42
|
extern int g_renderer_failed;
|
|
49
43
|
extern bool g_text_ok;
|
|
50
|
-
extern std::vector<ImageBatch> g_image_batches;
|
|
51
44
|
extern std::unordered_map<std::string, SDL_GPUTexture *> g_image_textures;
|
|
52
45
|
|
|
53
46
|
/* -- glyph atlas ---------------------------------------------------------- */
|
|
@@ -88,7 +81,12 @@ extern bool g_atlas_full_logged;
|
|
|
88
81
|
|
|
89
82
|
void pushRect(std::vector<Vertex> &verts, const xtgui::PaintRect &rect);
|
|
90
83
|
void pushGlyph(std::vector<TextVertex> &verts, float x, float y, float width, float height,
|
|
91
|
-
const GlyphEntry &entry, const xtgui::Color &color
|
|
84
|
+
const GlyphEntry &entry, const xtgui::Color &color,
|
|
85
|
+
const xtgui::Matrix2D &transform);
|
|
86
|
+
/** Push a textured quad, applying a `transform` matrix to its four corners. */
|
|
87
|
+
void pushQuad(std::vector<TextVertex> &verts, const float corners_x[4], const float corners_y[4],
|
|
88
|
+
const float u[4], const float v[4], const xtgui::Color &color,
|
|
89
|
+
const xtgui::Matrix2D &transform);
|
|
92
90
|
void uploadVertices(SDL_GPUDevice *device, XtGuiGeometry *geometry, const void *data,
|
|
93
91
|
uint32_t count, uint32_t stride);
|
|
94
92
|
|
|
@@ -104,7 +102,12 @@ SDL_GPUSampler *createSampler(SDL_GPUDevice *device);
|
|
|
104
102
|
/* -- images --------------------------------------------------------------- */
|
|
105
103
|
|
|
106
104
|
SDL_GPUGraphicsPipeline *createImagePipeline(SDL_GPUDevice *device, SDL_Window *reference);
|
|
107
|
-
|
|
105
|
+
/** Decode `src` (cached) and upload it as an RGBA texture. `pixel_width` /
|
|
106
|
+
* `pixel_height` are the drawn size in device pixels; vector sources (SVG) are
|
|
107
|
+
* rasterised at that size, raster sources ignore it. The texture cache is keyed
|
|
108
|
+
* by the resulting pixel size, so an SVG drawn at two sizes gets two textures. */
|
|
109
|
+
SDL_GPUTexture *ensureImageTexture(SDL_GPUDevice *device, const std::string &src, int pixel_width,
|
|
110
|
+
int pixel_height);
|
|
108
111
|
|
|
109
112
|
} // namespace renderer_detail
|
|
110
113
|
} // namespace xtgui
|
|
@@ -17,16 +17,17 @@
|
|
|
17
17
|
namespace xtgui {
|
|
18
18
|
namespace renderer_detail {
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
/** Bounds for the atlas raster of a transformed glyph: below 1 the text is
|
|
21
|
+
* already shrinking, above 4 the atlas cost outweighs the sharpness. */
|
|
22
|
+
constexpr float kMinGlyphScale = 0.5f;
|
|
23
|
+
constexpr float kMaxGlyphScale = 4.0f;
|
|
24
|
+
|
|
25
|
+
void pushQuad(std::vector<TextVertex> &verts, const float corners_x[4], const float corners_y[4],
|
|
26
|
+
const float u[4], const float v[4], const xtgui::Color &color,
|
|
27
|
+
const xtgui::Matrix2D &transform) {
|
|
26
28
|
TextVertex corners[4];
|
|
27
29
|
for (int i = 0; i < 4; i++) {
|
|
28
|
-
corners[i].x
|
|
29
|
-
corners[i].y = corners_y[i];
|
|
30
|
+
transform.apply(corners_x[i], corners_y[i], &corners[i].x, &corners[i].y);
|
|
30
31
|
corners[i].u = u[i];
|
|
31
32
|
corners[i].v = v[i];
|
|
32
33
|
corners[i].r = color.r;
|
|
@@ -38,6 +39,16 @@ void pushGlyph(std::vector<TextVertex> &verts, float x, float y, float width, fl
|
|
|
38
39
|
for (int i = 0; i < 6; i++) verts.push_back(corners[order[i]]);
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
void pushGlyph(std::vector<TextVertex> &verts, float x, float y, float width, float height,
|
|
43
|
+
const GlyphEntry &entry, const xtgui::Color &color,
|
|
44
|
+
const xtgui::Matrix2D &transform) {
|
|
45
|
+
const float corners_x[4] = {x, x + width, x + width, x};
|
|
46
|
+
const float corners_y[4] = {y, y, y + height, y + height};
|
|
47
|
+
const float u[4] = {entry.u0, entry.u1, entry.u1, entry.u0};
|
|
48
|
+
const float v[4] = {entry.v0, entry.v0, entry.v1, entry.v1};
|
|
49
|
+
pushQuad(verts, corners_x, corners_y, u, v, color, transform);
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
bool ensureAtlasTexture(SDL_GPUDevice *device) {
|
|
42
53
|
if (g_atlas_texture != nullptr) return true;
|
|
43
54
|
SDL_GPUTextureCreateInfo info;
|
|
@@ -295,52 +306,48 @@ void xt_gui_text_upload(SDL_GPUDevice *device, XtGuiGeometry *geometry,
|
|
|
295
306
|
if (scale <= 0.0f) scale = 1.0f;
|
|
296
307
|
|
|
297
308
|
std::vector<TextVertex> verts;
|
|
298
|
-
|
|
309
|
+
geometry->op_counts.assign(list.texts.size(), 0);
|
|
310
|
+
for (size_t index = 0; index < list.texts.size(); index++) {
|
|
311
|
+
const xtgui::PaintText &run = list.texts[index];
|
|
299
312
|
if (run.text.empty() || run.color.a <= 0.0f) continue;
|
|
300
313
|
xtgui::Font *logical = xtgui::xt_text_resolve(run.font);
|
|
301
314
|
if (logical == nullptr) continue;
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
315
|
+
|
|
316
|
+
/* A scaling `transform` magnifies the glyph quads on the GPU, so the atlas
|
|
317
|
+
has to hold a raster of that size or the text looks like a scaled bitmap:
|
|
318
|
+
rasterise at the device scale times the transform's magnification, and
|
|
319
|
+
clamp it — the atlas is finite, and a 40x transform would evict every
|
|
320
|
+
other glyph (a magnified bitmap is the right trade at that point). */
|
|
321
|
+
float glyph_scale = scale * run.transform.magnification();
|
|
322
|
+
if (glyph_scale < kMinGlyphScale) glyph_scale = kMinGlyphScale;
|
|
323
|
+
if (glyph_scale > kMaxGlyphScale) glyph_scale = kMaxGlyphScale;
|
|
306
324
|
|
|
307
325
|
std::vector<xtgui::ShapedGlyph> glyphs;
|
|
308
326
|
xtgui::xt_text_shape_run(run.text, logical, glyphs);
|
|
309
327
|
float pen_x = run.x;
|
|
328
|
+
uint32_t before = (uint32_t)verts.size();
|
|
310
329
|
for (const xtgui::ShapedGlyph &glyph : glyphs) {
|
|
311
330
|
if (glyph.glyph != 0) {
|
|
331
|
+
xtgui::Font *face = glyph.font != nullptr ? glyph.font : logical;
|
|
332
|
+
if (glyph_scale != 1.0f) {
|
|
333
|
+
xtgui::Font *scaled = xtgui::xt_text_scale(face, run.font.pixel_size * glyph_scale);
|
|
334
|
+
if (scaled != nullptr) face = scaled;
|
|
335
|
+
}
|
|
312
336
|
GlyphEntry entry;
|
|
313
|
-
if (ensureGlyph(device,
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
float
|
|
317
|
-
float
|
|
318
|
-
|
|
337
|
+
if (ensureGlyph(device, face, glyph.glyph, entry)) {
|
|
338
|
+
/* Entry metrics are in raster pixels; divide them back into logical
|
|
339
|
+
units — the transform (not the atlas) does the final scaling. */
|
|
340
|
+
float quad_x = pen_x + glyph.x_offset + (float)entry.left / glyph_scale;
|
|
341
|
+
float quad_y = run.baseline + glyph.y_offset - (float)entry.top / glyph_scale;
|
|
342
|
+
float quad_w = (float)entry.width / glyph_scale;
|
|
343
|
+
float quad_h = (float)entry.height / glyph_scale;
|
|
344
|
+
pushGlyph(verts, quad_x, quad_y, quad_w, quad_h, entry, run.color, run.transform);
|
|
319
345
|
}
|
|
320
346
|
}
|
|
321
347
|
pen_x += glyph.x_advance;
|
|
322
348
|
}
|
|
349
|
+
geometry->op_counts[index] = (uint32_t)verts.size() - before;
|
|
323
350
|
}
|
|
324
351
|
uploadVertices(device, geometry, verts.data(), (uint32_t)verts.size(), sizeof(TextVertex));
|
|
325
352
|
flushAtlas(device);
|
|
326
353
|
}
|
|
327
|
-
|
|
328
|
-
void xt_gui_text_draw(SDL_GPUCommandBuffer *cmd, SDL_GPURenderPass *pass, XtGuiGeometry *geometry,
|
|
329
|
-
float viewport_width, float viewport_height) {
|
|
330
|
-
using namespace xtgui::renderer_detail;
|
|
331
|
-
if (!g_text_ok || geometry->buffer == nullptr || geometry->vertex_count == 0) return;
|
|
332
|
-
if (g_atlas_texture == nullptr) return;
|
|
333
|
-
if (viewport_width <= 0.0f || viewport_height <= 0.0f) return;
|
|
334
|
-
SDL_BindGPUGraphicsPipeline(pass, g_text_pipeline);
|
|
335
|
-
SDL_GPUBufferBinding binding;
|
|
336
|
-
binding.buffer = geometry->buffer;
|
|
337
|
-
binding.offset = 0;
|
|
338
|
-
SDL_BindGPUVertexBuffers(pass, 0, &binding, 1);
|
|
339
|
-
SDL_GPUTextureSamplerBinding atlas_binding;
|
|
340
|
-
atlas_binding.texture = g_atlas_texture;
|
|
341
|
-
atlas_binding.sampler = g_sampler;
|
|
342
|
-
SDL_BindGPUFragmentSamplers(pass, 0, &atlas_binding, 1);
|
|
343
|
-
float viewport[2] = {viewport_width, viewport_height};
|
|
344
|
-
SDL_PushGPUVertexUniformData(cmd, 0, viewport, sizeof(viewport));
|
|
345
|
-
SDL_DrawGPUPrimitives(pass, geometry->vertex_count, 1, 0, 0);
|
|
346
|
-
}
|
package/runtime/ext_gui/style.h
CHANGED
|
@@ -16,14 +16,19 @@
|
|
|
16
16
|
|
|
17
17
|
#include "css.h"
|
|
18
18
|
#include "dom.h"
|
|
19
|
+
#include "transform.h"
|
|
19
20
|
|
|
20
21
|
namespace xtgui {
|
|
21
22
|
|
|
22
23
|
enum class Display { None, Block, Inline, InlineBlock, Flex, ListItem };
|
|
23
24
|
enum class Position { Static, Relative, Absolute, Fixed };
|
|
24
25
|
enum class FlexDirection { Row, RowReverse, Column, ColumnReverse };
|
|
26
|
+
enum class FlexWrap { NoWrap, Wrap, WrapReverse };
|
|
25
27
|
enum class JustifyContent { FlexStart, FlexEnd, Center, SpaceBetween, SpaceAround, SpaceEvenly };
|
|
26
28
|
enum class AlignItems { Stretch, FlexStart, FlexEnd, Center, Baseline };
|
|
29
|
+
enum class AlignContent { Stretch, FlexStart, FlexEnd, Center, SpaceBetween, SpaceAround };
|
|
30
|
+
enum class FloatSide { None, Left, Right };
|
|
31
|
+
enum class Clear { None, Left, Right, Both };
|
|
27
32
|
enum class TextAlign { Start, Center, End, Justify };
|
|
28
33
|
enum class WhiteSpace { Normal, NoWrap, Pre, PreWrap, PreLine };
|
|
29
34
|
enum class Overflow { Visible, Hidden, Scroll, Auto };
|
|
@@ -84,9 +89,13 @@ struct XtStyle {
|
|
|
84
89
|
WhiteSpace white_space = WhiteSpace::Normal; // inherited
|
|
85
90
|
|
|
86
91
|
FlexDirection flex_direction = FlexDirection::Row;
|
|
92
|
+
FlexWrap flex_wrap = FlexWrap::NoWrap;
|
|
87
93
|
JustifyContent justify_content = JustifyContent::FlexStart;
|
|
88
94
|
AlignItems align_items = AlignItems::Stretch;
|
|
95
|
+
AlignContent align_content = AlignContent::Stretch;
|
|
89
96
|
Length gap;
|
|
97
|
+
Length row_gap;
|
|
98
|
+
Length column_gap;
|
|
90
99
|
float flex_grow = 0.0f;
|
|
91
100
|
float flex_shrink = 1.0f;
|
|
92
101
|
Length flex_basis;
|
|
@@ -96,6 +105,15 @@ struct XtStyle {
|
|
|
96
105
|
int z_index = 0;
|
|
97
106
|
bool has_z_index = false;
|
|
98
107
|
|
|
108
|
+
FloatSide float_side = FloatSide::None;
|
|
109
|
+
Clear clear = Clear::None;
|
|
110
|
+
|
|
111
|
+
/** `transform` (not inherited) and `transform-origin` (defaults to the centre
|
|
112
|
+
* of the border box). Layout resolves the list into one matrix per box. */
|
|
113
|
+
std::vector<TransformOp> transform;
|
|
114
|
+
Length transform_origin_x{50.0f, Unit::Percent};
|
|
115
|
+
Length transform_origin_y{50.0f, Unit::Percent};
|
|
116
|
+
|
|
99
117
|
/** `transition` specs; not inherited. Applied by `XtDocument`. */
|
|
100
118
|
std::vector<TransitionSpec> transitions;
|
|
101
119
|
};
|
|
@@ -112,6 +130,9 @@ void xt_style_compute(const Node *root, const StyleSheet &sheet,
|
|
|
112
130
|
/** Format a computed property for diagnostics/tests (`computedStyle(sel, prop)`). */
|
|
113
131
|
std::string xt_style_property_to_string(const XtStyle &style, const std::string &property);
|
|
114
132
|
|
|
133
|
+
/** Format a length the way `computedStyle` does (`4px`, `50%`, `1.5`). */
|
|
134
|
+
std::string xt_style_length_to_string(const Length &length);
|
|
135
|
+
|
|
115
136
|
} // namespace xtgui
|
|
116
137
|
|
|
117
138
|
#endif /* XT_GUI_STYLE_H */
|
|
@@ -61,9 +61,14 @@ std::string xt_style_property_to_string(const XtStyle &style, const std::string
|
|
|
61
61
|
static const char *const displayNames[] = {"none", "block", "inline", "inline-block", "flex", "list-item"};
|
|
62
62
|
static const char *const positionNames[] = {"static", "relative", "absolute", "fixed"};
|
|
63
63
|
static const char *const flexNames[] = {"row", "row-reverse", "column", "column-reverse"};
|
|
64
|
+
static const char *const wrapNames[] = {"nowrap", "wrap", "wrap-reverse"};
|
|
64
65
|
static const char *const justifyNames[] = {"flex-start", "flex-end", "center", "space-between",
|
|
65
66
|
"space-around", "space-evenly"};
|
|
66
67
|
static const char *const alignNames[] = {"stretch", "flex-start", "flex-end", "center", "baseline"};
|
|
68
|
+
static const char *const alignContentNames[] = {"stretch", "flex-start", "flex-end", "center",
|
|
69
|
+
"space-between", "space-around"};
|
|
70
|
+
static const char *const floatNames[] = {"none", "left", "right"};
|
|
71
|
+
static const char *const clearNames[] = {"none", "left", "right", "both"};
|
|
67
72
|
static const char *const alignTextNames[] = {"start", "center", "end", "justify"};
|
|
68
73
|
|
|
69
74
|
if (property == "display") return displayNames[(int)style.display];
|
|
@@ -98,16 +103,28 @@ std::string xt_style_property_to_string(const XtStyle &style, const std::string
|
|
|
98
103
|
}
|
|
99
104
|
if (property == "text-align") return alignTextNames[(int)style.text_align];
|
|
100
105
|
if (property == "flex-direction") return flexNames[(int)style.flex_direction];
|
|
106
|
+
if (property == "flex-wrap") return wrapNames[(int)style.flex_wrap];
|
|
101
107
|
if (property == "justify-content") return justifyNames[(int)style.justify_content];
|
|
102
108
|
if (property == "align-items") return alignNames[(int)style.align_items];
|
|
109
|
+
if (property == "align-content") return alignContentNames[(int)style.align_content];
|
|
103
110
|
if (property == "gap") return formatLength(style.gap);
|
|
111
|
+
if (property == "row-gap") return formatLength(style.row_gap);
|
|
112
|
+
if (property == "column-gap") return formatLength(style.column_gap);
|
|
113
|
+
if (property == "float") return floatNames[(int)style.float_side];
|
|
114
|
+
if (property == "clear") return clearNames[(int)style.clear];
|
|
104
115
|
if (property == "flex-grow") return formatNumber(style.flex_grow);
|
|
105
116
|
if (property == "flex-shrink") return formatNumber(style.flex_shrink);
|
|
106
117
|
if (property == "flex-basis") return formatLength(style.flex_basis);
|
|
107
118
|
if (property == "border-radius") return formatNumber(style.border_radius) + "px";
|
|
108
119
|
if (property == "opacity") return formatNumber(style.opacity);
|
|
109
120
|
if (property == "z-index") return style.has_z_index ? formatNumber((float)style.z_index) : "auto";
|
|
121
|
+
if (property == "transform") return xt_transform_to_string(style.transform);
|
|
122
|
+
if (property == "transform-origin") {
|
|
123
|
+
return formatLength(style.transform_origin_x) + " " + formatLength(style.transform_origin_y);
|
|
124
|
+
}
|
|
110
125
|
return "";
|
|
111
126
|
}
|
|
112
127
|
|
|
128
|
+
std::string xt_style_length_to_string(const Length &length) { return formatLength(length); }
|
|
129
|
+
|
|
113
130
|
} // namespace xtgui
|
|
@@ -40,6 +40,13 @@ void xt_style_apply_user_agent(const Node *node, XtStyle *style) {
|
|
|
40
40
|
style->display = Display::InlineBlock;
|
|
41
41
|
}
|
|
42
42
|
if (tag == "img") style->display = Display::InlineBlock;
|
|
43
|
+
/* `<video>` mirrors `<img>` (a replaced inline-block; 300x150 by default from
|
|
44
|
+
* layout), while `<audio>` and its `<source>`/`<track>` children have no
|
|
45
|
+
* rendered box of their own. */
|
|
46
|
+
if (tag == "video") style->display = Display::InlineBlock;
|
|
47
|
+
/* `<canvas>` is also a replaced inline-block (300x150 default in layout). */
|
|
48
|
+
if (tag == "canvas") style->display = Display::InlineBlock;
|
|
49
|
+
if (tag == "audio" || tag == "source" || tag == "track") style->display = Display::None;
|
|
43
50
|
if (tag == "body") style->margin.set({8.0f, Unit::Px});
|
|
44
51
|
if (tag == "p") style->margin.top = style->margin.bottom = {16.0f, Unit::Px};
|
|
45
52
|
if (tag == "blockquote") {
|