279 lines
7.3 KiB
Rust
279 lines
7.3 KiB
Rust
use zero_ui::prelude::new_property::*;
|
|
|
|
/// Helper for declaring properties that set the widget state.
|
|
///
|
|
/// The state key is set in [`widget_state`](WidgetContext::widget_state) on init and is kept updated.
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// # fn main() -> () { }
|
|
/// use zero_ui::core::{property, context::{state_key, WidgetContext}, var::IntoVar, UiNode, Widget};
|
|
/// use zero_ui::properties::set_widget_state;
|
|
///
|
|
/// state_key! {
|
|
/// pub struct FooKey: u32;
|
|
/// }
|
|
///
|
|
/// #[property(context)]
|
|
/// pub fn foo(child: impl UiNode, value: impl IntoVar<u32>) -> impl UiNode {
|
|
/// set_widget_state(child, FooKey, value)
|
|
/// }
|
|
///
|
|
/// // after the property is used and the widget initializes:
|
|
///
|
|
/// /// Get the value from outside the widget.
|
|
/// fn get_foo_outer(widget: &impl Widget) -> u32 {
|
|
/// widget.state().get(FooKey).copied().unwrap_or_default()
|
|
/// }
|
|
///
|
|
/// /// Get the value from inside the widget.
|
|
/// fn get_foo_inner(ctx: &WidgetContext) -> u32 {
|
|
/// ctx.widget_state.get(FooKey).copied().unwrap_or_default()
|
|
/// }
|
|
/// ```
|
|
pub fn set_widget_state<U, K, V>(child: U, key: K, value: V) -> impl UiNode
|
|
where
|
|
U: UiNode,
|
|
K: StateKey,
|
|
K::Type: VarValue,
|
|
V: IntoVar<K::Type>,
|
|
{
|
|
struct SetWidgetStateNode<U, K, V> {
|
|
child: U,
|
|
key: K,
|
|
var: V,
|
|
}
|
|
#[impl_ui_node(child)]
|
|
impl<U, K, V> UiNode for SetWidgetStateNode<U, K, V>
|
|
where
|
|
U: UiNode,
|
|
K: StateKey,
|
|
K::Type: VarValue,
|
|
V: Var<K::Type>,
|
|
{
|
|
fn init(&mut self, ctx: &mut WidgetContext) {
|
|
ctx.widget_state.set(self.key, self.var.get(ctx).clone());
|
|
self.child.init(ctx);
|
|
}
|
|
|
|
fn update(&mut self, ctx: &mut WidgetContext) {
|
|
if let Some(new) = self.var.clone_new(ctx) {
|
|
ctx.widget_state.set(self.key, new);
|
|
}
|
|
self.child.update(ctx);
|
|
}
|
|
}
|
|
SetWidgetStateNode {
|
|
child,
|
|
key,
|
|
var: value.into_var(),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::core::{
|
|
context::TestWidgetContext,
|
|
var::{var, IntoVar},
|
|
UiNode,
|
|
};
|
|
use crate::widgets::{blank, container, layouts::v_stack};
|
|
|
|
state_key! {
|
|
struct TestKey: u8;
|
|
}
|
|
|
|
#[property(context)]
|
|
fn set_state_test(child: impl UiNode, value: impl IntoVar<u8>) -> impl UiNode {
|
|
set_widget_state(child, TestKey, value)
|
|
}
|
|
|
|
#[test]
|
|
fn set_widget_state_init_and_update() {
|
|
let value = var(2);
|
|
let mut wgt = blank! {
|
|
set_state_test = value.clone();
|
|
};
|
|
let mut ctx = TestWidgetContext::new();
|
|
|
|
assert_eq!(None, wgt.state().get(TestKey));
|
|
|
|
wgt.test_init(&mut ctx);
|
|
assert_eq!(Some(&2), wgt.state().get(TestKey));
|
|
value.set(&ctx.vars, 4);
|
|
ctx.apply_updates();
|
|
wgt.test_update(&mut ctx);
|
|
assert_eq!(Some(&4), wgt.state().get(TestKey));
|
|
}
|
|
|
|
context_var! {
|
|
struct TestVar: u8 = const 1;
|
|
}
|
|
|
|
#[property(context)]
|
|
fn with_var_test(child: impl UiNode, value: impl IntoVar<u8>) -> impl UiNode {
|
|
with_context_var(child, TestVar, value)
|
|
}
|
|
|
|
#[property(context)]
|
|
fn with_var_wgt_only_test(child: impl UiNode, value: impl IntoVar<u8>) -> impl UiNode {
|
|
with_context_var_wgt_only(child, TestVar, value)
|
|
}
|
|
|
|
#[property(inner)]
|
|
fn test_var_probe(child: impl UiNode, value: impl Var<u8>) -> impl UiNode {
|
|
struct TestVarProbeNode<C, V> {
|
|
child: C,
|
|
value: V,
|
|
}
|
|
#[impl_ui_node(child)]
|
|
impl<C: UiNode, V: Var<u8>> UiNode for TestVarProbeNode<C, V> {
|
|
fn init(&mut self, ctx: &mut WidgetContext) {
|
|
self.child.init(ctx);
|
|
self.value.set(ctx.vars, *TestVar::get(ctx)).expect("probe var is read-only");
|
|
}
|
|
|
|
fn update(&mut self, ctx: &mut WidgetContext) {
|
|
self.child.update(ctx);
|
|
if let Some(&new) = TestVar::get_new(ctx) {
|
|
self.value.set(ctx.vars, new).expect("probe var is read-only");
|
|
}
|
|
}
|
|
}
|
|
TestVarProbeNode { child, value }
|
|
}
|
|
|
|
#[test]
|
|
fn with_context_var_same_widget() {
|
|
let value = var(2);
|
|
let probe = var(0);
|
|
let mut wgt = blank! {
|
|
with_var_test = value.clone();
|
|
test_var_probe = probe.clone();
|
|
};
|
|
let mut ctx = TestWidgetContext::new();
|
|
|
|
wgt.test_init(&mut ctx);
|
|
ctx.apply_updates();
|
|
|
|
assert_eq!(&2, probe.get(&ctx.vars));
|
|
|
|
value.set(&ctx.vars, 3);
|
|
ctx.apply_updates();
|
|
wgt.test_update(&mut ctx);
|
|
ctx.apply_updates();
|
|
|
|
assert_eq!(&3, probe.get(&ctx.vars));
|
|
}
|
|
|
|
#[test]
|
|
fn with_context_var_inner_widget() {
|
|
let value = var(2);
|
|
let probe = var(0);
|
|
|
|
let mut wgt = container! {
|
|
content = blank! {
|
|
test_var_probe = probe.clone();
|
|
};
|
|
with_var_test = value.clone();
|
|
};
|
|
|
|
let mut ctx = TestWidgetContext::new();
|
|
|
|
wgt.test_init(&mut ctx);
|
|
ctx.apply_updates();
|
|
|
|
assert_eq!(&2, probe.get(&ctx.vars));
|
|
|
|
value.set(&ctx.vars, 3);
|
|
ctx.apply_updates();
|
|
wgt.test_update(&mut ctx);
|
|
ctx.apply_updates();
|
|
|
|
assert_eq!(&3, probe.get(&ctx.vars));
|
|
}
|
|
|
|
#[test]
|
|
fn with_context_var_sibling_not_affected() {
|
|
let value = var(2);
|
|
let probe = var(0);
|
|
|
|
let mut wgt = v_stack! {
|
|
items = widgets![
|
|
blank! {
|
|
with_var_test = value.clone();
|
|
},
|
|
blank! {
|
|
test_var_probe = probe.clone();
|
|
}
|
|
];
|
|
};
|
|
let mut ctx = TestWidgetContext::new();
|
|
|
|
wgt.test_init(&mut ctx);
|
|
ctx.apply_updates();
|
|
|
|
// `1` is the default value.
|
|
assert_eq!(&1, probe.get(&ctx.vars));
|
|
|
|
value.set(&ctx.vars, 3);
|
|
ctx.apply_updates();
|
|
wgt.test_update(&mut ctx);
|
|
ctx.apply_updates();
|
|
|
|
assert_eq!(&1, probe.get(&ctx.vars));
|
|
}
|
|
|
|
#[test]
|
|
fn with_context_var_wgt_only_same_widget() {
|
|
let value = var(2);
|
|
let probe = var(0);
|
|
let mut wgt = blank! {
|
|
with_var_wgt_only_test = value.clone();
|
|
test_var_probe = probe.clone();
|
|
};
|
|
let mut ctx = TestWidgetContext::new();
|
|
|
|
wgt.test_init(&mut ctx);
|
|
ctx.apply_updates();
|
|
|
|
assert_eq!(&2, probe.get(&ctx.vars));
|
|
|
|
value.set(&ctx.vars, 3);
|
|
ctx.apply_updates();
|
|
wgt.test_update(&mut ctx);
|
|
ctx.apply_updates();
|
|
|
|
assert_eq!(&3, probe.get(&ctx.vars));
|
|
}
|
|
|
|
#[test]
|
|
fn with_context_var_wgt_only_inner_widget_not_affected() {
|
|
let value = var(2);
|
|
let probe = var(0);
|
|
|
|
let mut wgt = container! {
|
|
content = blank! {
|
|
test_var_probe = probe.clone();
|
|
};
|
|
with_var_wgt_only_test = value.clone();
|
|
};
|
|
|
|
let mut ctx = TestWidgetContext::new();
|
|
|
|
wgt.test_init(&mut ctx);
|
|
ctx.apply_updates();
|
|
|
|
// `1` is the default value.
|
|
assert_eq!(&1, probe.get(&ctx.vars));
|
|
|
|
value.set(&ctx.vars, 3);
|
|
ctx.apply_updates();
|
|
wgt.test_update(&mut ctx);
|
|
ctx.apply_updates();
|
|
|
|
assert_eq!(&1, probe.get(&ctx.vars));
|
|
}
|
|
}
|