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