diff --git a/Cargo.lock b/Cargo.lock index dc9d074f01..6be6d3aea4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11095,6 +11095,7 @@ dependencies = [ "telemetry", "theme", "ui", + "ui_input", "util", "vim_mode_setting", "workspace", @@ -17514,6 +17515,7 @@ dependencies = [ "component", "editor", "gpui", + "menu", "settings", "theme", "ui", diff --git a/crates/editor/src/editor_settings_controls.rs b/crates/editor/src/editor_settings_controls.rs index dc5557b052..3ae08c7821 100644 --- a/crates/editor/src/editor_settings_controls.rs +++ b/crates/editor/src/editor_settings_controls.rs @@ -5,8 +5,7 @@ use project::project_settings::{InlineBlameSettings, ProjectSettings}; use settings::{EditableSettingControl, Settings}; use theme::{FontFamilyCache, FontFamilyName, ThemeSettings}; use ui::{ - CheckboxWithLabel, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer, SettingsGroup, - prelude::*, + CheckboxWithLabel, ContextMenu, DropdownMenu, SettingsContainer, SettingsGroup, prelude::*, }; use crate::EditorSettings; @@ -139,21 +138,12 @@ impl EditableSettingControl for BufferFontSizeControl { impl RenderOnce for BufferFontSizeControl { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { - let value = Self::read(cx); + let _value = Self::read(cx); h_flex() .gap_2() .child(Icon::new(IconName::FontSize)) - .child(NumericStepper::new( - "buffer-font-size", - value.to_string(), - move |_, _, cx| { - Self::write(value - px(1.), cx); - }, - move |_, _, cx| { - Self::write(value + px(1.), cx); - }, - )) + .child(div()) // todo!(Numeric stepper was here) } } diff --git a/crates/onboarding/Cargo.toml b/crates/onboarding/Cargo.toml index 4157be3172..5f157a0ae9 100644 --- a/crates/onboarding/Cargo.toml +++ b/crates/onboarding/Cargo.toml @@ -39,6 +39,7 @@ settings.workspace = true telemetry.workspace = true theme.workspace = true ui.workspace = true +ui_input.workspace = true util.workspace = true vim_mode_setting.workspace = true workspace-hack.workspace = true diff --git a/crates/onboarding/src/editing_page.rs b/crates/onboarding/src/editing_page.rs index d941a0315a..9dfd9356cc 100644 --- a/crates/onboarding/src/editing_page.rs +++ b/crates/onboarding/src/editing_page.rs @@ -12,10 +12,10 @@ use project::project_settings::ProjectSettings; use settings::{Settings as _, update_settings_file}; use theme::{FontFamilyCache, FontFamilyName, ThemeSettings}; use ui::{ - ButtonLike, ListItem, ListItemSpacing, NumericStepper, PopoverMenu, SwitchField, - ToggleButtonGroup, ToggleButtonGroupStyle, ToggleButtonSimple, ToggleState, Tooltip, - prelude::*, + ButtonLike, ListItem, ListItemSpacing, PopoverMenu, SwitchField, ToggleButtonGroup, + ToggleButtonGroupStyle, ToggleButtonSimple, ToggleState, Tooltip, prelude::*, }; +use ui_input::NumericStepper; use crate::{ImportCursorSettings, ImportVsCodeSettings, SettingsImportState}; @@ -350,14 +350,19 @@ fn render_font_customization_section( NumericStepper::new( "ui-font-size", ui_font_size.to_string(), + move |size, cx| { + write_ui_font_size(Pixels::from(size), cx); + }, move |_, _, cx| { write_ui_font_size(ui_font_size - px(1.), cx); }, move |_, _, cx| { write_ui_font_size(ui_font_size + px(1.), cx); }, + window, + cx, ) - .style(ui::NumericStepperStyle::Outlined) + .style(ui_input::NumericStepperStyle::Outlined) .tab_index({ *tab_index += 2; *tab_index - 2 @@ -414,14 +419,19 @@ fn render_font_customization_section( NumericStepper::new( "buffer-font-size", buffer_font_size.to_string(), + move |size, cx| { + write_buffer_font_size(Pixels::from(size), cx); + }, move |_, _, cx| { write_buffer_font_size(buffer_font_size - px(1.), cx); }, move |_, _, cx| { write_buffer_font_size(buffer_font_size + px(1.), cx); }, + window, + cx, ) - .style(ui::NumericStepperStyle::Outlined) + .style(ui_input::NumericStepperStyle::Outlined) .tab_index({ *tab_index += 2; *tab_index - 2 diff --git a/crates/settings_ui/src/appearance_settings_controls.rs b/crates/settings_ui/src/appearance_settings_controls.rs index 141ae13182..e3c04bfa43 100644 --- a/crates/settings_ui/src/appearance_settings_controls.rs +++ b/crates/settings_ui/src/appearance_settings_controls.rs @@ -6,9 +6,10 @@ use theme::{ FontFamilyCache, FontFamilyName, SystemAppearance, ThemeMode, ThemeRegistry, ThemeSettings, }; use ui::{ - CheckboxWithLabel, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer, SettingsGroup, - ToggleButton, prelude::*, + CheckboxWithLabel, ContextMenu, DropdownMenu, SettingsContainer, SettingsGroup, ToggleButton, + prelude::*, }; +use ui_input::NumericStepper; #[derive(IntoElement)] pub struct AppearanceSettingsControls {} @@ -254,7 +255,7 @@ impl EditableSettingControl for UiFontSizeControl { } impl RenderOnce for UiFontSizeControl { - fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { + fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { let value = Self::read(cx); h_flex() @@ -263,12 +264,17 @@ impl RenderOnce for UiFontSizeControl { .child(NumericStepper::new( "ui-font-size", value.to_string(), + move |size, cx| { + Self::write(Pixels::from(size), cx); + }, move |_, _, cx| { Self::write(value - px(1.), cx); }, move |_, _, cx| { Self::write(value + px(1.), cx); }, + window, + cx, )) } } diff --git a/crates/ui/src/components.rs b/crates/ui/src/components.rs index 486673e733..51a414cc32 100644 --- a/crates/ui/src/components.rs +++ b/crates/ui/src/components.rs @@ -22,7 +22,6 @@ mod list; mod modal; mod navigable; mod notification; -mod numeric_stepper; mod popover; mod popover_menu; mod progress; @@ -65,7 +64,6 @@ pub use list::*; pub use modal::*; pub use navigable::*; pub use notification::*; -pub use numeric_stepper::*; pub use popover::*; pub use popover_menu::*; pub use progress::*; diff --git a/crates/ui_input/Cargo.toml b/crates/ui_input/Cargo.toml index 0f337597f0..97f250c6ae 100644 --- a/crates/ui_input/Cargo.toml +++ b/crates/ui_input/Cargo.toml @@ -15,6 +15,7 @@ path = "src/ui_input.rs" component.workspace = true editor.workspace = true gpui.workspace = true +menu.workspace = true settings.workspace = true theme.workspace = true ui.workspace = true diff --git a/crates/ui/src/components/numeric_stepper.rs b/crates/ui_input/src/numeric_stepper.rs similarity index 62% rename from crates/ui/src/components/numeric_stepper.rs rename to crates/ui_input/src/numeric_stepper.rs index 2ddb86d9a0..9c7f1a7c67 100644 --- a/crates/ui/src/components/numeric_stepper.rs +++ b/crates/ui_input/src/numeric_stepper.rs @@ -1,6 +1,7 @@ -use gpui::ClickEvent; +use editor::Editor; +use gpui::{ClickEvent, Entity, Focusable}; -use crate::{IconButtonShape, prelude::*}; +use ui::{IconButtonShape, prelude::*}; #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub enum NumericStepperStyle { @@ -9,11 +10,21 @@ pub enum NumericStepperStyle { Ghost, } +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +pub enum NumericStepperMode { + #[default] + Read, + Edit, +} + #[derive(IntoElement, RegisterComponent)] pub struct NumericStepper { id: ElementId, value: SharedString, style: NumericStepperStyle, + input_field: Entity, + mode: Entity, + set_value_to: Box, on_decrement: Box, on_increment: Box, /// Whether to reserve space for the reset button. @@ -26,15 +37,61 @@ impl NumericStepper { pub fn new( id: impl Into, value: impl Into, + set_value_to: impl Fn(usize, &mut App) + 'static, on_decrement: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, on_increment: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, + window: &mut Window, + cx: &mut App, ) -> Self { + let id = id.into(); + let value = value.into(); + + let (input_field, mode) = window.with_global_id(id.clone(), |global_id, window| { + // todo! Make sure that using this api is inline and appropriate with the codebase + window.with_element_state::<(Entity, Entity), _>( + global_id, + |mut editor, window| { + let state = editor + .get_or_insert_with(|| { + let mode = cx.new(|_| NumericStepperMode::default()); + let weak_mode = mode.downgrade(); + let editor = cx.new(|cx| { + let editor = Editor::single_line(window, cx); + + cx.on_focus_out( + &editor.focus_handle(cx), + window, + move |this, _, window, cx| { + this.clear(window, cx); + + weak_mode + .update(cx, |mode, _| *mode = NumericStepperMode::Read) + .ok(); + }, + ) + .detach(); + + editor + }); + + (editor, mode) + }) + .clone(); + + (state.clone(), state) + }, + ) + }); + Self { - id: id.into(), - value: value.into(), - style: NumericStepperStyle::default(), + id, + value, + input_field, + mode, + set_value_to: Box::new(set_value_to), on_decrement: Box::new(on_decrement), on_increment: Box::new(on_increment), + style: NumericStepperStyle::default(), reserve_space_for_reset: false, on_reset: None, tab_index: None, @@ -74,7 +131,7 @@ impl RenderOnce for NumericStepper { let mut tab_index = self.tab_index; h_flex() - .id(self.id) + .id(self.id.clone()) .gap_1() .map(|element| { if let Some(on_reset) = self.on_reset { @@ -146,7 +203,59 @@ impl RenderOnce for NumericStepper { ) } }) - .child(Label::new(self.value).mx_3()) + .child(if matches!(self.mode.read(cx), NumericStepperMode::Read) { + div() + .id(SharedString::new(format!( + "numeric_stepper_label{}", + &self.id, + ))) + .child(Label::new(self.value).mx_3()) + .on_click({ + let mode = self.mode.downgrade(); + let input_field_focus_handle = self.input_field.focus_handle(cx); + + move |click, window, cx| { + if click.click_count() == 2 { + mode.update(cx, |mode, _| { + *mode = NumericStepperMode::Edit; + }) + .ok(); + + window.focus(&input_field_focus_handle); + } + } + }) + .into_any_element() + } else { + div() + .child(self.input_field.clone()) + .child("todo!(This should be removed. It's only here to get input_field to render correctly)") + .on_action::({ + let input_field = self.input_field.downgrade(); + let mode = self.mode.downgrade(); + let set_value = self.set_value_to; + + move |_, _, cx| { + input_field + .update(cx, |input_field, cx| { + if let Some(number) = + input_field.text(cx).parse::().ok() + { + set_value(number, cx); + + mode.update(cx, |mode, _| { + *mode = NumericStepperMode::Read + }) + .ok(); + } + }) + .ok(); + } + }) + .w_full() + .mx_3() + .into_any_element() + }) .map(|increment| { if is_outlined { increment.child( @@ -201,7 +310,7 @@ impl Component for NumericStepper { Some("A button used to increment or decrement a numeric value.") } - fn preview(_window: &mut Window, _cx: &mut App) -> Option { + fn preview(window: &mut Window, cx: &mut App) -> Option { Some( v_flex() .gap_6() @@ -213,8 +322,11 @@ impl Component for NumericStepper { NumericStepper::new( "numeric-stepper-component-preview", "10", + move |_, _| {}, move |_, _, _| {}, move |_, _, _| {}, + window, + cx, ) .into_any_element(), ), @@ -223,8 +335,11 @@ impl Component for NumericStepper { NumericStepper::new( "numeric-stepper-with-border-component-preview", "10", + move |_, _| {}, move |_, _, _| {}, move |_, _, _| {}, + window, + cx, ) .style(NumericStepperStyle::Outlined) .into_any_element(), diff --git a/crates/ui_input/src/ui_input.rs b/crates/ui_input/src/ui_input.rs index 1a5bebaf1e..fdb8f54d62 100644 --- a/crates/ui_input/src/ui_input.rs +++ b/crates/ui_input/src/ui_input.rs @@ -4,10 +4,12 @@ //! //! It can't be located in the `ui` crate because it depends on `editor`. //! +mod numeric_stepper; use component::{example_group, single_example}; use editor::{Editor, EditorElement, EditorStyle}; use gpui::{App, Entity, FocusHandle, Focusable, FontStyle, Hsla, TextStyle}; +pub use numeric_stepper::*; use settings::Settings; use theme::ThemeSettings; use ui::prelude::*;