Closes #ISSUE Allows tabbing through everything in all three pages. Until #35075 is merged it is not possible to actually "click" tab focused buttons with the keyboard. Additionally adds an action `onboarding::Finish` and displays the keybind. The action corresponds to both the "Skip all" and "Start Building" buttons, with the keybind displayed similar to how it is for the page nav buttons Release Notes: - N/A *or* Added/Fixed/Improved ... --------- Co-authored-by: MrSubidubi <finn@zed.dev>
238 lines
9.1 KiB
Rust
238 lines
9.1 KiB
Rust
use gpui::ClickEvent;
|
|
|
|
use crate::{IconButtonShape, prelude::*};
|
|
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub enum NumericStepperStyle {
|
|
Outlined,
|
|
#[default]
|
|
Ghost,
|
|
}
|
|
|
|
#[derive(IntoElement, RegisterComponent)]
|
|
pub struct NumericStepper {
|
|
id: ElementId,
|
|
value: SharedString,
|
|
style: NumericStepperStyle,
|
|
on_decrement: Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>,
|
|
on_increment: Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>,
|
|
/// Whether to reserve space for the reset button.
|
|
reserve_space_for_reset: bool,
|
|
on_reset: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
|
|
tab_index: Option<isize>,
|
|
}
|
|
|
|
impl NumericStepper {
|
|
pub fn new(
|
|
id: impl Into<ElementId>,
|
|
value: impl Into<SharedString>,
|
|
on_decrement: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
|
|
on_increment: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
|
|
) -> Self {
|
|
Self {
|
|
id: id.into(),
|
|
value: value.into(),
|
|
style: NumericStepperStyle::default(),
|
|
on_decrement: Box::new(on_decrement),
|
|
on_increment: Box::new(on_increment),
|
|
reserve_space_for_reset: false,
|
|
on_reset: None,
|
|
tab_index: None,
|
|
}
|
|
}
|
|
|
|
pub fn style(mut self, style: NumericStepperStyle) -> Self {
|
|
self.style = style;
|
|
self
|
|
}
|
|
|
|
pub fn reserve_space_for_reset(mut self, reserve_space_for_reset: bool) -> Self {
|
|
self.reserve_space_for_reset = reserve_space_for_reset;
|
|
self
|
|
}
|
|
|
|
pub fn on_reset(
|
|
mut self,
|
|
on_reset: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
|
|
) -> Self {
|
|
self.on_reset = Some(Box::new(on_reset));
|
|
self
|
|
}
|
|
|
|
pub fn tab_index(mut self, tab_index: isize) -> Self {
|
|
self.tab_index = Some(tab_index);
|
|
self
|
|
}
|
|
}
|
|
|
|
impl RenderOnce for NumericStepper {
|
|
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
|
let shape = IconButtonShape::Square;
|
|
let icon_size = IconSize::Small;
|
|
|
|
let is_outlined = matches!(self.style, NumericStepperStyle::Outlined);
|
|
let mut tab_index = self.tab_index;
|
|
|
|
h_flex()
|
|
.id(self.id)
|
|
.gap_1()
|
|
.map(|element| {
|
|
if let Some(on_reset) = self.on_reset {
|
|
element.child(
|
|
IconButton::new("reset", IconName::RotateCcw)
|
|
.shape(shape)
|
|
.icon_size(icon_size)
|
|
.when_some(tab_index.as_mut(), |this, tab_index| {
|
|
*tab_index += 1;
|
|
this.tab_index(*tab_index - 1)
|
|
})
|
|
.on_click(on_reset),
|
|
)
|
|
} else if self.reserve_space_for_reset {
|
|
element.child(
|
|
h_flex()
|
|
.size(icon_size.square(window, cx))
|
|
.flex_none()
|
|
.into_any_element(),
|
|
)
|
|
} else {
|
|
element
|
|
}
|
|
})
|
|
.child(
|
|
h_flex()
|
|
.gap_1()
|
|
.rounded_sm()
|
|
.map(|this| {
|
|
if is_outlined {
|
|
this.overflow_hidden()
|
|
.bg(cx.theme().colors().surface_background)
|
|
.border_1()
|
|
.border_color(cx.theme().colors().border_variant)
|
|
} else {
|
|
this.px_1().bg(cx.theme().colors().editor_background)
|
|
}
|
|
})
|
|
.map(|decrement| {
|
|
if is_outlined {
|
|
decrement.child(
|
|
h_flex()
|
|
.id("decrement_button")
|
|
.p_1p5()
|
|
.size_full()
|
|
.justify_center()
|
|
.hover(|s| s.bg(cx.theme().colors().element_hover))
|
|
.border_r_1()
|
|
.border_color(cx.theme().colors().border_variant)
|
|
.child(Icon::new(IconName::Dash).size(IconSize::Small))
|
|
.when_some(tab_index.as_mut(), |this, tab_index| {
|
|
*tab_index += 1;
|
|
this.tab_index(*tab_index - 1).focus(|style| {
|
|
style.bg(cx.theme().colors().element_hover)
|
|
})
|
|
})
|
|
.on_click(self.on_decrement),
|
|
)
|
|
} else {
|
|
decrement.child(
|
|
IconButton::new("decrement", IconName::Dash)
|
|
.shape(shape)
|
|
.icon_size(icon_size)
|
|
.when_some(tab_index.as_mut(), |this, tab_index| {
|
|
*tab_index += 1;
|
|
this.tab_index(*tab_index - 1)
|
|
})
|
|
.on_click(self.on_decrement),
|
|
)
|
|
}
|
|
})
|
|
.child(Label::new(self.value).mx_3())
|
|
.map(|increment| {
|
|
if is_outlined {
|
|
increment.child(
|
|
h_flex()
|
|
.id("increment_button")
|
|
.p_1p5()
|
|
.size_full()
|
|
.justify_center()
|
|
.hover(|s| s.bg(cx.theme().colors().element_hover))
|
|
.border_l_1()
|
|
.border_color(cx.theme().colors().border_variant)
|
|
.child(Icon::new(IconName::Plus).size(IconSize::Small))
|
|
.when_some(tab_index.as_mut(), |this, tab_index| {
|
|
*tab_index += 1;
|
|
this.tab_index(*tab_index - 1).focus(|style| {
|
|
style.bg(cx.theme().colors().element_hover)
|
|
})
|
|
})
|
|
.on_click(self.on_increment),
|
|
)
|
|
} else {
|
|
increment.child(
|
|
IconButton::new("increment", IconName::Dash)
|
|
.shape(shape)
|
|
.icon_size(icon_size)
|
|
.when_some(tab_index.as_mut(), |this, tab_index| {
|
|
*tab_index += 1;
|
|
this.tab_index(*tab_index - 1)
|
|
})
|
|
.on_click(self.on_increment),
|
|
)
|
|
}
|
|
}),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl Component for NumericStepper {
|
|
fn scope() -> ComponentScope {
|
|
ComponentScope::Input
|
|
}
|
|
|
|
fn name() -> &'static str {
|
|
"Numeric Stepper"
|
|
}
|
|
|
|
fn sort_name() -> &'static str {
|
|
Self::name()
|
|
}
|
|
|
|
fn description() -> Option<&'static str> {
|
|
Some("A button used to increment or decrement a numeric value.")
|
|
}
|
|
|
|
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
|
|
Some(
|
|
v_flex()
|
|
.gap_6()
|
|
.children(vec![example_group_with_title(
|
|
"Styles",
|
|
vec![
|
|
single_example(
|
|
"Default",
|
|
NumericStepper::new(
|
|
"numeric-stepper-component-preview",
|
|
"10",
|
|
move |_, _, _| {},
|
|
move |_, _, _| {},
|
|
)
|
|
.into_any_element(),
|
|
),
|
|
single_example(
|
|
"Outlined",
|
|
NumericStepper::new(
|
|
"numeric-stepper-with-border-component-preview",
|
|
"10",
|
|
move |_, _, _| {},
|
|
move |_, _, _| {},
|
|
)
|
|
.style(NumericStepperStyle::Outlined)
|
|
.into_any_element(),
|
|
),
|
|
],
|
|
)])
|
|
.into_any_element(),
|
|
)
|
|
}
|
|
}
|