use cranpose_core::MutableState;
use cranpose_foundation::PointerEventKind;
use cranpose_macros::composable;
use cranpose_testing::ComposeTestRule;
use cranpose_ui::*;
#[composable]
fn hover_tracking_app(hover_position: MutableState<Point>, is_hovered: MutableState<bool>) {
Column(
Modifier::empty()
.padding(20.0)
.then(Modifier::empty().size(Size {
width: 200.0,
height: 200.0,
}))
.then(Modifier::empty().pointer_input((), {
let position = hover_position;
let hovered = is_hovered;
move |scope: PointerInputScope| async move {
scope
.await_pointer_event_scope(|await_scope| async move {
loop {
let event = await_scope.await_pointer_event().await;
match event.kind {
PointerEventKind::Move => {
position.set(Point {
x: event.position.x,
y: event.position.y,
});
hovered.set(true);
}
PointerEventKind::Cancel => {
hovered.set(false);
}
_ => {}
}
}
})
.await;
}
})),
ColumnSpec::default(),
|| {
Text(
"Hover area",
Modifier::empty().padding(8.0),
TextStyle::default(),
);
},
);
}
#[test]
fn test_pointer_input_async_handler_is_present() {
let _app_context = cranpose_ui::AppContext::new();
let _app_context_scope = _app_context.enter_scope();
_app_context.enter(cranpose_ui::reset_render_state_for_tests);
let mut rule = ComposeTestRule::new();
let runtime = rule.runtime_handle();
let hover_position = MutableState::with_runtime(Point { x: 0.0, y: 0.0 }, runtime.clone());
let is_hovered = MutableState::with_runtime(false, runtime.clone());
rule.set_content({
let pos = hover_position;
let hovered = is_hovered;
move || {
hover_tracking_app(pos, hovered);
}
})
.expect("initial render succeeds");
assert_eq!(hover_position.get().x, 0.0);
assert_eq!(hover_position.get().y, 0.0);
assert!(!is_hovered.get());
let node_count = rule.applier_mut().len();
assert!(
node_count >= 2,
"Should have at least 2 nodes (Column and Text)"
);
println!(
"✓ Pointer input composition created successfully with {} nodes",
node_count
);
}
#[composable]
fn button_with_modifiers_app(click_count: MutableState<i32>) {
Column(
Modifier::empty().padding(20.0),
ColumnSpec::default(),
move || {
Text(
format!("Clicks: {}", click_count.get()),
Modifier::empty().padding(8.0),
TextStyle::default(),
);
Button(
Modifier::empty()
.rounded_corners(12.0)
.then(Modifier::empty().draw_behind(|scope| {
scope.draw_round_rect(
Brush::solid(Color(0.2, 0.45, 0.9, 1.0)),
CornerRadii::uniform(12.0),
);
})),
ButtonSpec::default(),
{
let count = click_count;
move || {
count.set(count.get() + 1);
}
},
|| {
Text(
"Click me",
Modifier::empty().padding(4.0),
TextStyle::default(),
);
},
);
},
);
}
#[test]
fn test_button_with_draw_modifiers_is_clickable() {
let _app_context = cranpose_ui::AppContext::new();
let _app_context_scope = _app_context.enter_scope();
_app_context.enter(cranpose_ui::reset_render_state_for_tests);
let mut rule = ComposeTestRule::new();
let runtime = rule.runtime_handle();
let click_count = MutableState::with_runtime(0, runtime.clone());
rule.set_content({
let count = click_count;
move || {
button_with_modifiers_app(count);
}
})
.expect("initial render succeeds");
assert_eq!(click_count.get(), 0);
let node_count = rule.applier_mut().len();
assert!(
node_count >= 3,
"Should have at least 3 nodes (Column, Text, Button)"
);
println!(
"✓ Button with draw modifiers created successfully with {} nodes",
node_count
);
}
#[composable]
fn dynamic_label_button_app(click_count: MutableState<i32>, is_active: MutableState<bool>) {
Column(
Modifier::empty().padding(20.0),
ColumnSpec::default(),
move || {
let active = is_active.get();
let label = if active { "Active" } else { "Inactive" };
Button(
Modifier::empty().padding(10.0),
ButtonSpec::default(),
{
let count = click_count;
move || {
is_active.set(!is_active.get());
count.set(count.get() + 1);
}
},
{
let label_str = label.to_string();
move || {
Text(
label_str.clone(),
Modifier::empty().padding(4.0),
TextStyle::default(),
);
}
},
);
},
);
}
#[test]
fn test_button_with_dynamic_content_updates_correctly() {
let _app_context = cranpose_ui::AppContext::new();
let _app_context_scope = _app_context.enter_scope();
_app_context.enter(cranpose_ui::reset_render_state_for_tests);
let mut rule = ComposeTestRule::new();
let runtime = rule.runtime_handle();
let click_count = MutableState::with_runtime(0, runtime.clone());
let is_active = MutableState::with_runtime(false, runtime.clone());
rule.set_content({
let count = click_count;
let active = is_active;
move || {
dynamic_label_button_app(count, active);
}
})
.expect("initial render succeeds");
assert_eq!(click_count.get(), 0);
assert!(!is_active.get());
is_active.set(true);
click_count.set(1);
rule.pump_until_idle()
.expect("recompose after state change");
assert_eq!(click_count.get(), 1);
assert!(is_active.get());
is_active.set(false);
click_count.set(2);
rule.pump_until_idle()
.expect("recompose after second toggle");
assert_eq!(click_count.get(), 2);
assert!(!is_active.get());
println!("✓ Button with dynamic content updates correctly through state changes");
}