@effindomv2/fui-rs 0.1.4 → 0.1.6
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/Cargo.toml +1 -1
- package/README.md +1 -0
- package/package.json +2 -2
- package/scripts/build.sh +7 -2
- package/src/app.rs +2 -2
- package/src/controls/button.rs +45 -2
- package/src/controls/checkbox.rs +14 -0
- package/src/controls/context_menu.rs +2 -2
- package/src/controls/dialog.rs +2 -2
- package/src/controls/internal/dropdown_field_presenter.rs +1 -2
- package/src/controls/internal/dropdown_option_row_presenter.rs +1 -2
- package/src/controls/internal/pressable_labeled_control.rs +31 -2
- package/src/controls/internal/text_input_core.rs +2 -2
- package/src/controls/mod.rs +24 -0
- package/src/controls/nav_link.rs +43 -10
- package/src/controls/radio_button.rs +14 -0
- package/src/controls/switch.rs +14 -0
- package/src/lib.rs +39 -13
- package/src/mobile_text_selection_toolbar.rs +1 -1
- package/src/node/core.rs +16 -2
- package/src/node/mod.rs +21 -1
- package/src/node/text_node.rs +31 -7
package/Cargo.toml
CHANGED
package/README.md
CHANGED
|
@@ -62,6 +62,7 @@ fui_app!(FlexBox, build_page);
|
|
|
62
62
|
|---|---|
|
|
63
63
|
| Retained app lifecycle macros | Available |
|
|
64
64
|
| `ui!` mixed child tree macro | Available |
|
|
65
|
+
| `fui_component!` retained component delegation | Available |
|
|
65
66
|
| Flex/Grid layout nodes | Available |
|
|
66
67
|
| Text, rich text, image, SVG | Available |
|
|
67
68
|
| Buttons, toggles, slider, dropdown, combobox | Available |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effindomv2/fui-rs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "AGPL-3.0-only OR LicenseRef-EffinDom-Commercial",
|
|
6
6
|
"description": "EffinDom v2 Rust frontend framework SDK and host generators",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"prepublishOnly": "npm run check:abi && npm run lint && npm run typecheck"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@effindomv2/runtime": "0.1.
|
|
42
|
+
"@effindomv2/runtime": "0.1.26"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@playwright/test": "1.59.1",
|
package/scripts/build.sh
CHANGED
|
@@ -196,9 +196,14 @@ if [ -f "${SHARED_DEMO_TEXTURE}" ]; then
|
|
|
196
196
|
cp "${SHARED_DEMO_TEXTURE}" "${DEMO_OUT_DIR}/stage4/demo-texture.png"
|
|
197
197
|
cp "${SHARED_DEMO_TEXTURE}" "${DEMO_OUT_DIR}/stage5/demo-texture.png"
|
|
198
198
|
fi
|
|
199
|
-
|
|
199
|
+
RUNTIME_SET_HASH="$(node -e 'const fs=require("fs"); const value=JSON.parse(fs.readFileSync(process.argv[1], "utf8")).runtime_set_hash; if (typeof value !== "string" || value.length === 0) process.exit(1); process.stdout.write(value);' "${RUNTIME_DIST_DIR}/effindom.v2.manifest.json")"
|
|
200
|
+
cat > "${OUT_DIR}/effindom-runtime-config.js" << RUNTIMECFG
|
|
200
201
|
window.__effindomRuntime = Object.assign({}, window.__effindomRuntime, {
|
|
201
|
-
|
|
202
|
+
manifestUrls: [
|
|
203
|
+
'https://runtimes.effindom.dev/v2/manifests/${RUNTIME_SET_HASH}.json',
|
|
204
|
+
'./effindom.v2.manifest.json',
|
|
205
|
+
],
|
|
206
|
+
expectedRuntimeSetHash: '${RUNTIME_SET_HASH}',
|
|
202
207
|
});
|
|
203
208
|
RUNTIMECFG
|
|
204
209
|
cp "${OUT_DIR}/effindom-runtime-config.js" "${DEMO_OUT_DIR}/effindom-runtime-config.js"
|
package/src/app.rs
CHANGED
|
@@ -109,7 +109,7 @@ impl ApplicationRegistration {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
pub fn page<TNode: Node>(mut self, build_page: impl Fn() -> TNode + 'static) -> Self {
|
|
112
|
+
pub fn page<TNode: Node + 'static>(mut self, build_page: impl Fn() -> TNode + 'static) -> Self {
|
|
113
113
|
self.build_page_fn = Rc::new(move || {
|
|
114
114
|
let node = build_page();
|
|
115
115
|
let shell = flex_box();
|
|
@@ -133,7 +133,7 @@ pub struct ManagedApplication<TPage: 'static> {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
impl<TPage: 'static> ManagedApplication<TPage> {
|
|
136
|
-
pub fn new<TNode: Node>(
|
|
136
|
+
pub fn new<TNode: Node + 'static>(
|
|
137
137
|
build_page: impl Fn() -> TPage + 'static,
|
|
138
138
|
get_root: impl Fn(&TPage) -> TNode + 'static,
|
|
139
139
|
) -> Self {
|
package/src/controls/button.rs
CHANGED
|
@@ -26,6 +26,8 @@ pub struct Button {
|
|
|
26
26
|
hover_background_override: Rc<Cell<Option<u32>>>,
|
|
27
27
|
pressed_background_override: Rc<Cell<Option<u32>>>,
|
|
28
28
|
border_override: Rc<Cell<Option<Border>>>,
|
|
29
|
+
font_family_override: Rc<RefCell<Option<crate::FontFamily>>>,
|
|
30
|
+
font_size_override: Rc<Cell<Option<f32>>>,
|
|
29
31
|
text_color_override: Rc<Cell<Option<u32>>>,
|
|
30
32
|
colors_value: Rc<Cell<Option<ButtonColors>>>,
|
|
31
33
|
theme_guard: Rc<RefCell<Option<SubscriptionGuard>>>,
|
|
@@ -66,6 +68,8 @@ impl Button {
|
|
|
66
68
|
hover_background_override: Rc::new(Cell::new(None)),
|
|
67
69
|
pressed_background_override: Rc::new(Cell::new(None)),
|
|
68
70
|
border_override: Rc::new(Cell::new(None)),
|
|
71
|
+
font_family_override: Rc::new(RefCell::new(None)),
|
|
72
|
+
font_size_override: Rc::new(Cell::new(None)),
|
|
69
73
|
text_color_override: Rc::new(Cell::new(None)),
|
|
70
74
|
colors_value: Rc::new(Cell::new(None)),
|
|
71
75
|
theme_guard: Rc::new(RefCell::new(None)),
|
|
@@ -250,10 +254,19 @@ impl Button {
|
|
|
250
254
|
self
|
|
251
255
|
}
|
|
252
256
|
|
|
253
|
-
|
|
257
|
+
fn set_explicit_text_color(&self, color: u32) {
|
|
254
258
|
self.text_color_override.set(Some(color));
|
|
255
259
|
self.sync_visual_state();
|
|
256
|
-
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
fn set_explicit_font_family(&self, family: crate::FontFamily) {
|
|
263
|
+
self.font_family_override.replace(Some(family.clone()));
|
|
264
|
+
self.label.borrow().font_family(family);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
fn set_explicit_font_size(&self, size: f32) {
|
|
268
|
+
self.font_size_override.set(Some(size));
|
|
269
|
+
self.label.borrow().font_size(size);
|
|
257
270
|
}
|
|
258
271
|
|
|
259
272
|
fn install_visual_subscriptions(&self) {
|
|
@@ -285,6 +298,8 @@ impl Button {
|
|
|
285
298
|
hover_background_override: self.hover_background_override.clone(),
|
|
286
299
|
pressed_background_override: self.pressed_background_override.clone(),
|
|
287
300
|
border_override: self.border_override.clone(),
|
|
301
|
+
font_family_override: self.font_family_override.clone(),
|
|
302
|
+
font_size_override: self.font_size_override.clone(),
|
|
288
303
|
text_color_override: self.text_color_override.clone(),
|
|
289
304
|
colors_value: self.colors_value.clone(),
|
|
290
305
|
}
|
|
@@ -328,6 +343,8 @@ impl Button {
|
|
|
328
343
|
self.hover_background_override.get(),
|
|
329
344
|
self.pressed_background_override.get(),
|
|
330
345
|
self.border_override.get(),
|
|
346
|
+
self.font_family_override.borrow().clone(),
|
|
347
|
+
self.font_size_override.get(),
|
|
331
348
|
self.text_color_override.get(),
|
|
332
349
|
self.colors_value.get(),
|
|
333
350
|
);
|
|
@@ -380,6 +397,20 @@ impl HasFlexBoxRoot for Button {
|
|
|
380
397
|
}
|
|
381
398
|
}
|
|
382
399
|
|
|
400
|
+
impl LabeledControlTextStyle for Button {
|
|
401
|
+
fn set_label_font_family(&self, family: crate::FontFamily) {
|
|
402
|
+
self.set_explicit_font_family(family);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
fn set_label_font_size(&self, size: f32) {
|
|
406
|
+
self.set_explicit_font_size(size);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
fn set_label_text_color(&self, color: u32) {
|
|
410
|
+
self.set_explicit_text_color(color);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
383
414
|
fn sync_button_visual_state(
|
|
384
415
|
root: &FlexBox,
|
|
385
416
|
presenter: &Rc<RefCell<Rc<dyn ButtonPresenter>>>,
|
|
@@ -391,6 +422,8 @@ fn sync_button_visual_state(
|
|
|
391
422
|
hover_background_override: Option<u32>,
|
|
392
423
|
pressed_background_override: Option<u32>,
|
|
393
424
|
border_override: Option<Border>,
|
|
425
|
+
font_family_override: Option<crate::FontFamily>,
|
|
426
|
+
font_size_override: Option<f32>,
|
|
394
427
|
text_color_override: Option<u32>,
|
|
395
428
|
colors: Option<ButtonColors>,
|
|
396
429
|
) {
|
|
@@ -421,6 +454,12 @@ fn sync_button_visual_state(
|
|
|
421
454
|
if let Some(border) = border_override {
|
|
422
455
|
root.border_config(border);
|
|
423
456
|
}
|
|
457
|
+
if let Some(family) = font_family_override {
|
|
458
|
+
presenter.label_node().font_family(family);
|
|
459
|
+
}
|
|
460
|
+
if let Some(size) = font_size_override {
|
|
461
|
+
presenter.label_node().font_size(size);
|
|
462
|
+
}
|
|
424
463
|
if let Some(color) = text_color_override {
|
|
425
464
|
presenter.label_node().text_color(color);
|
|
426
465
|
}
|
|
@@ -462,6 +501,8 @@ struct ButtonEventTarget {
|
|
|
462
501
|
hover_background_override: Rc<Cell<Option<u32>>>,
|
|
463
502
|
pressed_background_override: Rc<Cell<Option<u32>>>,
|
|
464
503
|
border_override: Rc<Cell<Option<Border>>>,
|
|
504
|
+
font_family_override: Rc<RefCell<Option<crate::FontFamily>>>,
|
|
505
|
+
font_size_override: Rc<Cell<Option<f32>>>,
|
|
465
506
|
text_color_override: Rc<Cell<Option<u32>>>,
|
|
466
507
|
colors_value: Rc<Cell<Option<ButtonColors>>>,
|
|
467
508
|
}
|
|
@@ -488,6 +529,8 @@ impl ButtonEventTarget {
|
|
|
488
529
|
self.hover_background_override.get(),
|
|
489
530
|
self.pressed_background_override.get(),
|
|
490
531
|
self.border_override.get(),
|
|
532
|
+
self.font_family_override.borrow().clone(),
|
|
533
|
+
self.font_size_override.get(),
|
|
491
534
|
self.text_color_override.get(),
|
|
492
535
|
self.colors_value.get(),
|
|
493
536
|
);
|
package/src/controls/checkbox.rs
CHANGED
|
@@ -263,6 +263,20 @@ impl Checkbox {
|
|
|
263
263
|
}
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
+
impl LabeledControlTextStyle for Checkbox {
|
|
267
|
+
fn set_label_font_family(&self, family: crate::FontFamily) {
|
|
268
|
+
self.base.font_family(family);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
fn set_label_font_size(&self, size: f32) {
|
|
272
|
+
self.base.font_size(size);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
fn set_label_text_color(&self, color: u32) {
|
|
276
|
+
self.base.text_color(color);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
266
280
|
impl Node for Checkbox {
|
|
267
281
|
fn retained_node_ref(&self) -> NodeRef {
|
|
268
282
|
self.root.retained_node_ref()
|
|
@@ -364,7 +364,7 @@ impl ContextMenuEntry {
|
|
|
364
364
|
)
|
|
365
365
|
.text_color(theme.context_menu.item.text_color)
|
|
366
366
|
.text_overflow(TextOverflow::Ellipsis)
|
|
367
|
-
.selectable(false
|
|
367
|
+
.selectable(false);
|
|
368
368
|
let shortcut_node = TextNode::new("");
|
|
369
369
|
shortcut_node
|
|
370
370
|
.font_family(theme.context_menu.item.font_family.clone())
|
|
@@ -376,7 +376,7 @@ impl ContextMenuEntry {
|
|
|
376
376
|
)
|
|
377
377
|
.text_color(theme.colors.text_muted)
|
|
378
378
|
.text_align(TextAlign::Left)
|
|
379
|
-
.selectable(false
|
|
379
|
+
.selectable(false);
|
|
380
380
|
|
|
381
381
|
root.width(100.0, Unit::Percent)
|
|
382
382
|
.height(theme.context_menu.item.height, Unit::Pixel)
|
package/src/controls/dialog.rs
CHANGED
|
@@ -118,11 +118,11 @@ impl Dialog {
|
|
|
118
118
|
let root = portal();
|
|
119
119
|
let title_node = TextNode::new("");
|
|
120
120
|
title_node
|
|
121
|
-
.selectable(false
|
|
121
|
+
.selectable(false)
|
|
122
122
|
.semantic_role(SemanticRole::Heading);
|
|
123
123
|
let body_node = TextNode::new("");
|
|
124
124
|
body_node
|
|
125
|
-
.selectable(false
|
|
125
|
+
.selectable(false)
|
|
126
126
|
.semantic_role(SemanticRole::StaticText);
|
|
127
127
|
let content_host = flex_box();
|
|
128
128
|
content_host
|
|
@@ -138,10 +138,9 @@ pub struct DefaultDropdownFieldPresenter {
|
|
|
138
138
|
|
|
139
139
|
impl DefaultDropdownFieldPresenter {
|
|
140
140
|
pub fn new(metrics: DropdownFieldMetrics) -> Self {
|
|
141
|
-
let theme = crate::theme::current_theme();
|
|
142
141
|
let value_node = text("");
|
|
143
142
|
value_node
|
|
144
|
-
.selectable(false
|
|
143
|
+
.selectable(false)
|
|
145
144
|
.fill_size()
|
|
146
145
|
.text_limits(0, 1)
|
|
147
146
|
.wrapping(false);
|
|
@@ -93,10 +93,9 @@ pub struct DefaultDropdownOptionRowPresenter {
|
|
|
93
93
|
|
|
94
94
|
impl DefaultDropdownOptionRowPresenter {
|
|
95
95
|
pub fn new(metrics: DropdownOptionRowMetrics) -> Self {
|
|
96
|
-
let theme = crate::theme::current_theme();
|
|
97
96
|
let label_node = text("");
|
|
98
97
|
label_node
|
|
99
|
-
.selectable(false
|
|
98
|
+
.selectable(false)
|
|
100
99
|
.fill_size()
|
|
101
100
|
.text_limits(0, 1)
|
|
102
101
|
.wrapping(false);
|
|
@@ -37,7 +37,9 @@ pub(crate) struct PressableLabeledControl {
|
|
|
37
37
|
key_pressed_state: Rc<Cell<bool>>,
|
|
38
38
|
pointer_pressed_state: Rc<Cell<bool>>,
|
|
39
39
|
enabled_state: Rc<Cell<bool>>,
|
|
40
|
+
label_font_family_override: Rc<RefCell<Option<crate::FontFamily>>>,
|
|
40
41
|
label_font_size_override: Rc<Cell<f32>>,
|
|
42
|
+
label_text_color_override: Rc<Cell<Option<u32>>>,
|
|
41
43
|
colors_value: Rc<Cell<Option<LabeledControlColors>>>,
|
|
42
44
|
activation: Rc<RefCell<Option<ActivationCallback>>>,
|
|
43
45
|
state_changed: Rc<RefCell<Option<StateCallback>>>,
|
|
@@ -78,7 +80,9 @@ impl PressableLabeledControl {
|
|
|
78
80
|
key_pressed_state: Rc::new(Cell::new(false)),
|
|
79
81
|
pointer_pressed_state: Rc::new(Cell::new(false)),
|
|
80
82
|
enabled_state: Rc::new(Cell::new(true)),
|
|
83
|
+
label_font_family_override: Rc::new(RefCell::new(None)),
|
|
81
84
|
label_font_size_override: Rc::new(Cell::new(0.0)),
|
|
85
|
+
label_text_color_override: Rc::new(Cell::new(None)),
|
|
82
86
|
colors_value: Rc::new(Cell::new(None)),
|
|
83
87
|
activation: Rc::new(RefCell::new(None)),
|
|
84
88
|
state_changed: Rc::new(RefCell::new(None)),
|
|
@@ -134,6 +138,21 @@ impl PressableLabeledControl {
|
|
|
134
138
|
self.sync_base_theme();
|
|
135
139
|
}
|
|
136
140
|
|
|
141
|
+
pub(crate) fn font_family(&self, family: crate::FontFamily) {
|
|
142
|
+
self.label_font_family_override.replace(Some(family));
|
|
143
|
+
self.sync_base_theme();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
pub(crate) fn font_size(&self, size: f32) {
|
|
147
|
+
self.label_font_size_override.set(size);
|
|
148
|
+
self.sync_base_theme();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
pub(crate) fn text_color(&self, color: u32) {
|
|
152
|
+
self.label_text_color_override.set(Some(color));
|
|
153
|
+
self.sync_base_theme();
|
|
154
|
+
}
|
|
155
|
+
|
|
137
156
|
pub(crate) fn replace_indicator_root(&self, next_root: FlexBox) {
|
|
138
157
|
if next_root
|
|
139
158
|
.retained_node_ref()
|
|
@@ -251,7 +270,9 @@ impl PressableLabeledControl {
|
|
|
251
270
|
key_pressed_state: self.key_pressed_state.clone(),
|
|
252
271
|
pointer_pressed_state: self.pointer_pressed_state.clone(),
|
|
253
272
|
enabled_state: self.enabled_state.clone(),
|
|
273
|
+
label_font_family_override: self.label_font_family_override.clone(),
|
|
254
274
|
label_font_size_override: self.label_font_size_override.clone(),
|
|
275
|
+
label_text_color_override: self.label_text_color_override.clone(),
|
|
255
276
|
colors_value: self.colors_value.clone(),
|
|
256
277
|
activation: self.activation.clone(),
|
|
257
278
|
state_changed: self.state_changed.clone(),
|
|
@@ -265,6 +286,8 @@ impl PressableLabeledControl {
|
|
|
265
286
|
&self.label_node,
|
|
266
287
|
&self.gap_node,
|
|
267
288
|
self.label_font_size_override.get(),
|
|
289
|
+
self.label_font_family_override.borrow().clone(),
|
|
290
|
+
self.label_text_color_override.get(),
|
|
268
291
|
self.colors_value.get(),
|
|
269
292
|
self.enabled_state.get(),
|
|
270
293
|
);
|
|
@@ -284,6 +307,8 @@ fn sync_base_theme_parts(
|
|
|
284
307
|
label_node: &TextCore,
|
|
285
308
|
gap_node: &FlexBox,
|
|
286
309
|
label_font_size_override: f32,
|
|
310
|
+
label_font_family_override: Option<crate::FontFamily>,
|
|
311
|
+
label_text_color_override: Option<u32>,
|
|
287
312
|
colors: Option<LabeledControlColors>,
|
|
288
313
|
enabled: bool,
|
|
289
314
|
) {
|
|
@@ -304,7 +329,7 @@ fn sync_base_theme_parts(
|
|
|
304
329
|
root.opacity(if enabled { 1.0 } else { 0.6 });
|
|
305
330
|
gap_node.width(theme.spacing.sm, Unit::Pixel);
|
|
306
331
|
label_node
|
|
307
|
-
.font_family(theme.fonts.body_family.clone())
|
|
332
|
+
.font_family(label_font_family_override.unwrap_or_else(|| theme.fonts.body_family.clone()))
|
|
308
333
|
.font_size(if label_font_size_override > 0.0 {
|
|
309
334
|
label_font_size_override
|
|
310
335
|
} else {
|
|
@@ -321,7 +346,7 @@ fn sync_base_theme_parts(
|
|
|
321
346
|
.map(|colors| colors.text_muted_color())
|
|
322
347
|
.unwrap_or(theme.colors.text_muted)
|
|
323
348
|
};
|
|
324
|
-
label_node.text_color(label_color);
|
|
349
|
+
label_node.text_color(label_text_color_override.unwrap_or(label_color));
|
|
325
350
|
}
|
|
326
351
|
|
|
327
352
|
fn sync_focus_chrome_parts(root: &FlexBox, focused: bool, enabled: bool) {
|
|
@@ -347,7 +372,9 @@ struct PressableLabeledControlEventTarget {
|
|
|
347
372
|
key_pressed_state: Rc<Cell<bool>>,
|
|
348
373
|
pointer_pressed_state: Rc<Cell<bool>>,
|
|
349
374
|
enabled_state: Rc<Cell<bool>>,
|
|
375
|
+
label_font_family_override: Rc<RefCell<Option<crate::FontFamily>>>,
|
|
350
376
|
label_font_size_override: Rc<Cell<f32>>,
|
|
377
|
+
label_text_color_override: Rc<Cell<Option<u32>>>,
|
|
351
378
|
colors_value: Rc<Cell<Option<LabeledControlColors>>>,
|
|
352
379
|
activation: Rc<RefCell<Option<ActivationCallback>>>,
|
|
353
380
|
state_changed: Rc<RefCell<Option<StateCallback>>>,
|
|
@@ -373,6 +400,8 @@ impl PressableLabeledControlEventTarget {
|
|
|
373
400
|
&self.label_node,
|
|
374
401
|
&self.gap_node,
|
|
375
402
|
self.label_font_size_override.get(),
|
|
403
|
+
self.label_font_family_override.borrow().clone(),
|
|
404
|
+
self.label_text_color_override.get(),
|
|
376
405
|
self.colors_value.get(),
|
|
377
406
|
self.enabled_state.get(),
|
|
378
407
|
);
|
|
@@ -174,7 +174,7 @@ impl TextInputCore {
|
|
|
174
174
|
.semantic_role(SemanticRole::Textbox)
|
|
175
175
|
.reflect_semantic_disabled_from_enabled()
|
|
176
176
|
.focusable(true, 0)
|
|
177
|
-
.selectable(true
|
|
177
|
+
.selectable(true)
|
|
178
178
|
.editable(true);
|
|
179
179
|
let placeholder_text = TextCore::new("");
|
|
180
180
|
let placeholder_host = flex_box();
|
|
@@ -891,7 +891,7 @@ impl TextInputCore {
|
|
|
891
891
|
}
|
|
892
892
|
|
|
893
893
|
fn sync_editor_editability(&self) {
|
|
894
|
-
self.editor_text.selectable(is_enabled(&self.root)
|
|
894
|
+
self.editor_text.selectable(is_enabled(&self.root));
|
|
895
895
|
self.editor_text
|
|
896
896
|
.editable(!self.read_only_value.get() && is_enabled(&self.root));
|
|
897
897
|
}
|
package/src/controls/mod.rs
CHANGED
|
@@ -69,6 +69,30 @@ pub struct SliderChangedEventArgs {
|
|
|
69
69
|
pub value: f32,
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
pub trait LabeledControlTextStyle: Sized {
|
|
73
|
+
#[doc(hidden)]
|
|
74
|
+
fn set_label_font_family(&self, family: crate::FontFamily);
|
|
75
|
+
#[doc(hidden)]
|
|
76
|
+
fn set_label_font_size(&self, size: f32);
|
|
77
|
+
#[doc(hidden)]
|
|
78
|
+
fn set_label_text_color(&self, color: u32);
|
|
79
|
+
|
|
80
|
+
fn font_family(&self, family: crate::FontFamily) -> &Self {
|
|
81
|
+
self.set_label_font_family(family);
|
|
82
|
+
self
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fn font_size(&self, size: f32) -> &Self {
|
|
86
|
+
self.set_label_font_size(size);
|
|
87
|
+
self
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
fn text_color(&self, color: u32) -> &Self {
|
|
91
|
+
self.set_label_text_color(color);
|
|
92
|
+
self
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
72
96
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
73
97
|
pub struct DropdownChangedEventArgs<T> {
|
|
74
98
|
pub item: T,
|
package/src/controls/nav_link.rs
CHANGED
|
@@ -42,6 +42,7 @@ pub struct NavLink {
|
|
|
42
42
|
enter_pressed: Rc<Cell<bool>>,
|
|
43
43
|
enter_pressed_open_in_new_tab: Rc<Cell<bool>>,
|
|
44
44
|
preview_pinned_for_context_menu: Rc<Cell<bool>>,
|
|
45
|
+
text_color_override: Rc<Cell<Option<u32>>>,
|
|
45
46
|
theme_guard: Rc<RefCell<Option<SubscriptionGuard>>>,
|
|
46
47
|
focus_visibility_guard: Rc<RefCell<Option<SubscriptionGuard>>>,
|
|
47
48
|
}
|
|
@@ -88,6 +89,7 @@ impl NavLink {
|
|
|
88
89
|
enter_pressed: Rc::new(Cell::new(false)),
|
|
89
90
|
enter_pressed_open_in_new_tab: Rc::new(Cell::new(false)),
|
|
90
91
|
preview_pinned_for_context_menu: Rc::new(Cell::new(false)),
|
|
92
|
+
text_color_override: Rc::new(Cell::new(None)),
|
|
91
93
|
theme_guard: Rc::new(RefCell::new(None)),
|
|
92
94
|
focus_visibility_guard: Rc::new(RefCell::new(None)),
|
|
93
95
|
};
|
|
@@ -209,7 +211,7 @@ impl NavLink {
|
|
|
209
211
|
fn event_target(&self) -> NavLinkEventTarget {
|
|
210
212
|
NavLinkEventTarget {
|
|
211
213
|
weak_root: self.root.downgrade(),
|
|
212
|
-
label: self.label.
|
|
214
|
+
label: self.label.clone(),
|
|
213
215
|
href: self.href.clone(),
|
|
214
216
|
open_in_new_tab: self.open_in_new_tab.clone(),
|
|
215
217
|
navigate: self.navigate.clone(),
|
|
@@ -220,9 +222,24 @@ impl NavLink {
|
|
|
220
222
|
enter_pressed: self.enter_pressed.clone(),
|
|
221
223
|
enter_pressed_open_in_new_tab: self.enter_pressed_open_in_new_tab.clone(),
|
|
222
224
|
preview_pinned_for_context_menu: self.preview_pinned_for_context_menu.clone(),
|
|
225
|
+
text_color_override: self.text_color_override.clone(),
|
|
223
226
|
}
|
|
224
227
|
}
|
|
225
228
|
|
|
229
|
+
fn set_explicit_font_family(&self, family: crate::FontFamily) {
|
|
230
|
+
self.label.font_family(family);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
fn set_explicit_font_size(&self, size: f32) {
|
|
234
|
+
self.label.font_size(size);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
fn set_explicit_text_color(&self, color: u32) {
|
|
238
|
+
self.text_color_override.set(Some(color));
|
|
239
|
+
self.label.text_color(color);
|
|
240
|
+
self.sync_visual_state();
|
|
241
|
+
}
|
|
242
|
+
|
|
226
243
|
pub fn href(&self) -> String {
|
|
227
244
|
self.href.borrow().clone()
|
|
228
245
|
}
|
|
@@ -281,10 +298,24 @@ impl HasFlexBoxRoot for NavLink {
|
|
|
281
298
|
}
|
|
282
299
|
}
|
|
283
300
|
|
|
301
|
+
impl LabeledControlTextStyle for NavLink {
|
|
302
|
+
fn set_label_font_family(&self, family: crate::FontFamily) {
|
|
303
|
+
self.set_explicit_font_family(family);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
fn set_label_font_size(&self, size: f32) {
|
|
307
|
+
self.set_explicit_font_size(size);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
fn set_label_text_color(&self, color: u32) {
|
|
311
|
+
self.set_explicit_text_color(color);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
284
315
|
#[derive(Clone)]
|
|
285
316
|
struct NavLinkEventTarget {
|
|
286
317
|
weak_root: WeakFlexBox,
|
|
287
|
-
label:
|
|
318
|
+
label: TextNode,
|
|
288
319
|
href: Rc<RefCell<String>>,
|
|
289
320
|
open_in_new_tab: Rc<Cell<bool>>,
|
|
290
321
|
navigate: Rc<RefCell<Option<NavigateCallback>>>,
|
|
@@ -295,6 +326,7 @@ struct NavLinkEventTarget {
|
|
|
295
326
|
enter_pressed: Rc<Cell<bool>>,
|
|
296
327
|
enter_pressed_open_in_new_tab: Rc<Cell<bool>>,
|
|
297
328
|
preview_pinned_for_context_menu: Rc<Cell<bool>>,
|
|
329
|
+
text_color_override: Rc<Cell<Option<u32>>>,
|
|
298
330
|
}
|
|
299
331
|
|
|
300
332
|
impl NavLinkEventTarget {
|
|
@@ -371,14 +403,15 @@ impl NavLinkEventTarget {
|
|
|
371
403
|
}
|
|
372
404
|
|
|
373
405
|
fn sync_visual_state(&self) {
|
|
374
|
-
let color =
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
406
|
+
let color = self.text_color_override.get().unwrap_or_else(|| {
|
|
407
|
+
if self.hovered.get() {
|
|
408
|
+
current_theme().colors.accent_hovered
|
|
409
|
+
} else {
|
|
410
|
+
current_theme().colors.accent
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
if self.label.handle() != NodeHandle::INVALID {
|
|
414
|
+
ui::set_text_color(self.label.handle().raw(), color);
|
|
382
415
|
}
|
|
383
416
|
}
|
|
384
417
|
|
|
@@ -195,6 +195,20 @@ impl RadioButton {
|
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
impl LabeledControlTextStyle for RadioButton {
|
|
199
|
+
fn set_label_font_family(&self, family: crate::FontFamily) {
|
|
200
|
+
self.base.font_family(family);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
fn set_label_font_size(&self, size: f32) {
|
|
204
|
+
self.base.font_size(size);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
fn set_label_text_color(&self, color: u32) {
|
|
208
|
+
self.base.text_color(color);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
198
212
|
impl Node for RadioButton {
|
|
199
213
|
fn retained_node_ref(&self) -> NodeRef {
|
|
200
214
|
self.root.retained_node_ref()
|
package/src/controls/switch.rs
CHANGED
|
@@ -186,6 +186,20 @@ impl Switch {
|
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
impl LabeledControlTextStyle for Switch {
|
|
190
|
+
fn set_label_font_family(&self, family: crate::FontFamily) {
|
|
191
|
+
self.base.font_family(family);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
fn set_label_font_size(&self, size: f32) {
|
|
195
|
+
self.base.font_size(size);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
fn set_label_text_color(&self, color: u32) {
|
|
199
|
+
self.base.text_color(color);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
189
203
|
impl Node for Switch {
|
|
190
204
|
fn retained_node_ref(&self) -> NodeRef {
|
|
191
205
|
self.root.retained_node_ref()
|
package/src/lib.rs
CHANGED
|
@@ -314,6 +314,31 @@ macro_rules! ui {
|
|
|
314
314
|
};
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
#[macro_export]
|
|
318
|
+
macro_rules! fui_component {
|
|
319
|
+
($component:ty => $root:ident) => {
|
|
320
|
+
impl $crate::Node for $component {
|
|
321
|
+
fn retained_node_ref(&self) -> $crate::node::NodeRef {
|
|
322
|
+
$crate::Node::retained_node_ref(&self.$root)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
fn retained_owner_attachment(&self) -> Option<std::rc::Rc<dyn std::any::Any>> {
|
|
326
|
+
$crate::Node::retained_owner_attachment(&self.$root)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
fn build_self(&self) {
|
|
330
|
+
$crate::Node::build_self(&self.$root);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
impl $crate::HasFlexBoxRoot for $component {
|
|
335
|
+
fn flex_box_root(&self) -> &$crate::FlexBox {
|
|
336
|
+
$crate::HasFlexBoxRoot::flex_box_root(&self.$root)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
317
342
|
pub mod prelude {
|
|
318
343
|
pub use crate::animation::{
|
|
319
344
|
animate_color, animate_color_with, animate_float, animate_float_with,
|
|
@@ -345,19 +370,19 @@ pub mod prelude {
|
|
|
345
370
|
DropdownChevronVisualState, DropdownColors, DropdownFieldMetrics, DropdownFieldPresenter,
|
|
346
371
|
DropdownFieldTemplate, DropdownFieldVisualState, DropdownItem, DropdownOptionRowMetrics,
|
|
347
372
|
DropdownOptionRowPresenter, DropdownOptionRowTemplate, DropdownOptionRowVisualState,
|
|
348
|
-
DropdownSizing, Form, LabeledControlColors, LabeledControlSizing,
|
|
349
|
-
NavigateEventArgs, Popup, PressableIndicatorMetrics,
|
|
350
|
-
PressableIndicatorVisualState, ProgressBar, RadioButton,
|
|
351
|
-
RadioGroup, RadioGroupChangedEventArgs,
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
TextInputTemplate, TextInputVisualState,
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
DEFAULT_SWITCH_INDICATOR_TEMPLATE, DEFAULT_TEXT_INPUT_TEMPLATE,
|
|
373
|
+
DropdownSizing, Form, LabeledControlColors, LabeledControlSizing, LabeledControlTextStyle,
|
|
374
|
+
MenuItem, NavLink, NavigateEventArgs, Popup, PressableIndicatorMetrics,
|
|
375
|
+
PressableIndicatorPresenter, PressableIndicatorVisualState, ProgressBar, RadioButton,
|
|
376
|
+
RadioButtonChangedEventArgs, RadioGroup, RadioGroupChangedEventArgs,
|
|
377
|
+
RadioIndicatorPresenter, RadioIndicatorTemplate, RadioIndicatorVisualState, SelectionArea,
|
|
378
|
+
Slider, SliderChangedEventArgs, SliderColors, SliderPresenter, SliderPresenterMetrics,
|
|
379
|
+
SliderSizing, SliderTemplate, SliderVisualState, Switch, SwitchChangedEventArgs,
|
|
380
|
+
SwitchIndicatorPresenter, SwitchIndicatorTemplate, SwitchIndicatorVisualState, TextArea,
|
|
381
|
+
TextInput, TextInputColors, TextInputPresenter, TextInputTemplate, TextInputVisualState,
|
|
382
|
+
DEFAULT_BUTTON_TEMPLATE, DEFAULT_CHECKBOX_INDICATOR_TEMPLATE,
|
|
383
|
+
DEFAULT_DROPDOWN_CHEVRON_TEMPLATE, DEFAULT_DROPDOWN_FIELD_TEMPLATE,
|
|
384
|
+
DEFAULT_DROPDOWN_OPTION_ROW_TEMPLATE, DEFAULT_RADIO_INDICATOR_TEMPLATE,
|
|
385
|
+
DEFAULT_SLIDER_TEMPLATE, DEFAULT_SWITCH_INDICATOR_TEMPLATE, DEFAULT_TEXT_INPUT_TEMPLATE,
|
|
361
386
|
};
|
|
362
387
|
pub use crate::drag_drop::{
|
|
363
388
|
DragCompletedEventArgs, DragDataObject, DragDropEffects, DragEventArgs, DragSession,
|
|
@@ -386,6 +411,7 @@ pub mod prelude {
|
|
|
386
411
|
FileWorkerProcessResult, FileWriteProgress,
|
|
387
412
|
};
|
|
388
413
|
pub use crate::focus_visibility::show_keyboard_focus_for_key_event;
|
|
414
|
+
pub use crate::fui_component;
|
|
389
415
|
pub use crate::image_sampling::{ImageSampling, ImageSamplingMode};
|
|
390
416
|
pub use crate::logger;
|
|
391
417
|
pub use crate::navigation;
|
|
@@ -174,7 +174,7 @@ fn create_label(label: &str, slot: i32) -> TextNode {
|
|
|
174
174
|
.font_size(theme.context_menu.item.font_size)
|
|
175
175
|
.text_color(theme.context_menu.item.text_color)
|
|
176
176
|
.text_overflow(TextOverflow::Ellipsis)
|
|
177
|
-
.selectable(false
|
|
177
|
+
.selectable(false)
|
|
178
178
|
.interactive(true)
|
|
179
179
|
.preserve_selection_on_pointer_down(true)
|
|
180
180
|
.on_pointer_up(move |event| {
|
package/src/node/core.rs
CHANGED
|
@@ -514,7 +514,7 @@ impl NodeRef {
|
|
|
514
514
|
}
|
|
515
515
|
}
|
|
516
516
|
|
|
517
|
-
pub(crate) fn from_node<T: Node>(inner: Rc<RefCell<NodeCore>>, node: T) -> Self {
|
|
517
|
+
pub(crate) fn from_node<T: Node + 'static>(inner: Rc<RefCell<NodeCore>>, node: T) -> Self {
|
|
518
518
|
Self {
|
|
519
519
|
inner,
|
|
520
520
|
build_callback: Some(Rc::new(move || node.build())),
|
|
@@ -1561,7 +1561,7 @@ impl NodeRef {
|
|
|
1561
1561
|
}
|
|
1562
1562
|
}
|
|
1563
1563
|
|
|
1564
|
-
pub trait Node: Clone
|
|
1564
|
+
pub trait Node: Clone {
|
|
1565
1565
|
#[doc(hidden)]
|
|
1566
1566
|
fn node_ref(&self) -> NodeRef {
|
|
1567
1567
|
self.retained_node_ref()
|
|
@@ -2208,3 +2208,17 @@ pub trait Node: Clone + 'static {
|
|
|
2208
2208
|
self
|
|
2209
2209
|
}
|
|
2210
2210
|
}
|
|
2211
|
+
|
|
2212
|
+
impl<T: Node + ?Sized> Node for &T {
|
|
2213
|
+
fn retained_node_ref(&self) -> NodeRef {
|
|
2214
|
+
(*self).retained_node_ref()
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
fn retained_owner_attachment(&self) -> Option<Rc<dyn Any>> {
|
|
2218
|
+
(*self).retained_owner_attachment()
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
fn build_self(&self) {
|
|
2222
|
+
(*self).build_self();
|
|
2223
|
+
}
|
|
2224
|
+
}
|
package/src/node/mod.rs
CHANGED
|
@@ -32,8 +32,10 @@ mod svg_node;
|
|
|
32
32
|
mod text_node;
|
|
33
33
|
mod virtual_list;
|
|
34
34
|
|
|
35
|
+
#[doc(hidden)]
|
|
36
|
+
pub use core::NodeRef;
|
|
35
37
|
pub use core::{ContextMenuEventArgs, Node, NodeHandle};
|
|
36
|
-
pub(crate) use core::{
|
|
38
|
+
pub(crate) use core::{WeakFlexBox, WeakNodeRef};
|
|
37
39
|
pub use custom_drawable::CustomDrawable;
|
|
38
40
|
pub use flex_box::{Border, FlexBox, GradientStop};
|
|
39
41
|
pub use grid::{Grid, GridTrack};
|
|
@@ -59,7 +61,25 @@ pub trait HasFlexBoxRoot {
|
|
|
59
61
|
fn flex_box_root(&self) -> &FlexBox;
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
impl HasFlexBoxRoot for FlexBox {
|
|
65
|
+
fn flex_box_root(&self) -> &FlexBox {
|
|
66
|
+
self
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
pub trait FlexBoxSurface: HasFlexBoxRoot {
|
|
71
|
+
fn bind_theme(&self, handler: impl Fn(&FlexBox, crate::theme::Theme) + 'static) -> &Self {
|
|
72
|
+
let root = self.flex_box_root();
|
|
73
|
+
let weak_root = root.downgrade();
|
|
74
|
+
let guard = crate::theme::subscribe(move |theme| {
|
|
75
|
+
if let Some(root) = weak_root.upgrade() {
|
|
76
|
+
handler(&root, theme);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
root.retained_node_ref().retain_attachment(Rc::new(guard));
|
|
80
|
+
self
|
|
81
|
+
}
|
|
82
|
+
|
|
63
83
|
fn width(&self, width: f32, unit: Unit) -> &Self {
|
|
64
84
|
self.flex_box_root().width(width, unit);
|
|
65
85
|
self
|
package/src/node/text_node.rs
CHANGED
|
@@ -81,6 +81,19 @@ impl TextNode {
|
|
|
81
81
|
self
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
pub fn bind_theme(&self, handler: impl Fn(&TextNode, crate::theme::Theme) + 'static) -> &Self {
|
|
85
|
+
let weak_core = Rc::downgrade(&self.core);
|
|
86
|
+
let weak_props = Rc::downgrade(&self.props);
|
|
87
|
+
let guard = crate::theme::subscribe(move |theme| {
|
|
88
|
+
let (Some(core), Some(props)) = (weak_core.upgrade(), weak_props.upgrade()) else {
|
|
89
|
+
return;
|
|
90
|
+
};
|
|
91
|
+
handler(&TextNode { core, props }, theme);
|
|
92
|
+
});
|
|
93
|
+
self.retained_node_ref().retain_attachment(Rc::new(guard));
|
|
94
|
+
self
|
|
95
|
+
}
|
|
96
|
+
|
|
84
97
|
pub fn width(&self, width: f32, unit: Unit) -> &Self {
|
|
85
98
|
self.props.borrow_mut().width = Some((width, unit));
|
|
86
99
|
{
|
|
@@ -281,15 +294,13 @@ impl TextNode {
|
|
|
281
294
|
self
|
|
282
295
|
}
|
|
283
296
|
|
|
284
|
-
pub fn selectable(&self, selectable: bool
|
|
285
|
-
let resolved_selection_color = if selection_color == 0 {
|
|
286
|
-
theme::current_theme().colors.selection
|
|
287
|
-
} else {
|
|
288
|
-
selection_color
|
|
289
|
-
};
|
|
297
|
+
pub fn selectable(&self, selectable: bool) -> &Self {
|
|
290
298
|
let mut props = self.props.borrow_mut();
|
|
299
|
+
let resolved_selection_color = props
|
|
300
|
+
.selectable
|
|
301
|
+
.map(|(_, color)| color)
|
|
302
|
+
.unwrap_or_else(|| theme::current_theme().colors.selection);
|
|
291
303
|
props.selectable = Some((selectable, resolved_selection_color));
|
|
292
|
-
props.uses_theme_selection_color = selection_color == 0;
|
|
293
304
|
drop(props);
|
|
294
305
|
self.core.borrow_mut().behavior.selectable_text = selectable;
|
|
295
306
|
if self.has_built_handle() {
|
|
@@ -299,6 +310,19 @@ impl TextNode {
|
|
|
299
310
|
self
|
|
300
311
|
}
|
|
301
312
|
|
|
313
|
+
pub fn selection_color(&self, color: u32) -> &Self {
|
|
314
|
+
let mut props = self.props.borrow_mut();
|
|
315
|
+
let selectable = props.selectable.map(|(value, _)| value).unwrap_or(false);
|
|
316
|
+
props.selectable = Some((selectable, color));
|
|
317
|
+
props.uses_theme_selection_color = false;
|
|
318
|
+
drop(props);
|
|
319
|
+
if self.has_built_handle() {
|
|
320
|
+
ui::set_selectable(self.handle().raw(), selectable, color);
|
|
321
|
+
self.notify_retained_mutation();
|
|
322
|
+
}
|
|
323
|
+
self
|
|
324
|
+
}
|
|
325
|
+
|
|
302
326
|
pub fn editable(&self, editable: bool) -> &Self {
|
|
303
327
|
if editable {
|
|
304
328
|
let selection_color = self
|