cranpose_ui/widgets/
button.rs1#![allow(non_snake_case)]
4
5use crate::composable;
6use crate::interaction::MutableInteractionSource;
7use crate::layout::policies::FlexMeasurePolicy;
8use crate::modifier::Modifier;
9use crate::widgets::Layout;
10use cranpose_core::NodeId;
11use cranpose_ui_layout::{HorizontalAlignment, LinearArrangement};
12use std::cell::RefCell;
13use std::rc::Rc;
14
15#[derive(Clone, Debug, Default, PartialEq)]
16pub struct ButtonSpec {
17 pub interaction_source: Option<MutableInteractionSource>,
18}
19
20impl ButtonSpec {
21 pub fn new() -> Self {
22 Self::default()
23 }
24
25 pub fn interaction_source(mut self, interaction_source: MutableInteractionSource) -> Self {
26 self.interaction_source = Some(interaction_source);
27 self
28 }
29}
30
31fn button_modifier<F>(modifier: Modifier, spec: ButtonSpec, on_click: F) -> Modifier
32where
33 F: FnMut() + 'static,
34{
35 let on_click_rc: Rc<RefCell<dyn FnMut()>> = Rc::new(RefCell::new(on_click));
36 let modifier = if let Some(interaction_source) = spec.interaction_source {
37 modifier.press_interaction_source(interaction_source)
38 } else {
39 modifier
40 };
41
42 modifier.clickable(move |_point| {
43 (on_click_rc.borrow_mut())();
44 })
45}
46
47#[composable]
71pub fn Button<F, G>(modifier: Modifier, spec: ButtonSpec, on_click: F, content: G) -> NodeId
72where
73 F: FnMut() + 'static,
74 G: FnMut() + 'static,
75{
76 Layout(
77 button_modifier(modifier, spec, on_click),
78 FlexMeasurePolicy::column(
79 LinearArrangement::Center,
80 HorizontalAlignment::CenterHorizontally,
81 ),
82 content,
83 )
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use cranpose_core::{Composition, MemoryApplier};
90
91 #[test]
92 fn default_button_spec_has_no_interaction_source() {
93 let spec = ButtonSpec::default();
94
95 assert!(spec.interaction_source.is_none());
96 }
97
98 #[test]
99 fn button_spec_builder_preserves_interaction_source() {
100 let composition = Composition::new(MemoryApplier::new());
101 let source = MutableInteractionSource::with_runtime(composition.runtime_handle());
102 let spec = ButtonSpec::new().interaction_source(source.clone());
103
104 assert_eq!(spec.interaction_source, Some(source));
105 }
106}