ui: Rename and simplify NumberField component (#39731)

This commit is contained in:
Danilo Leal
2025-10-07 21:35:51 -03:00
committed by GitHub
parent 8bf37dd130
commit d615525771
5 changed files with 111 additions and 183 deletions
@@ -9,25 +9,16 @@ use editor::{Editor, EditorStyle};
use gpui::{ClickEvent, Entity, FocusHandle, Focusable, FontWeight, Modifiers};
use settings::{CodeFade, MinimumContrast};
use ui::{IconButtonShape, prelude::*};
use ui::prelude::*;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub enum NumericStepperStyle {
Outlined,
#[default]
Ghost,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub enum NumericStepperMode {
pub enum NumberFieldMode {
#[default]
Read,
Edit,
}
pub trait NumericStepperType:
Display + Copy + Clone + Sized + PartialOrd + FromStr + 'static
{
pub trait NumberFieldType: Display + Copy + Clone + Sized + PartialOrd + FromStr + 'static {
fn default_format(value: &Self) -> String {
format!("{}", value)
}
@@ -40,7 +31,7 @@ pub trait NumericStepperType:
fn saturating_sub(self, rhs: Self) -> Self;
}
impl NumericStepperType for gpui::FontWeight {
impl NumberFieldType for gpui::FontWeight {
fn default_step() -> Self {
FontWeight(10.0)
}
@@ -64,7 +55,7 @@ impl NumericStepperType for gpui::FontWeight {
}
}
impl NumericStepperType for settings::CodeFade {
impl NumberFieldType for settings::CodeFade {
fn default_step() -> Self {
CodeFade(0.10)
}
@@ -88,7 +79,7 @@ impl NumericStepperType for settings::CodeFade {
}
}
impl NumericStepperType for settings::MinimumContrast {
impl NumberFieldType for settings::MinimumContrast {
fn default_step() -> Self {
MinimumContrast(1.0)
}
@@ -114,7 +105,7 @@ impl NumericStepperType for settings::MinimumContrast {
macro_rules! impl_numeric_stepper_int {
($type:ident) => {
impl NumericStepperType for $type {
impl NumberFieldType for $type {
fn default_step() -> Self {
1
}
@@ -148,7 +139,7 @@ macro_rules! impl_numeric_stepper_int {
macro_rules! impl_numeric_stepper_nonzero_int {
($nonzero:ty, $inner:ty) => {
impl NumericStepperType for $nonzero {
impl NumberFieldType for $nonzero {
fn default_step() -> Self {
<$nonzero>::new(1).unwrap()
}
@@ -184,7 +175,7 @@ macro_rules! impl_numeric_stepper_nonzero_int {
macro_rules! impl_numeric_stepper_float {
($type:ident) => {
impl NumericStepperType for $type {
impl NumberFieldType for $type {
fn default_format(value: &Self) -> String {
format!("{:.2}", value)
}
@@ -234,12 +225,11 @@ impl_numeric_stepper_nonzero_int!(NonZeroU64, u64);
impl_numeric_stepper_nonzero_int!(NonZero<usize>, usize);
#[derive(RegisterComponent)]
pub struct NumericStepper<T = usize> {
pub struct NumberField<T = usize> {
id: ElementId,
value: T,
style: NumericStepperStyle,
focus_handle: FocusHandle,
mode: Entity<NumericStepperMode>,
mode: Entity<NumberFieldMode>,
format: Box<dyn FnOnce(&T) -> String>,
large_step: T,
small_step: T,
@@ -251,12 +241,12 @@ pub struct NumericStepper<T = usize> {
tab_index: Option<isize>,
}
impl<T: NumericStepperType> NumericStepper<T> {
impl<T: NumberFieldType> NumberField<T> {
pub fn new(id: impl Into<ElementId>, value: T, window: &mut Window, cx: &mut App) -> Self {
let id = id.into();
let (mode, focus_handle) = window.with_id(id.clone(), |window| {
let mode = window.use_state(cx, |_, _| NumericStepperMode::default());
let mode = window.use_state(cx, |_, _| NumberFieldMode::default());
let focus_handle = window.use_state(cx, |_, cx| cx.focus_handle());
(mode, focus_handle)
});
@@ -266,7 +256,6 @@ impl<T: NumericStepperType> NumericStepper<T> {
mode,
value,
focus_handle: focus_handle.read(cx).clone(),
style: NumericStepperStyle::default(),
format: Box::new(T::default_format),
large_step: T::large_step(),
step: T::default_step(),
@@ -309,11 +298,6 @@ impl<T: NumericStepperType> NumericStepper<T> {
self
}
pub fn style(mut self, style: NumericStepperStyle) -> Self {
self.style = style;
self
}
pub fn on_reset(
mut self,
on_reset: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
@@ -333,7 +317,7 @@ impl<T: NumericStepperType> NumericStepper<T> {
}
}
impl<T: NumericStepperType> IntoElement for NumericStepper<T> {
impl<T: NumberFieldType> IntoElement for NumberField<T> {
type Element = gpui::Component<Self>;
fn into_element(self) -> Self::Element {
@@ -341,12 +325,8 @@ impl<T: NumericStepperType> IntoElement for NumericStepper<T> {
}
}
impl<T: NumericStepperType> RenderOnce for NumericStepper<T> {
impl<T: NumberFieldType> RenderOnce for NumberField<T> {
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;
let get_step = {
@@ -371,8 +351,7 @@ impl<T: NumericStepperType> RenderOnce for NumericStepper<T> {
.when_some(self.on_reset, |this, on_reset| {
this.child(
IconButton::new("reset", IconName::RotateCcw)
.shape(shape)
.icon_size(icon_size)
.icon_size(IconSize::Small)
.when_some(tab_index.as_mut(), |this, tab_index| {
*tab_index += 1;
this.tab_index(*tab_index - 1)
@@ -382,7 +361,6 @@ impl<T: NumericStepperType> RenderOnce for NumericStepper<T> {
})
.child(
h_flex()
.when(!is_outlined, |this| this.gap_1())
.map(|decrement| {
let decrement_handler = {
let value = self.value;
@@ -397,57 +375,42 @@ impl<T: NumericStepperType> RenderOnce for NumericStepper<T> {
}
};
if is_outlined {
decrement.child(
h_flex()
.id("decrement_button")
.cursor(gpui::CursorStyle::PointingHand)
.p_1p5()
.size_full()
.justify_center()
.overflow_hidden()
.rounded_tl_sm()
.rounded_bl_sm()
.border_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().surface_background)
.hover(|s| s.bg(cx.theme().colors().element_hover))
.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
.border_color(cx.theme().colors().border_focused)
.bg(cx.theme().colors().element_hover)
})
decrement.child(
h_flex()
.id("decrement_button")
.cursor(gpui::CursorStyle::PointingHand)
.p_1p5()
.size_full()
.justify_center()
.overflow_hidden()
.rounded_tl_sm()
.rounded_bl_sm()
.border_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().surface_background)
.hover(|s| s.bg(cx.theme().colors().element_hover))
.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
.border_color(cx.theme().colors().border_focused)
.bg(cx.theme().colors().element_hover)
})
.on_click(decrement_handler),
)
} 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(decrement_handler),
)
}
})
.on_click(decrement_handler),
)
})
.child(
h_flex()
.min_w_16()
.size_full()
.when(is_outlined, |this| {
this.border_y_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().surface_background)
})
.border_y_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().surface_background)
.in_focus(|this| this.border_color(cx.theme().colors().border_focused))
.child(match *self.mode.read(cx) {
NumericStepperMode::Read => h_flex()
NumberFieldMode::Read => h_flex()
.id("numeric_stepper_label")
.px_1()
.flex_1()
@@ -466,12 +429,12 @@ impl<T: NumericStepperType> RenderOnce for NumericStepper<T> {
move |click, _, _cx| {
if click.click_count() == 2 || click.is_keyboard() {
// Edit mode is disabled until we implement center text alignment for editor
// mode.write(cx, NumericStepperMode::Edit);
// mode.write(cx, NumberFieldMode::Edit);
}
}
})
.into_any_element(),
NumericStepperMode::Edit => h_flex()
NumberFieldMode::Edit => h_flex()
.flex_1()
.child(window.use_state(cx, {
|window, cx| {
@@ -506,7 +469,7 @@ impl<T: NumericStepperType> RenderOnce for NumericStepper<T> {
}
on_change(&new_value, window, cx);
};
mode.write(cx, NumericStepperMode::Read);
mode.write(cx, NumberFieldMode::Read);
}
})
.detach();
@@ -537,56 +500,43 @@ impl<T: NumericStepperType> RenderOnce for NumericStepper<T> {
}
};
if is_outlined {
increment.child(
h_flex()
.id("increment_button")
.cursor(gpui::CursorStyle::PointingHand)
.p_1p5()
.size_full()
.justify_center()
.overflow_hidden()
.rounded_tr_sm()
.rounded_br_sm()
.border_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().surface_background)
.hover(|s| s.bg(cx.theme().colors().element_hover))
.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
.border_color(cx.theme().colors().border_focused)
.bg(cx.theme().colors().element_hover)
})
increment.child(
h_flex()
.id("increment_button")
.cursor(gpui::CursorStyle::PointingHand)
.p_1p5()
.size_full()
.justify_center()
.overflow_hidden()
.rounded_tr_sm()
.rounded_br_sm()
.border_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().surface_background)
.hover(|s| s.bg(cx.theme().colors().element_hover))
.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
.border_color(cx.theme().colors().border_focused)
.bg(cx.theme().colors().element_hover)
})
.on_click(increment_handler),
)
} else {
increment.child(
IconButton::new("increment", IconName::Plus)
.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(increment_handler),
)
}
})
.on_click(increment_handler),
)
}),
)
}
}
impl Component for NumericStepper<usize> {
impl Component for NumberField<usize> {
fn scope() -> ComponentScope {
ComponentScope::Input
}
fn name() -> &'static str {
"Numeric Stepper"
"Number Field"
}
fn sort_name() -> &'static str {
@@ -594,50 +544,30 @@ impl Component for NumericStepper<usize> {
}
fn description() -> Option<&'static str> {
Some("A button used to increment or decrement a numeric value.")
Some("A numeric input element with increment and decrement buttons.")
}
fn preview(window: &mut Window, cx: &mut App) -> Option<AnyElement> {
let first_stepper = window.use_state(cx, |_, _| 100usize);
let second_stepper = window.use_state(cx, |_, _| 100.0);
let stepper_example = window.use_state(cx, |_, _| 100.0);
Some(
v_flex()
.gap_6()
.children(vec![example_group_with_title(
"Styles",
vec![
single_example(
"Default",
NumericStepper::new(
"numeric-stepper-component-preview",
*first_stepper.read(cx),
window,
cx,
)
.on_change({
let first_stepper = first_stepper.clone();
move |value, _, cx| first_stepper.write(cx, *value)
})
.into_any_element(),
),
single_example(
"Outlined",
NumericStepper::new(
"numeric-stepper-with-border-component-preview",
*second_stepper.read(cx),
window,
cx,
)
.on_change({
let second_stepper = second_stepper.clone();
move |value, _, cx| second_stepper.write(cx, *value)
})
.min(1.0)
.max(100.0)
.style(NumericStepperStyle::Outlined)
.into_any_element(),
),
],
.children(vec![single_example(
"Default Numeric Stepper",
NumberField::new(
"numeric-stepper-component-preview",
*stepper_example.read(cx),
window,
cx,
)
.on_change({
let stepper_example = stepper_example.clone();
move |value, _, cx| stepper_example.write(cx, *value)
})
.min(1.0)
.max(100.0)
.into_any_element(),
)])
.into_any_element(),
)
+2 -2
View File
@@ -5,13 +5,13 @@
//! It can't be located in the `ui` crate because it depends on `editor`.
//!
mod font_picker;
mod numeric_stepper;
mod number_field;
use component::{example_group, single_example};
use editor::{Editor, EditorElement, EditorStyle};
pub use font_picker::*;
use gpui::{App, Entity, FocusHandle, Focusable, FontStyle, Hsla, Length, TextStyle};
pub use numeric_stepper::*;
pub use number_field::*;
use settings::Settings;
use std::sync::Arc;
use theme::ThemeSettings;