Skip to main content

cranpose_ui/widgets/
button.rs

1//! Button widget implementation
2
3#![allow(non_snake_case)]
4
5use crate::composable;
6use crate::layout::policies::FlexMeasurePolicy;
7use crate::modifier::Modifier;
8use crate::widgets::Layout;
9use cranpose_core::NodeId;
10use cranpose_ui_layout::{HorizontalAlignment, LinearArrangement};
11
12/// A clickable button with a background and content.
13///
14/// # When to use
15/// Use this to trigger an action when clicked. The button serves as a container
16/// for other composables (typically `Text`).
17///
18/// # Arguments
19///
20/// * `modifier` - Modifiers to apply to the button container (e.g., size, padding).
21/// * `on_click` - The callback to execute when the button is clicked.
22/// * `content` - The content to display inside the button (e.g., `Text` or `Icon`).
23///
24/// # Example
25///
26/// ```rust,ignore
27/// Button(
28///     Modifier::padding(8.0),
29///     || println!("Clicked!"),
30///     || Text("Click Me", Modifier::empty())
31/// );
32/// ```
33#[composable]
34pub fn Button<F, G>(modifier: Modifier, on_click: F, content: G) -> NodeId
35where
36    F: FnMut() + 'static,
37    G: FnMut() + 'static,
38{
39    use std::cell::RefCell;
40    use std::rc::Rc;
41
42    // Wrap the on_click handler in Rc<RefCell<>> to make it callable from Fn closure
43    let on_click_rc: Rc<RefCell<dyn FnMut()>> = Rc::new(RefCell::new(on_click));
44
45    // Add clickable modifier to handle click events
46    let clickable_modifier = modifier.clickable(move |_point| {
47        (on_click_rc.borrow_mut())();
48    });
49
50    // Use Layout with FlexMeasurePolicy (column) to arrange button content
51    // This matches how Button is implemented in Jetpack Compose
52    Layout(
53        clickable_modifier,
54        FlexMeasurePolicy::column(
55            LinearArrangement::Center,
56            HorizontalAlignment::CenterHorizontally,
57        ),
58        content,
59    )
60}