@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
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
use super::internal::pressable_labeled_control::
|
|
1
|
+
use super::internal::pressable_labeled_control::{
|
|
2
|
+
PressableLabeledControlState, WeakPressableLabeledControl,
|
|
3
|
+
};
|
|
2
4
|
use super::internal::radio_indicator_presenter::{
|
|
3
5
|
create_default_radio_indicator_presenter, RadioIndicatorPresenter, RadioIndicatorTemplate,
|
|
4
6
|
RadioIndicatorVisualState,
|
|
@@ -250,9 +252,24 @@ impl HasFlexBoxRoot for RadioButton {
|
|
|
250
252
|
}
|
|
251
253
|
}
|
|
252
254
|
|
|
255
|
+
impl ThemeBindable for RadioButton {
|
|
256
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
257
|
+
self.root.retained_node_ref()
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
261
|
+
let target = RadioEventTarget::from_radio(self);
|
|
262
|
+
Box::new(move || target.upgrade())
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
253
266
|
#[derive(Clone)]
|
|
254
267
|
struct RadioEventTarget {
|
|
268
|
+
base: WeakPressableLabeledControl,
|
|
255
269
|
presenter: Rc<RefCell<Rc<dyn RadioIndicatorPresenter>>>,
|
|
270
|
+
template_override: Rc<RefCell<Option<Rc<dyn RadioIndicatorTemplate>>>>,
|
|
271
|
+
sizing_value: Rc<Cell<Option<LabeledControlSizing>>>,
|
|
272
|
+
value_text: String,
|
|
256
273
|
checked: Rc<Cell<bool>>,
|
|
257
274
|
colors_value: Rc<Cell<Option<LabeledControlColors>>>,
|
|
258
275
|
changed: Rc<RefCell<Option<RadioButtonChangedCallback>>>,
|
|
@@ -263,7 +280,11 @@ struct RadioEventTarget {
|
|
|
263
280
|
impl RadioEventTarget {
|
|
264
281
|
fn from_radio(radio: &RadioButton) -> Self {
|
|
265
282
|
Self {
|
|
283
|
+
base: radio.base.downgrade(),
|
|
266
284
|
presenter: radio.indicator_presenter.clone(),
|
|
285
|
+
template_override: radio.template_override.clone(),
|
|
286
|
+
sizing_value: radio.sizing_value.clone(),
|
|
287
|
+
value_text: radio.value_text.clone(),
|
|
267
288
|
checked: radio.checked.clone(),
|
|
268
289
|
colors_value: radio.colors_value.clone(),
|
|
269
290
|
changed: radio.changed.clone(),
|
|
@@ -272,6 +293,23 @@ impl RadioEventTarget {
|
|
|
272
293
|
}
|
|
273
294
|
}
|
|
274
295
|
|
|
296
|
+
fn upgrade(&self) -> Option<RadioButton> {
|
|
297
|
+
let base = self.base.upgrade()?;
|
|
298
|
+
Some(RadioButton {
|
|
299
|
+
root: base.root(),
|
|
300
|
+
base,
|
|
301
|
+
indicator_presenter: self.presenter.clone(),
|
|
302
|
+
template_override: self.template_override.clone(),
|
|
303
|
+
sizing_value: self.sizing_value.clone(),
|
|
304
|
+
colors_value: self.colors_value.clone(),
|
|
305
|
+
value_text: self.value_text.clone(),
|
|
306
|
+
checked: self.checked.clone(),
|
|
307
|
+
changed: self.changed.clone(),
|
|
308
|
+
owner_group: self.owner_group.clone(),
|
|
309
|
+
weak_root: self.weak_root.clone(),
|
|
310
|
+
})
|
|
311
|
+
}
|
|
312
|
+
|
|
275
313
|
fn activate(&self, base_state: PressableLabeledControlState) {
|
|
276
314
|
if let Some(group) = self.owner_group() {
|
|
277
315
|
if let Some(handle) = upgraded_handle(&self.weak_root) {
|
|
@@ -80,3 +80,20 @@ impl HasFlexBoxRoot for SelectionArea {
|
|
|
80
80
|
&self.root
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
+
|
|
84
|
+
impl ThemeBindable for SelectionArea {
|
|
85
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
86
|
+
self.root.retained_node_ref()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
90
|
+
let weak_root = self.root.downgrade();
|
|
91
|
+
let signal = self.selected_text_signal.clone();
|
|
92
|
+
Box::new(move || {
|
|
93
|
+
Some(SelectionArea {
|
|
94
|
+
root: weak_root.upgrade()?,
|
|
95
|
+
selected_text_signal: signal.clone(),
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
}
|
package/src/controls/slider.rs
CHANGED
|
@@ -415,11 +415,24 @@ impl HasFlexBoxRoot for Slider {
|
|
|
415
415
|
}
|
|
416
416
|
}
|
|
417
417
|
|
|
418
|
+
impl ThemeBindable for Slider {
|
|
419
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
420
|
+
self.root.retained_node_ref()
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
424
|
+
let target = SliderEventTarget::from_slider(self);
|
|
425
|
+
Box::new(move || target.upgrade())
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
418
429
|
#[derive(Clone)]
|
|
419
430
|
struct SliderEventTarget {
|
|
420
431
|
weak_root: Rc<WeakNodeRef>,
|
|
421
432
|
weak_flex_root: WeakFlexBox,
|
|
422
433
|
slider_presenter: Rc<RefCell<Rc<dyn SliderPresenter>>>,
|
|
434
|
+
template_override: Rc<RefCell<Option<Rc<dyn SliderTemplate>>>>,
|
|
435
|
+
sizing_value: Rc<Cell<Option<SliderSizing>>>,
|
|
423
436
|
min: Rc<Cell<f32>>,
|
|
424
437
|
max: Rc<Cell<f32>>,
|
|
425
438
|
step: Rc<Cell<f32>>,
|
|
@@ -439,6 +452,8 @@ impl SliderEventTarget {
|
|
|
439
452
|
weak_root: slider.weak_root.clone(),
|
|
440
453
|
weak_flex_root: slider.weak_flex_root.clone(),
|
|
441
454
|
slider_presenter: slider.slider_presenter.clone(),
|
|
455
|
+
template_override: slider.template_override.clone(),
|
|
456
|
+
sizing_value: slider.sizing_value.clone(),
|
|
442
457
|
min: slider.min.clone(),
|
|
443
458
|
max: slider.max.clone(),
|
|
444
459
|
step: slider.step.clone(),
|
|
@@ -453,6 +468,28 @@ impl SliderEventTarget {
|
|
|
453
468
|
}
|
|
454
469
|
}
|
|
455
470
|
|
|
471
|
+
fn upgrade(&self) -> Option<Slider> {
|
|
472
|
+
Some(Slider {
|
|
473
|
+
root: self.weak_flex_root.upgrade()?,
|
|
474
|
+
slider_presenter: self.slider_presenter.clone(),
|
|
475
|
+
template_override: self.template_override.clone(),
|
|
476
|
+
sizing_value: self.sizing_value.clone(),
|
|
477
|
+
colors_value: self.colors_value.clone(),
|
|
478
|
+
min: self.min.clone(),
|
|
479
|
+
max: self.max.clone(),
|
|
480
|
+
step: self.step.clone(),
|
|
481
|
+
value: self.value.clone(),
|
|
482
|
+
length: self.length.clone(),
|
|
483
|
+
orientation: self.orientation.clone(),
|
|
484
|
+
changed: self.changed.clone(),
|
|
485
|
+
hovered_state: self.hovered_state.clone(),
|
|
486
|
+
dragging_state: self.dragging_state.clone(),
|
|
487
|
+
focused_state: self.focused_state.clone(),
|
|
488
|
+
weak_root: self.weak_root.clone(),
|
|
489
|
+
weak_flex_root: self.weak_flex_root.clone(),
|
|
490
|
+
})
|
|
491
|
+
}
|
|
492
|
+
|
|
456
493
|
fn is_enabled(&self) -> bool {
|
|
457
494
|
self.weak_flex_root
|
|
458
495
|
.upgrade()
|
package/src/controls/switch.rs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
use super::internal::pressable_labeled_control::
|
|
1
|
+
use super::internal::pressable_labeled_control::{
|
|
2
|
+
PressableLabeledControlState, WeakPressableLabeledControl,
|
|
3
|
+
};
|
|
2
4
|
use super::internal::switch_indicator_presenter::{
|
|
3
5
|
create_default_switch_indicator_presenter, SwitchIndicatorPresenter, SwitchIndicatorTemplate,
|
|
4
6
|
SwitchIndicatorVisualState,
|
|
@@ -241,9 +243,23 @@ impl HasFlexBoxRoot for Switch {
|
|
|
241
243
|
}
|
|
242
244
|
}
|
|
243
245
|
|
|
246
|
+
impl ThemeBindable for Switch {
|
|
247
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
248
|
+
self.root.retained_node_ref()
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
252
|
+
let target = SwitchEventTarget::from_switch(self);
|
|
253
|
+
Box::new(move || target.upgrade())
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
244
257
|
#[derive(Clone)]
|
|
245
258
|
struct SwitchEventTarget {
|
|
259
|
+
base: WeakPressableLabeledControl,
|
|
246
260
|
presenter: Rc<RefCell<Rc<dyn SwitchIndicatorPresenter>>>,
|
|
261
|
+
template_override: Rc<RefCell<Option<Rc<dyn SwitchIndicatorTemplate>>>>,
|
|
262
|
+
sizing_value: Rc<Cell<Option<LabeledControlSizing>>>,
|
|
247
263
|
checked: Rc<Cell<bool>>,
|
|
248
264
|
colors_value: Rc<Cell<Option<LabeledControlColors>>>,
|
|
249
265
|
changed: Rc<RefCell<Option<SwitchChangedCallback>>>,
|
|
@@ -253,7 +269,10 @@ struct SwitchEventTarget {
|
|
|
253
269
|
impl SwitchEventTarget {
|
|
254
270
|
fn from_switch(switch: &Switch) -> Self {
|
|
255
271
|
Self {
|
|
272
|
+
base: switch.base.downgrade(),
|
|
256
273
|
presenter: switch.indicator_presenter.clone(),
|
|
274
|
+
template_override: switch.template_override.clone(),
|
|
275
|
+
sizing_value: switch.sizing_value.clone(),
|
|
257
276
|
checked: switch.checked.clone(),
|
|
258
277
|
colors_value: switch.colors_value.clone(),
|
|
259
278
|
changed: switch.changed.clone(),
|
|
@@ -261,6 +280,21 @@ impl SwitchEventTarget {
|
|
|
261
280
|
}
|
|
262
281
|
}
|
|
263
282
|
|
|
283
|
+
fn upgrade(&self) -> Option<Switch> {
|
|
284
|
+
let base = self.base.upgrade()?;
|
|
285
|
+
Some(Switch {
|
|
286
|
+
root: base.root(),
|
|
287
|
+
base,
|
|
288
|
+
indicator_presenter: self.presenter.clone(),
|
|
289
|
+
template_override: self.template_override.clone(),
|
|
290
|
+
sizing_value: self.sizing_value.clone(),
|
|
291
|
+
colors_value: self.colors_value.clone(),
|
|
292
|
+
checked: self.checked.clone(),
|
|
293
|
+
changed: self.changed.clone(),
|
|
294
|
+
weak_root: self.weak_root.clone(),
|
|
295
|
+
})
|
|
296
|
+
}
|
|
297
|
+
|
|
264
298
|
fn activate(&self, base_state: PressableLabeledControlState) {
|
|
265
299
|
apply_switch_checked(
|
|
266
300
|
&self.presenter,
|
package/src/controls/tests.rs
CHANGED
|
@@ -1185,6 +1185,149 @@ fn nav_link_bind_theme_preserves_concrete_control_dx_and_retained_lifetime() {
|
|
|
1185
1185
|
use_custom_theme(previous_theme);
|
|
1186
1186
|
}
|
|
1187
1187
|
|
|
1188
|
+
#[test]
|
|
1189
|
+
fn button_bind_theme_preserves_concrete_control_dx_and_retained_lifetime() {
|
|
1190
|
+
ffi::test::reset();
|
|
1191
|
+
let previous_theme = current_theme();
|
|
1192
|
+
let parent = column();
|
|
1193
|
+
let control = button("Themed action");
|
|
1194
|
+
control.bind_theme(|button, theme| {
|
|
1195
|
+
button.colors(
|
|
1196
|
+
ButtonColors::new()
|
|
1197
|
+
.background(theme.colors.accent)
|
|
1198
|
+
.text_primary(theme.colors.text_on_accent),
|
|
1199
|
+
);
|
|
1200
|
+
});
|
|
1201
|
+
parent.child(&control);
|
|
1202
|
+
Application::mount(parent);
|
|
1203
|
+
let handle = control.handle().raw();
|
|
1204
|
+
drop(control);
|
|
1205
|
+
ffi::test::take_calls();
|
|
1206
|
+
|
|
1207
|
+
let changed = generate_theme(false, 0x2468ACFF);
|
|
1208
|
+
use_custom_theme(changed.clone());
|
|
1209
|
+
let calls = ffi::test::take_calls();
|
|
1210
|
+
assert!(calls.iter().any(|call| matches!(
|
|
1211
|
+
call,
|
|
1212
|
+
Call::SetBgColor { handle: actual, color }
|
|
1213
|
+
if *actual == handle && *color == changed.colors.accent
|
|
1214
|
+
)));
|
|
1215
|
+
|
|
1216
|
+
use_custom_theme(previous_theme);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
#[test]
|
|
1220
|
+
fn flex_box_backed_controls_preserve_concrete_bind_theme_dx() {
|
|
1221
|
+
ffi::test::reset();
|
|
1222
|
+
let invocations = Rc::new(Cell::new(0));
|
|
1223
|
+
let count = |invocations: &Rc<Cell<i32>>| {
|
|
1224
|
+
let invocations = invocations.clone();
|
|
1225
|
+
move || invocations.set(invocations.get() + 1)
|
|
1226
|
+
};
|
|
1227
|
+
|
|
1228
|
+
let checkbox = checkbox("Checkbox");
|
|
1229
|
+
checkbox.bind_theme({
|
|
1230
|
+
let count = count(&invocations);
|
|
1231
|
+
move |control, theme| {
|
|
1232
|
+
count();
|
|
1233
|
+
control.colors(LabeledControlColors::new().text_primary(theme.colors.text_primary));
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
let switch = switch("Switch");
|
|
1237
|
+
switch.bind_theme({
|
|
1238
|
+
let count = count(&invocations);
|
|
1239
|
+
move |control, theme| {
|
|
1240
|
+
count();
|
|
1241
|
+
control.colors(LabeledControlColors::new().text_primary(theme.colors.text_primary));
|
|
1242
|
+
}
|
|
1243
|
+
});
|
|
1244
|
+
let radio = radio_button("Radio");
|
|
1245
|
+
radio.bind_theme({
|
|
1246
|
+
let count = count(&invocations);
|
|
1247
|
+
move |control, theme| {
|
|
1248
|
+
count();
|
|
1249
|
+
control.colors(LabeledControlColors::new().text_primary(theme.colors.text_primary));
|
|
1250
|
+
}
|
|
1251
|
+
});
|
|
1252
|
+
let slider = slider();
|
|
1253
|
+
slider.bind_theme({
|
|
1254
|
+
let count = count(&invocations);
|
|
1255
|
+
move |control, theme| {
|
|
1256
|
+
count();
|
|
1257
|
+
control.colors(SliderColors::new().fill(theme.colors.accent));
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1260
|
+
let progress = progress_bar();
|
|
1261
|
+
progress.bind_theme({
|
|
1262
|
+
let count = count(&invocations);
|
|
1263
|
+
move |control, theme| {
|
|
1264
|
+
count();
|
|
1265
|
+
control.colors(ProgressBarColors::new().fill(theme.colors.accent));
|
|
1266
|
+
}
|
|
1267
|
+
});
|
|
1268
|
+
let dropdown = dropdown();
|
|
1269
|
+
dropdown.bind_theme({
|
|
1270
|
+
let count = count(&invocations);
|
|
1271
|
+
move |control, theme| {
|
|
1272
|
+
count();
|
|
1273
|
+
control.colors(DropdownColors::new().text_primary(theme.colors.text_primary));
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
let combo = combo_box();
|
|
1277
|
+
combo.bind_theme({
|
|
1278
|
+
let count = count(&invocations);
|
|
1279
|
+
move |control, theme| {
|
|
1280
|
+
count();
|
|
1281
|
+
control.colors(DropdownColors::new().text_primary(theme.colors.text_primary));
|
|
1282
|
+
}
|
|
1283
|
+
});
|
|
1284
|
+
let input = text_input();
|
|
1285
|
+
input.bind_theme({
|
|
1286
|
+
let count = count(&invocations);
|
|
1287
|
+
move |control, theme| {
|
|
1288
|
+
count();
|
|
1289
|
+
control.colors(TextInputColors::new().text_primary(theme.colors.text_primary));
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
let area = text_area();
|
|
1293
|
+
area.bind_theme({
|
|
1294
|
+
let count = count(&invocations);
|
|
1295
|
+
move |control, theme| {
|
|
1296
|
+
count();
|
|
1297
|
+
control.colors(TextInputColors::new().text_primary(theme.colors.text_primary));
|
|
1298
|
+
}
|
|
1299
|
+
});
|
|
1300
|
+
let selection = selection_area();
|
|
1301
|
+
selection.bind_theme({
|
|
1302
|
+
let count = count(&invocations);
|
|
1303
|
+
move |control, _theme| {
|
|
1304
|
+
count();
|
|
1305
|
+
let _ = control.selected_text();
|
|
1306
|
+
}
|
|
1307
|
+
});
|
|
1308
|
+
let barrier = anti_selection_area();
|
|
1309
|
+
barrier.bind_theme({
|
|
1310
|
+
let count = count(&invocations);
|
|
1311
|
+
move |control, _theme| {
|
|
1312
|
+
count();
|
|
1313
|
+
control.children(Vec::<Child>::new());
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
|
|
1317
|
+
let scroll = crate::scroll_box();
|
|
1318
|
+
scroll.bind_theme({
|
|
1319
|
+
let count = count(&invocations);
|
|
1320
|
+
move |_control: &crate::ScrollBox, _theme| count()
|
|
1321
|
+
});
|
|
1322
|
+
let list = crate::virtual_list(10, 32.0);
|
|
1323
|
+
list.bind_theme({
|
|
1324
|
+
let count = count(&invocations);
|
|
1325
|
+
move |_control: &crate::VirtualList, _theme| count()
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
assert_eq!(invocations.get(), 13);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1188
1331
|
#[test]
|
|
1189
1332
|
fn retained_pressable_and_slider_keep_theme_subscriptions_after_wrappers_drop() {
|
|
1190
1333
|
ffi::test::reset();
|
|
@@ -3214,6 +3357,100 @@ fn progress_bar_invalid_sizing_warns_and_falls_back_to_defaults() {
|
|
|
3214
3357
|
)));
|
|
3215
3358
|
}
|
|
3216
3359
|
|
|
3360
|
+
#[test]
|
|
3361
|
+
fn progress_bar_direct_length_and_thickness_match_fui_as() {
|
|
3362
|
+
ffi::test::reset();
|
|
3363
|
+
let progress = progress_bar();
|
|
3364
|
+
progress.length(320.0).thickness(18.0);
|
|
3365
|
+
Application::mount(progress);
|
|
3366
|
+
let calls = ffi::test::take_calls();
|
|
3367
|
+
assert!(calls.iter().any(|call| matches!(
|
|
3368
|
+
call,
|
|
3369
|
+
Call::SetWidth { value, unit_enum, .. }
|
|
3370
|
+
if *value == 320.0 && *unit_enum == Unit::Pixel as u32
|
|
3371
|
+
)));
|
|
3372
|
+
assert!(calls.iter().any(|call| matches!(
|
|
3373
|
+
call,
|
|
3374
|
+
Call::SetHeight { value, unit_enum, .. }
|
|
3375
|
+
if *value == 18.0 && *unit_enum == Unit::Pixel as u32
|
|
3376
|
+
)));
|
|
3377
|
+
|
|
3378
|
+
ffi::test::reset();
|
|
3379
|
+
let progress = progress_bar();
|
|
3380
|
+
progress.length(0.0).thickness(-2.0);
|
|
3381
|
+
let calls = ffi::test::take_calls();
|
|
3382
|
+
assert!(calls.iter().any(|call| matches!(
|
|
3383
|
+
call,
|
|
3384
|
+
Call::Log { category, message }
|
|
3385
|
+
if category == "Warning/Layout"
|
|
3386
|
+
&& message == "ProgressBar.length() received 0; clamping to 1.0."
|
|
3387
|
+
)));
|
|
3388
|
+
assert!(calls.iter().any(|call| matches!(
|
|
3389
|
+
call,
|
|
3390
|
+
Call::Log { category, message }
|
|
3391
|
+
if category == "Warning/Layout"
|
|
3392
|
+
&& message == "ProgressBar.thickness() received -2; clamping to 1.0."
|
|
3393
|
+
)));
|
|
3394
|
+
}
|
|
3395
|
+
|
|
3396
|
+
#[test]
|
|
3397
|
+
fn progress_bar_maps_length_and_thickness_to_orientation() {
|
|
3398
|
+
ffi::test::reset();
|
|
3399
|
+
let progress = progress_bar();
|
|
3400
|
+
progress
|
|
3401
|
+
.value(25.0)
|
|
3402
|
+
.length(300.0)
|
|
3403
|
+
.thickness(18.0)
|
|
3404
|
+
.orientation(Orientation::Vertical);
|
|
3405
|
+
Application::mount(progress.clone());
|
|
3406
|
+
let calls = ffi::test::take_calls();
|
|
3407
|
+
let root_handle = progress.retained_node_ref().handle().raw();
|
|
3408
|
+
let fill_handle = child_handles_for_parent(&calls, root_handle)
|
|
3409
|
+
.into_iter()
|
|
3410
|
+
.next()
|
|
3411
|
+
.expect("fill handle");
|
|
3412
|
+
|
|
3413
|
+
assert!(calls.iter().any(|call| matches!(
|
|
3414
|
+
call,
|
|
3415
|
+
Call::SetWidth { handle, value, unit_enum }
|
|
3416
|
+
if *handle == root_handle && *value == 18.0 && *unit_enum == Unit::Pixel as u32
|
|
3417
|
+
)));
|
|
3418
|
+
assert!(calls.iter().any(|call| matches!(
|
|
3419
|
+
call,
|
|
3420
|
+
Call::SetHeight { handle, value, unit_enum }
|
|
3421
|
+
if *handle == root_handle && *value == 300.0 && *unit_enum == Unit::Pixel as u32
|
|
3422
|
+
)));
|
|
3423
|
+
assert!(calls.iter().any(|call| matches!(
|
|
3424
|
+
call,
|
|
3425
|
+
Call::SetWidth { handle, value, unit_enum }
|
|
3426
|
+
if *handle == fill_handle && *value == 18.0 && *unit_enum == Unit::Pixel as u32
|
|
3427
|
+
)));
|
|
3428
|
+
assert!(calls.iter().any(|call| matches!(
|
|
3429
|
+
call,
|
|
3430
|
+
Call::SetHeight { handle, value, unit_enum }
|
|
3431
|
+
if *handle == fill_handle && *value == 75.0 && *unit_enum == Unit::Pixel as u32
|
|
3432
|
+
)));
|
|
3433
|
+
assert!(calls.iter().any(|call| matches!(
|
|
3434
|
+
call,
|
|
3435
|
+
Call::SetSemanticOrientation { handle, orientation_enum }
|
|
3436
|
+
if *handle == root_handle && *orientation_enum == Orientation::Vertical as u32
|
|
3437
|
+
)));
|
|
3438
|
+
|
|
3439
|
+
ffi::test::reset();
|
|
3440
|
+
progress.orientation(Orientation::Horizontal);
|
|
3441
|
+
let calls = ffi::test::take_calls();
|
|
3442
|
+
assert!(calls.iter().any(|call| matches!(
|
|
3443
|
+
call,
|
|
3444
|
+
Call::SetWidth { handle, value, unit_enum }
|
|
3445
|
+
if *handle == root_handle && *value == 300.0 && *unit_enum == Unit::Pixel as u32
|
|
3446
|
+
)));
|
|
3447
|
+
assert!(calls.iter().any(|call| matches!(
|
|
3448
|
+
call,
|
|
3449
|
+
Call::SetHeight { handle, value, unit_enum }
|
|
3450
|
+
if *handle == root_handle && *value == 18.0 && *unit_enum == Unit::Pixel as u32
|
|
3451
|
+
)));
|
|
3452
|
+
}
|
|
3453
|
+
|
|
3217
3454
|
#[test]
|
|
3218
3455
|
fn progress_bar_theme_changes_update_default_colors() {
|
|
3219
3456
|
ffi::test::reset();
|
|
@@ -198,6 +198,21 @@ impl HasFlexBoxRoot for TextArea {
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
impl crate::ThemeBindable for TextArea {
|
|
202
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
203
|
+
self.core.flex_box_root().retained_node_ref()
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
207
|
+
let weak_core = Rc::downgrade(&self.core);
|
|
208
|
+
Box::new(move || {
|
|
209
|
+
Some(TextArea {
|
|
210
|
+
core: weak_core.upgrade()?,
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
201
216
|
impl Node for TextArea {
|
|
202
217
|
fn retained_node_ref(&self) -> NodeRef {
|
|
203
218
|
let core = self.core.clone();
|
|
@@ -189,6 +189,21 @@ impl HasFlexBoxRoot for TextInput {
|
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
impl crate::ThemeBindable for TextInput {
|
|
193
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
194
|
+
self.core.flex_box_root().retained_node_ref()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
198
|
+
let weak_core = Rc::downgrade(&self.core);
|
|
199
|
+
Box::new(move || {
|
|
200
|
+
Some(TextInput {
|
|
201
|
+
core: weak_core.upgrade()?,
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
192
207
|
impl Node for TextInput {
|
|
193
208
|
fn retained_node_ref(&self) -> NodeRef {
|
|
194
209
|
let core = self.core.clone();
|
|
@@ -19,56 +19,56 @@ unsafe extern "C" {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
pub fn fui_get_accent_color() -> u32 {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
#[cfg(any(target_family = "wasm", feature = "native-runtime"))]
|
|
23
|
+
{
|
|
24
|
+
unsafe { __host_fui_get_accent_color() }
|
|
25
|
+
}
|
|
26
|
+
#[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
|
|
27
|
+
{
|
|
28
|
+
unsafe { crate::ffi::fui_get_accent_color() }
|
|
29
|
+
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
pub fn fui_get_platform_family() -> u32 {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
#[cfg(any(target_family = "wasm", feature = "native-runtime"))]
|
|
34
|
+
{
|
|
35
|
+
unsafe { __host_fui_get_platform_family() }
|
|
36
|
+
}
|
|
37
|
+
#[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
|
|
38
|
+
{
|
|
39
|
+
unsafe { crate::ffi::fui_get_platform_family() }
|
|
40
|
+
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
pub fn fui_is_coarse_pointer() -> bool {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
#[cfg(any(target_family = "wasm", feature = "native-runtime"))]
|
|
45
|
+
{
|
|
46
|
+
unsafe { __host_fui_is_coarse_pointer() }
|
|
47
|
+
}
|
|
48
|
+
#[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
|
|
49
|
+
{
|
|
50
|
+
unsafe { crate::ffi::fui_is_coarse_pointer() }
|
|
51
|
+
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
pub fn fui_is_dark_mode() -> bool {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
#[cfg(any(target_family = "wasm", feature = "native-runtime"))]
|
|
56
|
+
{
|
|
57
|
+
unsafe { __host_fui_is_dark_mode() }
|
|
58
|
+
}
|
|
59
|
+
#[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
|
|
60
|
+
{
|
|
61
|
+
unsafe { crate::ffi::fui_is_dark_mode() }
|
|
62
|
+
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
pub fn fui_now_ms() -> f64 {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
#[cfg(any(target_family = "wasm", feature = "native-runtime"))]
|
|
67
|
+
{
|
|
68
|
+
unsafe { __host_fui_now_ms() }
|
|
69
|
+
}
|
|
70
|
+
#[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
|
|
71
|
+
{
|
|
72
|
+
crate::ffi::test::host_now_ms()
|
|
73
|
+
}
|
|
74
74
|
}
|
package/src/lib.rs
CHANGED
|
@@ -424,7 +424,7 @@ pub mod prelude {
|
|
|
424
424
|
Child, ContextMenuEventArgs, Corners, CustomDrawable, EdgeInsets, FlexBox, FlexBoxSurface,
|
|
425
425
|
GradientStop, Grid, GridTrack, HasFlexBoxRoot, Image, ImageNode, Length, Node,
|
|
426
426
|
PresenterHostStyle, ScrollBar, ScrollBarVisibility, ScrollBox, ScrollState, ScrollView,
|
|
427
|
-
Shadow, Svg, SvgNode, Text, TextCore, TextNode, VirtualList,
|
|
427
|
+
Shadow, Svg, SvgNode, Text, TextCore, TextNode, ThemeBindable, VirtualList,
|
|
428
428
|
};
|
|
429
429
|
pub use crate::persisted;
|
|
430
430
|
pub use crate::platform;
|
|
@@ -542,7 +542,7 @@ pub use node::{
|
|
|
542
542
|
ContextMenuEventArgs, Corners, CustomDrawable, EdgeInsets, FlexBox, FlexBoxSurface,
|
|
543
543
|
GradientStop, Grid, GridTrack, HasFlexBoxRoot, Image, ImageNode, Length, Node,
|
|
544
544
|
PresenterHostStyle, ScrollBar, ScrollBarVisibility, ScrollBox, ScrollState, ScrollView, Shadow,
|
|
545
|
-
Svg, SvgNode, Text, TextCore, TextNode, VirtualList,
|
|
545
|
+
Svg, SvgNode, Text, TextCore, TextNode, ThemeBindable, VirtualList,
|
|
546
546
|
};
|
|
547
547
|
pub use persisted::*;
|
|
548
548
|
pub use platform::*;
|
package/src/node/mod.rs
CHANGED
|
@@ -64,25 +64,43 @@ pub trait HasFlexBoxRoot {
|
|
|
64
64
|
fn flex_box_root(&self) -> &FlexBox;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
pub trait ThemeBindable: Sized + 'static {
|
|
68
|
+
#[doc(hidden)]
|
|
69
|
+
fn theme_binding_node(&self) -> NodeRef;
|
|
70
|
+
|
|
71
|
+
#[doc(hidden)]
|
|
72
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>>;
|
|
73
|
+
|
|
74
|
+
fn bind_theme(&self, handler: impl Fn(&Self, crate::theme::Theme) + 'static) -> &Self {
|
|
75
|
+
let target = self.weak_theme_target();
|
|
76
|
+
let guard = crate::theme::subscribe(move |theme| {
|
|
77
|
+
if let Some(control) = target() {
|
|
78
|
+
handler(&control, theme);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
self.theme_binding_node().retain_attachment(Rc::new(guard));
|
|
82
|
+
self
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
67
86
|
impl HasFlexBoxRoot for FlexBox {
|
|
68
87
|
fn flex_box_root(&self) -> &FlexBox {
|
|
69
88
|
self
|
|
70
89
|
}
|
|
71
90
|
}
|
|
72
91
|
|
|
73
|
-
|
|
74
|
-
fn
|
|
75
|
-
|
|
76
|
-
let weak_root = root.downgrade();
|
|
77
|
-
let guard = crate::theme::subscribe(move |theme| {
|
|
78
|
-
if let Some(root) = weak_root.upgrade() {
|
|
79
|
-
handler(&root, theme);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
root.retained_node_ref().retain_attachment(Rc::new(guard));
|
|
83
|
-
self
|
|
92
|
+
impl ThemeBindable for FlexBox {
|
|
93
|
+
fn theme_binding_node(&self) -> NodeRef {
|
|
94
|
+
self.retained_node_ref()
|
|
84
95
|
}
|
|
85
96
|
|
|
97
|
+
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
98
|
+
let weak = self.downgrade();
|
|
99
|
+
Box::new(move || weak.upgrade())
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
pub trait FlexBoxSurface: HasFlexBoxRoot {
|
|
86
104
|
fn width(&self, width: f32, unit: Unit) -> &Self {
|
|
87
105
|
self.flex_box_root().width(width, unit);
|
|
88
106
|
self
|