@effindomv2/fui-rs 0.1.11 → 0.1.13
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/package.json +1 -1
- package/scripts/hostgen/rust-host-events.ts +1 -1
- package/scripts/hostgen/rust-host-services.ts +16 -16
- package/src/controls/anti_selection_area.rs +15 -0
- package/src/controls/button.rs +39 -0
- package/src/controls/checkbox.rs +36 -1
- package/src/controls/combobox.rs +18 -1
- package/src/controls/dropdown.rs +18 -0
- package/src/controls/internal/pressable_labeled_control.rs +70 -0
- package/src/controls/mod.rs +2 -1
- package/src/controls/nav_link.rs +11 -13
- package/src/controls/progress_bar.rs +120 -6
- package/src/controls/radio_button.rs +39 -1
- package/src/controls/selection_area.rs +17 -0
- package/src/controls/slider.rs +37 -0
- package/src/controls/switch.rs +35 -1
- package/src/controls/tests.rs +237 -0
- package/src/controls/text_area.rs +15 -0
- package/src/controls/text_input.rs +15 -0
- package/src/generated/framework_host_services.rs +40 -40
- package/src/lib.rs +2 -2
- package/src/node/mod.rs +29 -11
- package/src/node/scroll_box.rs +15 -0
- package/src/node/text_node.rs +21 -13
- package/src/node/virtual_list.rs +15 -0
- package/src/text.rs +15 -0
package/Cargo.toml
CHANGED
package/package.json
CHANGED
|
@@ -167,5 +167,5 @@ export async function generateRustHostEventsFile(
|
|
|
167
167
|
lines.push("");
|
|
168
168
|
});
|
|
169
169
|
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
170
|
-
await fs.writeFile(outputPath, `${lines.join("\n")}\n`, "utf8");
|
|
170
|
+
await fs.writeFile(outputPath, `${lines.join("\n").replace(/\n+$/, "")}\n`, "utf8");
|
|
171
171
|
}
|
|
@@ -140,9 +140,9 @@ function emitWasmCallArgs(args: readonly HostServiceTypeName[]): string {
|
|
|
140
140
|
function emitFrameworkNonWasmBody(importName: string, args: readonly HostServiceTypeName[]): string[] {
|
|
141
141
|
const argNames = args.map((_type, index) => `arg${String(index)}`);
|
|
142
142
|
if (importName === "fui_now_ms") {
|
|
143
|
-
return ["
|
|
143
|
+
return [" crate::ffi::test::host_now_ms()"];
|
|
144
144
|
}
|
|
145
|
-
return [`
|
|
145
|
+
return [` unsafe { crate::ffi::${importName}(${argNames.join(", ")}) }`];
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
function emitDecodeReturn(
|
|
@@ -154,7 +154,7 @@ function emitDecodeReturn(
|
|
|
154
154
|
return [];
|
|
155
155
|
}
|
|
156
156
|
if (!isBufferType(returns)) {
|
|
157
|
-
return ["
|
|
157
|
+
return [" raw_result"];
|
|
158
158
|
}
|
|
159
159
|
const decodeFn =
|
|
160
160
|
returns === "string" ? "decode_host_service_string_result" :
|
|
@@ -165,7 +165,7 @@ function emitDecodeReturn(
|
|
|
165
165
|
returns === "u64_array" ? "decode_host_service_u64_array_result" :
|
|
166
166
|
"decode_host_service_f64_array_result";
|
|
167
167
|
return [
|
|
168
|
-
`
|
|
168
|
+
` ${runtimePath}::${decodeFn}(result_ptr, raw_result, "${importName}")`,
|
|
169
169
|
];
|
|
170
170
|
}
|
|
171
171
|
|
|
@@ -219,32 +219,32 @@ export async function generateRustHostServicesFile(
|
|
|
219
219
|
const wrapperName = snakeCaseIdentifier(method.importName);
|
|
220
220
|
const returnType = rustReturnType(method.returns);
|
|
221
221
|
wrappers.push(`pub fn ${wrapperName}(${emitWrapperArgs(method.args)}) -> ${returnType} {`);
|
|
222
|
-
wrappers.push(`
|
|
223
|
-
wrappers.push("
|
|
222
|
+
wrappers.push(` #[cfg(${hostImportCfg})]`);
|
|
223
|
+
wrappers.push(" {");
|
|
224
224
|
if (isBufferType(method.returns)) {
|
|
225
|
-
wrappers.push(`
|
|
226
|
-
wrappers.push(`
|
|
225
|
+
wrappers.push(` let result_ptr = ${runtimePath}::host_service_result_buffer_ptr();`);
|
|
226
|
+
wrappers.push(` let result_cap = ${runtimePath}::host_service_result_buffer_size();`);
|
|
227
227
|
const wasmCallArgs = emitWasmCallArgs(method.args);
|
|
228
228
|
const callArgs = wasmCallArgs.length > 0 ? `${wasmCallArgs}, result_ptr, result_cap` : "result_ptr, result_cap";
|
|
229
|
-
wrappers.push(`
|
|
229
|
+
wrappers.push(` let raw_result = unsafe { __host_${method.importName}(${callArgs}) };`);
|
|
230
230
|
wrappers.push(...emitDecodeReturn(method.returns, runtimePath, method.importName));
|
|
231
231
|
} else if (method.returns === "void") {
|
|
232
232
|
const wasmCallArgs = emitWasmCallArgs(method.args);
|
|
233
|
-
wrappers.push(`
|
|
233
|
+
wrappers.push(` unsafe { __host_${method.importName}(${wasmCallArgs}) };`);
|
|
234
234
|
} else {
|
|
235
|
-
wrappers.push(`
|
|
235
|
+
wrappers.push(` unsafe { __host_${method.importName}(${emitWasmCallArgs(method.args)}) }`);
|
|
236
236
|
}
|
|
237
|
-
wrappers.push("
|
|
238
|
-
wrappers.push(`
|
|
239
|
-
wrappers.push("
|
|
237
|
+
wrappers.push(" }");
|
|
238
|
+
wrappers.push(` #[cfg(${fallbackCfg})]`);
|
|
239
|
+
wrappers.push(" {");
|
|
240
240
|
if (mode === "framework") {
|
|
241
241
|
wrappers.push(...emitFrameworkNonWasmBody(method.importName, method.args));
|
|
242
242
|
} else {
|
|
243
243
|
wrappers.push(
|
|
244
|
-
`
|
|
244
|
+
` panic!("Host service ${method.importName} is only available in wasm/browser builds.");`,
|
|
245
245
|
);
|
|
246
246
|
}
|
|
247
|
-
wrappers.push("
|
|
247
|
+
wrappers.push(" }");
|
|
248
248
|
wrappers.push("}");
|
|
249
249
|
wrappers.push("");
|
|
250
250
|
}
|
|
@@ -52,3 +52,18 @@ impl HasFlexBoxRoot for AntiSelectionArea {
|
|
|
52
52
|
&self.root
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
impl ThemeBindable for AntiSelectionArea {
|
|
57
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
58
|
+
self.root.retained_node_ref()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
62
|
+
let weak_root = self.root.downgrade();
|
|
63
|
+
Box::new(move || {
|
|
64
|
+
Some(AntiSelectionArea {
|
|
65
|
+
root: weak_root.upgrade()?,
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/controls/button.rs
CHANGED
|
@@ -4,6 +4,7 @@ use super::internal::button_presenter::{
|
|
|
4
4
|
use super::*;
|
|
5
5
|
use crate::ffi::CursorStyle;
|
|
6
6
|
use crate::node::{TextCore, WeakFlexBox};
|
|
7
|
+
use crate::theme::subscribe;
|
|
7
8
|
use crate::{focus_adorner, focus_visibility};
|
|
8
9
|
use std::rc::Rc;
|
|
9
10
|
|
|
@@ -250,6 +251,9 @@ impl Button {
|
|
|
250
251
|
ButtonEventTarget {
|
|
251
252
|
weak_root: self.root.downgrade(),
|
|
252
253
|
presenter: self.presenter.clone(),
|
|
254
|
+
label: self.label.clone(),
|
|
255
|
+
template: self.template.clone(),
|
|
256
|
+
label_value: self.label_value.clone(),
|
|
253
257
|
click: self.click.clone(),
|
|
254
258
|
double_click: self.double_click.clone(),
|
|
255
259
|
triple_click: self.triple_click.clone(),
|
|
@@ -345,6 +349,17 @@ impl HasFlexBoxRoot for Button {
|
|
|
345
349
|
}
|
|
346
350
|
}
|
|
347
351
|
|
|
352
|
+
impl ThemeBindable for Button {
|
|
353
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
354
|
+
self.root.retained_node_ref()
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
358
|
+
let target = self.event_target();
|
|
359
|
+
Box::new(move || target.upgrade())
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
348
363
|
impl LabeledControlTextStyle for Button {
|
|
349
364
|
fn set_label_font_family(&self, family: crate::FontFamily) {
|
|
350
365
|
self.set_explicit_font_family(family);
|
|
@@ -434,6 +449,9 @@ fn create_button_presenter(template: Option<Rc<dyn ButtonTemplate>>) -> Rc<dyn B
|
|
|
434
449
|
struct ButtonEventTarget {
|
|
435
450
|
weak_root: WeakFlexBox,
|
|
436
451
|
presenter: Rc<RefCell<Rc<dyn ButtonPresenter>>>,
|
|
452
|
+
label: Rc<RefCell<TextCore>>,
|
|
453
|
+
template: Rc<RefCell<Option<Rc<dyn ButtonTemplate>>>>,
|
|
454
|
+
label_value: Rc<RefCell<String>>,
|
|
437
455
|
click: Rc<RefCell<Option<ClickCallback>>>,
|
|
438
456
|
double_click: Rc<RefCell<Option<ClickCallback>>>,
|
|
439
457
|
triple_click: Rc<RefCell<Option<ClickCallback>>>,
|
|
@@ -448,6 +466,27 @@ struct ButtonEventTarget {
|
|
|
448
466
|
}
|
|
449
467
|
|
|
450
468
|
impl ButtonEventTarget {
|
|
469
|
+
fn upgrade(&self) -> Option<Button> {
|
|
470
|
+
Some(Button {
|
|
471
|
+
root: self.weak_root.upgrade()?,
|
|
472
|
+
presenter: self.presenter.clone(),
|
|
473
|
+
label: self.label.clone(),
|
|
474
|
+
template: self.template.clone(),
|
|
475
|
+
label_value: self.label_value.clone(),
|
|
476
|
+
click: self.click.clone(),
|
|
477
|
+
double_click: self.double_click.clone(),
|
|
478
|
+
triple_click: self.triple_click.clone(),
|
|
479
|
+
hovered_state: self.hovered_state.clone(),
|
|
480
|
+
pressed_state: self.pressed_state.clone(),
|
|
481
|
+
focused_state: self.focused_state.clone(),
|
|
482
|
+
keyboard_armed_key: self.keyboard_armed_key.clone(),
|
|
483
|
+
font_family_override: self.font_family_override.clone(),
|
|
484
|
+
font_size_override: self.font_size_override.clone(),
|
|
485
|
+
text_color_override: self.text_color_override.clone(),
|
|
486
|
+
colors_value: self.colors_value.clone(),
|
|
487
|
+
})
|
|
488
|
+
}
|
|
489
|
+
|
|
451
490
|
fn is_enabled(&self) -> bool {
|
|
452
491
|
self.weak_root
|
|
453
492
|
.upgrade()
|
package/src/controls/checkbox.rs
CHANGED
|
@@ -2,7 +2,9 @@ use super::internal::checkbox_indicator_presenter::{
|
|
|
2
2
|
create_default_checkbox_indicator_presenter, CheckboxIndicatorPresenter,
|
|
3
3
|
CheckboxIndicatorTemplate, CheckboxIndicatorVisualState,
|
|
4
4
|
};
|
|
5
|
-
use super::internal::pressable_labeled_control::
|
|
5
|
+
use super::internal::pressable_labeled_control::{
|
|
6
|
+
PressableLabeledControlState, WeakPressableLabeledControl,
|
|
7
|
+
};
|
|
6
8
|
use super::*;
|
|
7
9
|
use std::cell::{Cell, RefCell};
|
|
8
10
|
use std::rc::Rc;
|
|
@@ -318,9 +320,23 @@ impl HasFlexBoxRoot for Checkbox {
|
|
|
318
320
|
}
|
|
319
321
|
}
|
|
320
322
|
|
|
323
|
+
impl ThemeBindable for Checkbox {
|
|
324
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
325
|
+
self.root.retained_node_ref()
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
329
|
+
let target = CheckboxEventTarget::from_checkbox(self);
|
|
330
|
+
Box::new(move || target.upgrade())
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
321
334
|
#[derive(Clone)]
|
|
322
335
|
struct CheckboxEventTarget {
|
|
336
|
+
base: WeakPressableLabeledControl,
|
|
323
337
|
presenter: Rc<RefCell<Rc<dyn CheckboxIndicatorPresenter>>>,
|
|
338
|
+
template_override: Rc<RefCell<Option<Rc<dyn CheckboxIndicatorTemplate>>>>,
|
|
339
|
+
sizing_value: Rc<Cell<Option<LabeledControlSizing>>>,
|
|
324
340
|
state: Rc<Cell<CheckState>>,
|
|
325
341
|
tri_state: Rc<Cell<bool>>,
|
|
326
342
|
colors_value: Rc<Cell<Option<LabeledControlColors>>>,
|
|
@@ -331,7 +347,10 @@ struct CheckboxEventTarget {
|
|
|
331
347
|
impl CheckboxEventTarget {
|
|
332
348
|
fn from_checkbox(checkbox: &Checkbox) -> Self {
|
|
333
349
|
Self {
|
|
350
|
+
base: checkbox.base.downgrade(),
|
|
334
351
|
presenter: checkbox.indicator_presenter.clone(),
|
|
352
|
+
template_override: checkbox.template_override.clone(),
|
|
353
|
+
sizing_value: checkbox.sizing_value.clone(),
|
|
335
354
|
state: checkbox.state.clone(),
|
|
336
355
|
tri_state: checkbox.tri_state.clone(),
|
|
337
356
|
colors_value: checkbox.colors_value.clone(),
|
|
@@ -340,6 +359,22 @@ impl CheckboxEventTarget {
|
|
|
340
359
|
}
|
|
341
360
|
}
|
|
342
361
|
|
|
362
|
+
fn upgrade(&self) -> Option<Checkbox> {
|
|
363
|
+
let base = self.base.upgrade()?;
|
|
364
|
+
Some(Checkbox {
|
|
365
|
+
root: base.root(),
|
|
366
|
+
base,
|
|
367
|
+
indicator_presenter: self.presenter.clone(),
|
|
368
|
+
template_override: self.template_override.clone(),
|
|
369
|
+
sizing_value: self.sizing_value.clone(),
|
|
370
|
+
state: self.state.clone(),
|
|
371
|
+
tri_state: self.tri_state.clone(),
|
|
372
|
+
colors_value: self.colors_value.clone(),
|
|
373
|
+
changed: self.changed.clone(),
|
|
374
|
+
weak_root: self.weak_root.clone(),
|
|
375
|
+
})
|
|
376
|
+
}
|
|
377
|
+
|
|
343
378
|
fn activate(&self, base_state: PressableLabeledControlState) {
|
|
344
379
|
let next = match (self.state.get(), self.tri_state.get()) {
|
|
345
380
|
(CheckState::False, _) => CheckState::True,
|
package/src/controls/combobox.rs
CHANGED
|
@@ -24,7 +24,7 @@ use crate::node::{
|
|
|
24
24
|
};
|
|
25
25
|
use crate::signal::SubscriptionGuard;
|
|
26
26
|
use crate::theme::{current_theme, subscribe, Theme};
|
|
27
|
-
use crate::{app, frame_scheduler};
|
|
27
|
+
use crate::{app, frame_scheduler, ThemeBindable};
|
|
28
28
|
use std::cell::{Cell, RefCell};
|
|
29
29
|
use std::rc::{Rc, Weak};
|
|
30
30
|
|
|
@@ -876,6 +876,23 @@ impl HasFlexBoxRoot for ComboBox {
|
|
|
876
876
|
}
|
|
877
877
|
}
|
|
878
878
|
|
|
879
|
+
impl ThemeBindable for ComboBox {
|
|
880
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
881
|
+
self.root.retained_node_ref()
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
885
|
+
let weak_root = self.root.downgrade();
|
|
886
|
+
let weak_shared = Rc::downgrade(&self.shared);
|
|
887
|
+
Box::new(move || {
|
|
888
|
+
Some(ComboBox {
|
|
889
|
+
root: weak_root.upgrade()?,
|
|
890
|
+
shared: weak_shared.upgrade()?,
|
|
891
|
+
})
|
|
892
|
+
})
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
879
896
|
impl Node for ComboBox {
|
|
880
897
|
fn retained_node_ref(&self) -> NodeRef {
|
|
881
898
|
self.root.retained_node_ref()
|
package/src/controls/dropdown.rs
CHANGED
|
@@ -22,6 +22,7 @@ use crate::node::{row, FlexBox, FlexBoxSurface, HasFlexBoxRoot, Node, NodeRef, W
|
|
|
22
22
|
use crate::persisted::{persisted_value_adapter, PersistedInt32Codec};
|
|
23
23
|
use crate::signal::SubscriptionGuard;
|
|
24
24
|
use crate::theme::{current_theme, subscribe};
|
|
25
|
+
use crate::ThemeBindable;
|
|
25
26
|
use std::cell::{Cell, RefCell};
|
|
26
27
|
use std::rc::{Rc, Weak};
|
|
27
28
|
|
|
@@ -642,6 +643,23 @@ impl HasFlexBoxRoot for Dropdown {
|
|
|
642
643
|
}
|
|
643
644
|
}
|
|
644
645
|
|
|
646
|
+
impl ThemeBindable for Dropdown {
|
|
647
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
648
|
+
self.root.retained_node_ref()
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
652
|
+
let weak_root = self.root.downgrade();
|
|
653
|
+
let weak_shared = Rc::downgrade(&self.shared);
|
|
654
|
+
Box::new(move || {
|
|
655
|
+
Some(Dropdown {
|
|
656
|
+
root: weak_root.upgrade()?,
|
|
657
|
+
shared: weak_shared.upgrade()?,
|
|
658
|
+
})
|
|
659
|
+
})
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
645
663
|
impl DropdownShared {
|
|
646
664
|
fn is_enabled(&self) -> bool {
|
|
647
665
|
self.root
|
|
@@ -96,6 +96,29 @@ impl PressableLabeledControl {
|
|
|
96
96
|
self.root.clone()
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
pub(crate) fn downgrade(&self) -> WeakPressableLabeledControl {
|
|
100
|
+
WeakPressableLabeledControl {
|
|
101
|
+
root: self.root.downgrade(),
|
|
102
|
+
indicator_root: self.indicator_root.clone(),
|
|
103
|
+
label_node: self.label_node.clone(),
|
|
104
|
+
gap_node: self.gap_node.clone(),
|
|
105
|
+
label_host: self.label_host.clone(),
|
|
106
|
+
hovered_state: self.hovered_state.clone(),
|
|
107
|
+
pressed_state: self.pressed_state.clone(),
|
|
108
|
+
focused_state: self.focused_state.clone(),
|
|
109
|
+
key_pressed_state: self.key_pressed_state.clone(),
|
|
110
|
+
pointer_pressed_state: self.pointer_pressed_state.clone(),
|
|
111
|
+
enabled_state: self.enabled_state.clone(),
|
|
112
|
+
label_font_family_override: self.label_font_family_override.clone(),
|
|
113
|
+
label_font_size_override: self.label_font_size_override.clone(),
|
|
114
|
+
label_text_color_override: self.label_text_color_override.clone(),
|
|
115
|
+
colors_value: self.colors_value.clone(),
|
|
116
|
+
activation: self.activation.clone(),
|
|
117
|
+
state_changed: self.state_changed.clone(),
|
|
118
|
+
key_handler: self.key_handler.clone(),
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
99
122
|
#[cfg(test)]
|
|
100
123
|
pub(crate) fn test_parts(&self) -> (FlexBox, FlexBox, FlexBox, FlexBox) {
|
|
101
124
|
(
|
|
@@ -302,6 +325,53 @@ impl PressableLabeledControl {
|
|
|
302
325
|
}
|
|
303
326
|
}
|
|
304
327
|
|
|
328
|
+
#[derive(Clone)]
|
|
329
|
+
pub(crate) struct WeakPressableLabeledControl {
|
|
330
|
+
root: WeakFlexBox,
|
|
331
|
+
indicator_root: Rc<RefCell<FlexBox>>,
|
|
332
|
+
label_node: TextCore,
|
|
333
|
+
gap_node: FlexBox,
|
|
334
|
+
label_host: FlexBox,
|
|
335
|
+
hovered_state: Rc<Cell<bool>>,
|
|
336
|
+
pressed_state: Rc<Cell<bool>>,
|
|
337
|
+
focused_state: Rc<Cell<bool>>,
|
|
338
|
+
key_pressed_state: Rc<Cell<bool>>,
|
|
339
|
+
pointer_pressed_state: Rc<Cell<bool>>,
|
|
340
|
+
enabled_state: Rc<Cell<bool>>,
|
|
341
|
+
label_font_family_override: Rc<RefCell<Option<crate::FontFamily>>>,
|
|
342
|
+
label_font_size_override: Rc<Cell<f32>>,
|
|
343
|
+
label_text_color_override: Rc<Cell<Option<u32>>>,
|
|
344
|
+
colors_value: Rc<Cell<Option<LabeledControlColors>>>,
|
|
345
|
+
activation: Rc<RefCell<Option<ActivationCallback>>>,
|
|
346
|
+
state_changed: Rc<RefCell<Option<StateCallback>>>,
|
|
347
|
+
key_handler: Rc<RefCell<Option<KeyCallback>>>,
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
impl WeakPressableLabeledControl {
|
|
351
|
+
pub(crate) fn upgrade(&self) -> Option<PressableLabeledControl> {
|
|
352
|
+
Some(PressableLabeledControl {
|
|
353
|
+
root: self.root.upgrade()?,
|
|
354
|
+
indicator_root: self.indicator_root.clone(),
|
|
355
|
+
label_node: self.label_node.clone(),
|
|
356
|
+
gap_node: self.gap_node.clone(),
|
|
357
|
+
label_host: self.label_host.clone(),
|
|
358
|
+
hovered_state: self.hovered_state.clone(),
|
|
359
|
+
pressed_state: self.pressed_state.clone(),
|
|
360
|
+
focused_state: self.focused_state.clone(),
|
|
361
|
+
key_pressed_state: self.key_pressed_state.clone(),
|
|
362
|
+
pointer_pressed_state: self.pointer_pressed_state.clone(),
|
|
363
|
+
enabled_state: self.enabled_state.clone(),
|
|
364
|
+
label_font_family_override: self.label_font_family_override.clone(),
|
|
365
|
+
label_font_size_override: self.label_font_size_override.clone(),
|
|
366
|
+
label_text_color_override: self.label_text_color_override.clone(),
|
|
367
|
+
colors_value: self.colors_value.clone(),
|
|
368
|
+
activation: self.activation.clone(),
|
|
369
|
+
state_changed: self.state_changed.clone(),
|
|
370
|
+
key_handler: self.key_handler.clone(),
|
|
371
|
+
})
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
305
375
|
fn sync_base_theme_parts(
|
|
306
376
|
root: &FlexBox,
|
|
307
377
|
label_node: &TextCore,
|
package/src/controls/mod.rs
CHANGED
|
@@ -5,7 +5,8 @@ use crate::ffi::{
|
|
|
5
5
|
SemanticCheckedState, SemanticRole, Unit,
|
|
6
6
|
};
|
|
7
7
|
use crate::node::{
|
|
8
|
-
flex_box, row, Child, FlexBox, HasFlexBoxRoot, Node, NodeRef, TextNode,
|
|
8
|
+
flex_box, row, Child, FlexBox, HasFlexBoxRoot, Node, NodeRef, TextNode, ThemeBindable,
|
|
9
|
+
WeakNodeRef,
|
|
9
10
|
};
|
|
10
11
|
use crate::theme::{current_theme, subscribe};
|
|
11
12
|
use std::cell::{Cell, RefCell};
|
package/src/controls/nav_link.rs
CHANGED
|
@@ -271,19 +271,6 @@ impl NavLink {
|
|
|
271
271
|
self
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
pub fn bind_theme(&self, handler: impl Fn(&NavLink, crate::theme::Theme) + 'static) -> &Self {
|
|
275
|
-
let target = self.event_target();
|
|
276
|
-
let guard = subscribe(move |theme| {
|
|
277
|
-
if let Some(link) = target.upgrade() {
|
|
278
|
-
handler(&link, theme);
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
self.root
|
|
282
|
-
.retained_node_ref()
|
|
283
|
-
.retain_attachment(Rc::new(guard));
|
|
284
|
-
self
|
|
285
|
-
}
|
|
286
|
-
|
|
287
274
|
pub fn on_navigate(&self, handler: impl Fn(NavigateEventArgs) + 'static) -> &Self {
|
|
288
275
|
*self.navigate.borrow_mut() = Some(Rc::new(handler));
|
|
289
276
|
self
|
|
@@ -315,6 +302,17 @@ impl HasFlexBoxRoot for NavLink {
|
|
|
315
302
|
}
|
|
316
303
|
}
|
|
317
304
|
|
|
305
|
+
impl ThemeBindable for NavLink {
|
|
306
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
307
|
+
self.root.retained_node_ref()
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
311
|
+
let target = self.event_target();
|
|
312
|
+
Box::new(move || target.upgrade())
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
318
316
|
impl LabeledControlTextStyle for NavLink {
|
|
319
317
|
fn set_label_font_family(&self, family: crate::FontFamily) {
|
|
320
318
|
self.set_explicit_font_family(family);
|
|
@@ -31,6 +31,7 @@ pub struct ProgressBar {
|
|
|
31
31
|
min: Rc<Cell<f32>>,
|
|
32
32
|
max: Rc<Cell<f32>>,
|
|
33
33
|
value: Rc<Cell<f32>>,
|
|
34
|
+
orientation: Rc<Cell<Orientation>>,
|
|
34
35
|
sizing_value: Rc<Cell<Option<ProgressBarSizing>>>,
|
|
35
36
|
colors_value: Rc<Cell<Option<ProgressBarColors>>>,
|
|
36
37
|
weak_root: WeakFlexBox,
|
|
@@ -47,7 +48,9 @@ impl ProgressBar {
|
|
|
47
48
|
pub fn new() -> Self {
|
|
48
49
|
let root = flex_box();
|
|
49
50
|
let fill = flex_box();
|
|
50
|
-
root.
|
|
51
|
+
root.semantic_orientation(Orientation::Horizontal)
|
|
52
|
+
.semantic_value_range(0.0, 0.0, 100.0)
|
|
53
|
+
.child(&fill);
|
|
51
54
|
let control = Self {
|
|
52
55
|
weak_root: root.downgrade(),
|
|
53
56
|
weak_fill: fill.downgrade(),
|
|
@@ -56,6 +59,7 @@ impl ProgressBar {
|
|
|
56
59
|
min: Rc::new(Cell::new(0.0)),
|
|
57
60
|
max: Rc::new(Cell::new(100.0)),
|
|
58
61
|
value: Rc::new(Cell::new(0.0)),
|
|
62
|
+
orientation: Rc::new(Cell::new(Orientation::Horizontal)),
|
|
59
63
|
sizing_value: Rc::new(Cell::new(None)),
|
|
60
64
|
colors_value: Rc::new(Cell::new(None)),
|
|
61
65
|
};
|
|
@@ -88,6 +92,43 @@ impl ProgressBar {
|
|
|
88
92
|
self
|
|
89
93
|
}
|
|
90
94
|
|
|
95
|
+
pub fn length(&self, value: f32) -> &Self {
|
|
96
|
+
let value = clamp_direct_dimension("length", value);
|
|
97
|
+
let current = self.sizing_value.get().unwrap_or_default();
|
|
98
|
+
let mut sizing = ProgressBarSizing::new().length(value);
|
|
99
|
+
if current.has_thickness() {
|
|
100
|
+
sizing = sizing.thickness(current.thickness_px());
|
|
101
|
+
}
|
|
102
|
+
self.sizing_value.set(Some(sizing));
|
|
103
|
+
self.sync_geometry();
|
|
104
|
+
self
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
pub fn thickness(&self, value: f32) -> &Self {
|
|
108
|
+
let value = clamp_direct_dimension("thickness", value);
|
|
109
|
+
let current = self.sizing_value.get().unwrap_or_default();
|
|
110
|
+
let mut sizing = ProgressBarSizing::new().thickness(value);
|
|
111
|
+
if current.has_length() {
|
|
112
|
+
sizing = sizing.length(current.length_px());
|
|
113
|
+
}
|
|
114
|
+
self.sizing_value.set(Some(sizing));
|
|
115
|
+
self.sync_geometry();
|
|
116
|
+
self.sync_visual_state();
|
|
117
|
+
self
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
pub fn orientation(&self, orientation: Orientation) -> &Self {
|
|
121
|
+
let orientation = if orientation == Orientation::Vertical {
|
|
122
|
+
Orientation::Vertical
|
|
123
|
+
} else {
|
|
124
|
+
Orientation::Horizontal
|
|
125
|
+
};
|
|
126
|
+
self.orientation.set(orientation);
|
|
127
|
+
self.root.semantic_orientation(orientation);
|
|
128
|
+
self.sync_geometry();
|
|
129
|
+
self
|
|
130
|
+
}
|
|
131
|
+
|
|
91
132
|
pub fn sizing(&self, sizing: ProgressBarSizing) -> &Self {
|
|
92
133
|
self.sizing_value.set(Some(sizing));
|
|
93
134
|
self.sync_geometry();
|
|
@@ -165,6 +206,7 @@ impl ProgressBar {
|
|
|
165
206
|
self.min.get(),
|
|
166
207
|
self.max.get(),
|
|
167
208
|
self.value.get(),
|
|
209
|
+
self.orientation.get(),
|
|
168
210
|
resolve_sizing(self.sizing_value.get()),
|
|
169
211
|
);
|
|
170
212
|
}
|
|
@@ -179,6 +221,46 @@ impl ProgressBar {
|
|
|
179
221
|
}
|
|
180
222
|
}
|
|
181
223
|
|
|
224
|
+
#[derive(Clone)]
|
|
225
|
+
struct ProgressBarThemeTarget {
|
|
226
|
+
root: WeakFlexBox,
|
|
227
|
+
fill: WeakFlexBox,
|
|
228
|
+
min: Rc<Cell<f32>>,
|
|
229
|
+
max: Rc<Cell<f32>>,
|
|
230
|
+
value: Rc<Cell<f32>>,
|
|
231
|
+
orientation: Rc<Cell<Orientation>>,
|
|
232
|
+
sizing_value: Rc<Cell<Option<ProgressBarSizing>>>,
|
|
233
|
+
colors_value: Rc<Cell<Option<ProgressBarColors>>>,
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
impl ProgressBarThemeTarget {
|
|
237
|
+
fn upgrade(&self) -> Option<ProgressBar> {
|
|
238
|
+
Some(ProgressBar {
|
|
239
|
+
root: self.root.upgrade()?,
|
|
240
|
+
fill: self.fill.upgrade()?,
|
|
241
|
+
min: self.min.clone(),
|
|
242
|
+
max: self.max.clone(),
|
|
243
|
+
value: self.value.clone(),
|
|
244
|
+
orientation: self.orientation.clone(),
|
|
245
|
+
sizing_value: self.sizing_value.clone(),
|
|
246
|
+
colors_value: self.colors_value.clone(),
|
|
247
|
+
weak_root: self.root.clone(),
|
|
248
|
+
weak_fill: self.fill.clone(),
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
fn clamp_direct_dimension(method: &str, value: f32) -> f32 {
|
|
254
|
+
if value > 0.0 {
|
|
255
|
+
return value;
|
|
256
|
+
}
|
|
257
|
+
crate::logger::warn(
|
|
258
|
+
"Layout",
|
|
259
|
+
&format!("ProgressBar.{method}() received {value}; clamping to 1.0."),
|
|
260
|
+
);
|
|
261
|
+
1.0
|
|
262
|
+
}
|
|
263
|
+
|
|
182
264
|
impl Node for ProgressBar {
|
|
183
265
|
fn retained_node_ref(&self) -> NodeRef {
|
|
184
266
|
self.root.retained_node_ref()
|
|
@@ -197,12 +279,33 @@ impl HasFlexBoxRoot for ProgressBar {
|
|
|
197
279
|
}
|
|
198
280
|
}
|
|
199
281
|
|
|
282
|
+
impl ThemeBindable for ProgressBar {
|
|
283
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
284
|
+
self.root.retained_node_ref()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
288
|
+
let target = ProgressBarThemeTarget {
|
|
289
|
+
root: self.weak_root.clone(),
|
|
290
|
+
fill: self.weak_fill.clone(),
|
|
291
|
+
min: self.min.clone(),
|
|
292
|
+
max: self.max.clone(),
|
|
293
|
+
value: self.value.clone(),
|
|
294
|
+
orientation: self.orientation.clone(),
|
|
295
|
+
sizing_value: self.sizing_value.clone(),
|
|
296
|
+
colors_value: self.colors_value.clone(),
|
|
297
|
+
};
|
|
298
|
+
Box::new(move || target.upgrade())
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
200
302
|
fn sync_progress_geometry(
|
|
201
303
|
root: &FlexBox,
|
|
202
304
|
fill: &FlexBox,
|
|
203
305
|
min: f32,
|
|
204
306
|
max: f32,
|
|
205
307
|
current_value: f32,
|
|
308
|
+
orientation: Orientation,
|
|
206
309
|
sizing: ResolvedProgressBarSizing,
|
|
207
310
|
) {
|
|
208
311
|
let range = max - min;
|
|
@@ -212,15 +315,26 @@ fn sync_progress_geometry(
|
|
|
212
315
|
0.0
|
|
213
316
|
};
|
|
214
317
|
let fill_length = sizing.length * fraction;
|
|
215
|
-
|
|
216
|
-
.
|
|
217
|
-
|
|
318
|
+
if orientation == Orientation::Vertical {
|
|
319
|
+
root.flex_direction(FlexDirection::Column)
|
|
320
|
+
.justify_content(JustifyContent::End)
|
|
321
|
+
.width(sizing.thickness, Unit::Pixel)
|
|
322
|
+
.height(sizing.length, Unit::Pixel);
|
|
323
|
+
fill.width(sizing.thickness, Unit::Pixel)
|
|
324
|
+
.height(fill_length, Unit::Pixel);
|
|
325
|
+
} else {
|
|
326
|
+
root.flex_direction(FlexDirection::Row)
|
|
327
|
+
.justify_content(JustifyContent::Start)
|
|
328
|
+
.width(sizing.length, Unit::Pixel)
|
|
329
|
+
.height(sizing.thickness, Unit::Pixel);
|
|
330
|
+
fill.width(fill_length, Unit::Pixel)
|
|
331
|
+
.height(sizing.thickness, Unit::Pixel);
|
|
332
|
+
}
|
|
333
|
+
root.semantic_value_range(current_value, min, max)
|
|
218
334
|
.default_semantic_label(format!(
|
|
219
335
|
"Progress bar, value {}, range {} to {}",
|
|
220
336
|
current_value, min, max
|
|
221
337
|
));
|
|
222
|
-
fill.width(fill_length, Unit::Pixel)
|
|
223
|
-
.height(sizing.thickness, Unit::Pixel);
|
|
224
338
|
}
|
|
225
339
|
|
|
226
340
|
fn sync_progress_visual_state(
|