numeric_stepper.rs

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