numeric_stepper.rs

 1use gpui::ClickEvent;
 2
 3use crate::{prelude::*, IconButtonShape};
 4
 5#[derive(IntoElement)]
 6pub struct NumericStepper {
 7    value: SharedString,
 8    on_decrement: Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
 9    on_increment: Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
10    /// Whether to reserve space for the reset button.
11    reserve_space_for_reset: bool,
12    on_reset: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
13}
14
15impl NumericStepper {
16    pub fn new(
17        value: impl Into<SharedString>,
18        on_decrement: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
19        on_increment: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
20    ) -> Self {
21        Self {
22            value: value.into(),
23            on_decrement: Box::new(on_decrement),
24            on_increment: Box::new(on_increment),
25            reserve_space_for_reset: false,
26            on_reset: None,
27        }
28    }
29
30    pub fn reserve_space_for_reset(mut self, reserve_space_for_reset: bool) -> Self {
31        self.reserve_space_for_reset = reserve_space_for_reset;
32        self
33    }
34
35    pub fn on_reset(
36        mut self,
37        on_reset: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
38    ) -> Self {
39        self.on_reset = Some(Box::new(on_reset));
40        self
41    }
42}
43
44impl RenderOnce for NumericStepper {
45    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
46        let shape = IconButtonShape::Square;
47        let icon_size = IconSize::Small;
48
49        h_flex()
50            .gap_1()
51            .map(|element| {
52                if let Some(on_reset) = self.on_reset {
53                    element.child(
54                        IconButton::new("reset", IconName::RotateCcw)
55                            .shape(shape)
56                            .icon_size(icon_size)
57                            .on_click(on_reset),
58                    )
59                } else if self.reserve_space_for_reset {
60                    element.child(
61                        h_flex()
62                            .size(icon_size.square(cx))
63                            .flex_none()
64                            .into_any_element(),
65                    )
66                } else {
67                    element
68                }
69            })
70            .child(
71                h_flex()
72                    .gap_1()
73                    .px_1()
74                    .rounded_sm()
75                    .bg(cx.theme().colors().editor_background)
76                    .child(
77                        IconButton::new("decrement", IconName::Dash)
78                            .shape(shape)
79                            .icon_size(icon_size)
80                            .on_click(self.on_decrement),
81                    )
82                    .child(Label::new(self.value))
83                    .child(
84                        IconButton::new("increment", IconName::Plus)
85                            .shape(shape)
86                            .icon_size(icon_size)
87                            .on_click(self.on_increment),
88                    ),
89            )
90    }
91}