diff --git a/crates/gpui_elements/src/editable_text.rs b/crates/gpui_elements/src/editable_text.rs index e5b7e600e2..249bc960cc 100644 --- a/crates/gpui_elements/src/editable_text.rs +++ b/crates/gpui_elements/src/editable_text.rs @@ -9,7 +9,6 @@ pub use state::*; pub use storage::*; /* TODO list -- disabled input/area - remove gpuikit based input - auto-scroll when cursor moves - cursor blinking diff --git a/crates/gpui_elements/src/editable_text/element.rs b/crates/gpui_elements/src/editable_text/element.rs index 295cf9767b..21044ffd63 100644 --- a/crates/gpui_elements/src/editable_text/element.rs +++ b/crates/gpui_elements/src/editable_text/element.rs @@ -21,6 +21,7 @@ pub fn editable_text(id: impl Into) -> EditableTextElement { supports_multiline: true, init_storage: InitStorage::default(), placeholder: None, + accepts_input: true, }; this.interactivity.element_id = Some(id.into()); @@ -40,7 +41,6 @@ pub fn text_area(id: impl Into) -> EditableTextElement { editable_text(id).multiline(true) } -// TODO: Disabled flag/state? pub struct EditableTextElement { interactivity: Interactivity, // Populated on first render with an entity stored/attached to the view. @@ -50,6 +50,7 @@ pub struct EditableTextElement { init_storage: InitStorage, supports_multiline: bool, placeholder: Option, + accepts_input: bool, } impl EditableTextElement { @@ -78,6 +79,12 @@ impl EditableTextElement { self.init_storage = InitStorage::new_typed(move |_cx| storage.clone()); self } + + /// Configures whether the element can accept input (effectively is the element currently enabled). + pub fn accepts_input(mut self, enabled: bool) -> Self { + self.accepts_input = enabled; + self + } } impl InteractiveElement for EditableTextElement { @@ -193,6 +200,7 @@ impl Element for EditableTextElement { let placeholder = self.placeholder.clone(); let placeholder_color = Hsla::white().opacity(0.5); // TODO: as an element param let supports_multiline = self.supports_multiline; + let accepts_input = self.accepts_input; let layout_id = self.interactivity().request_layout( global_id, inspector_id, @@ -317,6 +325,7 @@ impl Element for EditableTextElement { let layout_data = TextInputLayoutData { supports_multiline, + accepts_input, wrap_width, size: Some(size), last_seen_storage_version, @@ -552,6 +561,7 @@ impl Element for EditableTextElement { window.set_cursor_style(CursorStyle::IBeam, hitbox); } + let accepts_input = self.accepts_input; let inner_bounds = prepaint.inner_bounds; let to_local_position = -(bounds.origin + prepaint.scroll_offset); let perform_paint = |style: &Style, window: &mut Window, cx: &mut App| { @@ -559,9 +569,11 @@ impl Element for EditableTextElement { return; } - // NOTE: Skip when disabled - let ime_handler = ElementInputHandler::new(inner_bounds, request_layout.state.clone()); - window.handle_input(&prepaint.focus_handle, ime_handler, cx); + if accepts_input { + let ime_handler = + ElementInputHandler::new(inner_bounds, request_layout.state.clone()); + window.handle_input(&prepaint.focus_handle, ime_handler, cx); + } window.on_mouse_event({ let state = request_layout.state.clone(); diff --git a/crates/gpui_elements/src/editable_text/state.rs b/crates/gpui_elements/src/editable_text/state.rs index cab7811516..4ed64b0a86 100644 --- a/crates/gpui_elements/src/editable_text/state.rs +++ b/crates/gpui_elements/src/editable_text/state.rs @@ -38,6 +38,7 @@ pub struct EditableTextState { #[derive(Default)] pub(super) struct TextInputLayoutData { pub supports_multiline: bool, + pub accepts_input: bool, /// The last known width at which the lines were wrapped. pub wrap_width: Option, /// The last known size of the text, as generated during layout. @@ -277,6 +278,10 @@ impl EditableTextState { boundary: TextBoundary, cx: &mut Context, ) { + if !self.layout_data.accepts_input { + return; + } + let range = self.selected_range(); let range = match range.is_empty() { false => range, @@ -555,13 +560,20 @@ impl<'app> EditableTextActionHandler> for EditableTextState } fn insert_enter(&mut self, _: &Enter, window: &mut Window, cx: &mut Context<'app, Self>) { - if self.layout_data.supports_multiline { - // TODO: Why is the cursor appearing at the start of the entire field instead of on the new line? - self.replace_text_in_range(None, "\n", window, cx); + if !self.layout_data.supports_multiline { + return; } + if !self.layout_data.accepts_input { + return; + } + // TODO: Why is the cursor appearing at the start of the entire field instead of on the new line? + self.replace_text_in_range(None, "\n", window, cx); } fn insert_tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<'app, Self>) { + if !self.layout_data.accepts_input { + return; + } self.replace_text_in_range(None, "\t", window, cx); } @@ -741,6 +753,10 @@ impl<'app> EditableTextActionHandler> for EditableTextState } fn cut(&mut self, _: &Cut, _w: &mut Window, cx: &mut Context<'app, Self>) { + if !self.layout_data.accepts_input { + return; + } + if !self.selected_range.is_empty() { // Cut selected text let slice = &self.storage.content_utf8()[self.selected_range.clone()]; @@ -789,6 +805,10 @@ impl<'app> EditableTextActionHandler> for EditableTextState } fn paste(&mut self, _: &Paste, _w: &mut Window, cx: &mut Context<'app, Self>) { + if !self.layout_data.accepts_input { + return; + } + let Some(text) = cx.read_from_clipboard().and_then(|item| item.text()) else { return; };