diff --git a/crates/gpui_elements/src/editable_text.rs b/crates/gpui_elements/src/editable_text.rs index 4fdbfcf022..4e453a995e 100644 --- a/crates/gpui_elements/src/editable_text.rs +++ b/crates/gpui_elements/src/editable_text.rs @@ -17,7 +17,6 @@ pub use text_area_state::*; /* TODO list - default-value -- naming clean up (EditableText) - remove gpuikit based input - cursor blinking - color styling configs @@ -25,4 +24,5 @@ pub use text_area_state::*; - text sanitation - test IME (char palette only available on macos) - unit tests +- disabled input/area */ diff --git a/crates/gpui_elements/src/editable_text/actions.rs b/crates/gpui_elements/src/editable_text/actions.rs index d597b17496..31073545f0 100644 --- a/crates/gpui_elements/src/editable_text/actions.rs +++ b/crates/gpui_elements/src/editable_text/actions.rs @@ -238,7 +238,7 @@ pub trait EditableTextActionHandler<'app>: Sized { } } -pub(super) trait EditableInputActionElement: StateBackedEditableText { +pub(super) trait EditableTextActionElement: StateBackedEditableText { fn state_entity_rc(&self) -> &Rc>>; fn register_action( diff --git a/crates/gpui_elements/src/editable_text/input_element.rs b/crates/gpui_elements/src/editable_text/input_element.rs index 73dfd2b15b..339a494577 100644 --- a/crates/gpui_elements/src/editable_text/input_element.rs +++ b/crates/gpui_elements/src/editable_text/input_element.rs @@ -1,6 +1,6 @@ use crate::editable_text::{ - InitStorage, StateBackedEditableText, TextInputState, - actions::{DEFAULT_INPUT_CONTEXT, EditableInputActionElement}, + EditableTextInputState, InitStorage, StateBackedEditableText, + actions::{DEFAULT_INPUT_CONTEXT, EditableTextActionElement}, shared_element::{self, EditableTextElement}, }; use gpui::{ @@ -31,7 +31,7 @@ pub struct TextInputElement { // Populated on first render with an entity stored/attached to the view. // This reference is shared with the action handlers, which are processed between renders // and therefore cannot otherwise access state attached to the view. - state_entity: Rc>>, + state_entity: Rc>>, init_storage: InitStorage, placeholder: Option, } @@ -54,6 +54,7 @@ impl InteractiveElement for TextInputElement { } } +// forced implementation since the API for the element doesnt use Stateful impl StatefulInteractiveElement for TextInputElement {} impl Styled for TextInputElement { @@ -70,10 +71,10 @@ impl IntoElement for TextInputElement { } impl StateBackedEditableText for TextInputElement { - type State = TextInputState; + type State = EditableTextInputState; } -impl EditableInputActionElement for TextInputElement { +impl EditableTextActionElement for TextInputElement { fn state_entity_rc(&self) -> &Rc>> { &self.state_entity } @@ -90,7 +91,7 @@ impl EditableTextElement for TextInputElement { } impl Element for TextInputElement { - type RequestLayoutState = shared_element::LayoutState; + type RequestLayoutState = shared_element::LayoutState; type PrepaintState = shared_element::PrepaintState; fn id(&self) -> Option { diff --git a/crates/gpui_elements/src/editable_text/input_state.rs b/crates/gpui_elements/src/editable_text/input_state.rs index 35d412242c..89dc75489c 100644 --- a/crates/gpui_elements/src/editable_text/input_state.rs +++ b/crates/gpui_elements/src/editable_text/input_state.rs @@ -1,6 +1,7 @@ use super::notify::TextHistoryPushed; use crate::editable_text::{ - TextBoundary, TextInputStateBase, TextStateNotifier, UnicodeTextStorage, notify::TextChanged, + EditableTextStateBase, EditableTextStateNotifier, TextBoundary, UnicodeTextStorage, + notify::TextChanged, }; use gpui::{ Bounds, Context, EntityInputHandler, EventEmitter, NavigationDirection, Pixels, Point, @@ -8,34 +9,34 @@ use gpui::{ }; use std::ops::Range; -pub struct TextInputState { - internal: TextInputStateBase, +pub struct EditableTextInputState { + internal: EditableTextStateBase, } -impl EventEmitter for TextInputState {} -impl EventEmitter for TextInputState {} +impl EventEmitter for EditableTextInputState {} +impl EventEmitter for EditableTextInputState {} -impl std::ops::Deref for TextInputState { - type Target = TextInputStateBase; +impl std::ops::Deref for EditableTextInputState { + type Target = EditableTextStateBase; fn deref(&self) -> &Self::Target { &self.internal } } -impl std::ops::DerefMut for TextInputState { +impl std::ops::DerefMut for EditableTextInputState { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.internal } } -impl TextInputState { +impl EditableTextInputState { pub fn new(storage: impl Into>, cx: &mut Context) -> Self { - let internal = TextInputStateBase::new(storage, cx); + let internal = EditableTextStateBase::new(storage, cx); Self { internal } } } -impl TextStateNotifier for Context<'_, TextInputState> { +impl EditableTextStateNotifier for Context<'_, EditableTextInputState> { fn notify_changed(&mut self) { self.notify(); } @@ -49,7 +50,7 @@ impl TextStateNotifier for Context<'_, TextInputState> { } } -impl EntityInputHandler for TextInputState { +impl EntityInputHandler for EditableTextInputState { fn text_for_range( &mut self, range_utf16: Range, @@ -92,7 +93,6 @@ impl EntityInputHandler for TextInputState { let range_utf8 = self.internal.ime_resolve_range(range_utf16); self.internal .replace_text_in_range_bytes(range_utf8, text_to_insert, cx); - //self.mark_layout_dirty(); //cx.emit(CursorTrigger::PauseBlinkingForUserAction); cx.emit_text_changed(TextChanged); cx.notify_changed(); @@ -116,7 +116,6 @@ impl EntityInputHandler for TextInputState { &new_selected_range_utf16, text_to_insert.len(), ); - //self.mark_layout_dirty(); cx.emit_text_changed(TextChanged); cx.notify_changed(); } @@ -146,7 +145,7 @@ impl EntityInputHandler for TextInputState { } use super::actions::*; -impl<'app> EditableTextActionHandler<'app> for TextInputState { +impl<'app> EditableTextActionHandler<'app> for EditableTextInputState { type Context = gpui::Context<'app, Self>; fn escape(&mut self, _: &Escape, window: &mut Window, cx: &mut Self::Context) { diff --git a/crates/gpui_elements/src/editable_text/shared_element.rs b/crates/gpui_elements/src/editable_text/shared_element.rs index 091979d875..d61b8fd102 100644 --- a/crates/gpui_elements/src/editable_text/shared_element.rs +++ b/crates/gpui_elements/src/editable_text/shared_element.rs @@ -1,13 +1,12 @@ use crate::editable_text::{ TextInputLayoutData, TextLineSegment, - actions::{EditableInputActionElement, EditableTextActionHandler}, + actions::{EditableTextActionElement, EditableTextActionHandler}, }; use gpui::{ - Along, App, Axis, Bounds, ContentMask, Context, CursorStyle, DispatchPhase, Display, - ElementInputHandler, Entity, FocusHandle, Focusable, Hitbox, HitboxBehavior, Hsla, - InteractiveElement, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, - Pixels, Point, ScrollWheelEvent, SharedString, Size, Style, TextAlign, TextLayout, Window, - WrappedLine, fill, point, px, size, + App, Bounds, Context, CursorStyle, DispatchPhase, Display, ElementInputHandler, Entity, + FocusHandle, Focusable, Hitbox, HitboxBehavior, Hsla, InteractiveElement, MouseButton, + MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point, SharedString, Size, + Style, TextAlign, TextLayout, Window, WrappedLine, fill, point, px, size, }; use smallvec::SmallVec; use std::{ops::Range, sync::Arc}; @@ -49,7 +48,7 @@ pub struct PrepaintState { pub caret_visible: bool, } -pub trait EditableTextElement: InteractiveElement + EditableInputActionElement { +pub trait EditableTextElement: InteractiveElement + EditableTextActionElement { fn init_state(&self, cx: &mut Context) -> Self::State; fn placeholder(&self) -> &Option; @@ -214,7 +213,6 @@ pub trait EditableTextElement: InteractiveElement + EditableInputActionElement { size: Some(size), last_seen_storage_version, lines, - lines_represent_placeholder: show_placeholder, }; // Update the state for use in prepaint, paint, and action handlers. diff --git a/crates/gpui_elements/src/editable_text/shared_state.rs b/crates/gpui_elements/src/editable_text/shared_state.rs index 37465ea3be..ca681d144b 100644 --- a/crates/gpui_elements/src/editable_text/shared_state.rs +++ b/crates/gpui_elements/src/editable_text/shared_state.rs @@ -9,7 +9,7 @@ use gpui::{ }; use std::{ops::Range, sync::Arc}; -pub trait TextStateNotifier { +pub trait EditableTextStateNotifier { fn notify_changed(&mut self); fn emit_text_changed(&mut self, event: TextChanged); fn emit_history(&mut self, event: TextHistoryPushed); @@ -17,13 +17,13 @@ pub trait TextStateNotifier { pub trait StateBackedEditableText { type State: 'static - + std::ops::Deref + + std::ops::Deref + std::ops::DerefMut + EntityInputHandler + for<'app> EditableTextActionHandler<'app>; } -pub struct TextInputStateBase { +pub struct EditableTextStateBase { storage: Box, /// The utf-8 character range that is currently selected by the user. @@ -60,7 +60,6 @@ pub(super) struct TextInputLayoutData { /// The `ShapedLine` produced by the painter's `prepaint`. /// Cached so IME `bounds_for_range` / `character_index_for_point` can evaluate without re-shaping. pub lines: Vec, - pub lines_represent_placeholder: bool, } pub(super) struct TextLineSegment { /// The utf8 byte range in the content string that this line covers. @@ -86,13 +85,13 @@ impl TextLineSegment { } } -impl Focusable for TextInputStateBase { +impl Focusable for EditableTextStateBase { fn focus_handle(&self, _: &App) -> FocusHandle { self.focus_handle.clone() } } -impl TextInputStateBase { +impl EditableTextStateBase { pub fn new(storage: impl Into>, cx: &mut App) -> Self { Self { storage: storage.into(), @@ -142,7 +141,7 @@ impl TextInputStateBase { } } -impl TextInputStateBase { +impl EditableTextStateBase { /// Returns the utf-8 character position of the start of the line that contains the provided pixel-point. pub fn index_for_pixel_point(&self, point: Point, line_height: Pixels) -> usize { let storage_len_utf8 = self.storage.content_utf8().len(); @@ -178,7 +177,7 @@ impl TextInputStateBase { } } -impl TextInputStateBase { +impl EditableTextStateBase { pub fn ime_text_for_range( &self, range_utf16: Range, @@ -233,7 +232,7 @@ impl TextInputStateBase { fn emit_change_for_undo( &self, - cx: &mut impl TextStateNotifier, + cx: &mut impl EditableTextStateNotifier, range: Range, length: usize, ) { @@ -249,7 +248,7 @@ impl TextInputStateBase { &mut self, range: Range, mut text_to_insert: &str, - cx: &mut impl TextStateNotifier, + cx: &mut impl EditableTextStateNotifier, ) { // TODO: Apply text sanitization // single-line fields should prune \n and \r @@ -355,8 +354,8 @@ impl TextInputStateBase { } } -impl TextInputStateBase { - pub fn move_to(&mut self, caret_pos: usize, cx: &mut impl TextStateNotifier) { +impl EditableTextStateBase { + pub fn move_to(&mut self, caret_pos: usize, cx: &mut impl EditableTextStateNotifier) { //cx.emit(CursorTrigger::PauseBlinkingForUserAction); let caret_pos = caret_pos.min(self.storage.content_utf8().len()); self.selected_range = caret_pos..caret_pos; @@ -364,7 +363,7 @@ impl TextInputStateBase { cx.notify_changed(); } - pub fn select_to(&mut self, caret_pos: usize, cx: &mut impl TextStateNotifier) { + pub fn select_to(&mut self, caret_pos: usize, cx: &mut impl EditableTextStateNotifier) { //cx.emit(CursorTrigger::PauseBlinkingForUserAction); let caret_pos = caret_pos.min(self.storage().content_utf8().len()); self.selected_range.start = caret_pos; @@ -376,7 +375,7 @@ impl TextInputStateBase { &mut self, direction: NavigationDirection, boundary: TextBoundary, - cx: &mut impl TextStateNotifier, + cx: &mut impl EditableTextStateNotifier, ) { let range = self.selected_range(); let range = match range.is_empty() { @@ -399,7 +398,7 @@ impl TextInputStateBase { &mut self, direction: NavigationDirection, boundary: TextBoundary, - cx: &mut impl TextStateNotifier, + cx: &mut impl EditableTextStateNotifier, ) { let caret_pos = match self.selected_range.is_empty() { false => match direction { @@ -489,7 +488,7 @@ impl TextInputStateBase { (direction > 0).then(|| self.storage.content_utf8().len()) } - pub fn select_all(&mut self, cx: &mut impl TextStateNotifier) { + pub fn select_all(&mut self, cx: &mut impl EditableTextStateNotifier) { self.selected_range = 0..self.storage.content_utf8().len(); cx.notify_changed(); } @@ -498,7 +497,7 @@ impl TextInputStateBase { &mut self, direction: NavigationDirection, boundary: TextBoundary, - cx: &mut impl TextStateNotifier, + cx: &mut impl EditableTextStateNotifier, ) { let caret_pos = self .storage @@ -508,7 +507,7 @@ impl TextInputStateBase { pub fn cut(&mut self, cx: &mut T) where - T: TextStateNotifier + std::ops::Deref, + T: EditableTextStateNotifier + std::ops::Deref, { if !self.selected_range.is_empty() { // Cut selected text @@ -559,7 +558,7 @@ impl TextInputStateBase { pub fn paste(&mut self, cx: &mut T) where - T: TextStateNotifier + std::ops::Deref, + T: EditableTextStateNotifier + std::ops::Deref, { let Some(text) = cx.read_from_clipboard().and_then(|item| item.text()) else { return; @@ -578,7 +577,7 @@ impl TextInputStateBase { window: &mut Window, cx: &mut Context, ) where - Context: TextStateNotifier + std::ops::DerefMut, + Context: EditableTextStateNotifier + std::ops::DerefMut, { window.focus(&self.focus_handle, cx); self.is_selecting = true; @@ -629,7 +628,7 @@ impl TextInputStateBase { self.is_selecting = false; } - pub fn on_mouse_move(&mut self, character_pos: usize, cx: &mut impl TextStateNotifier) { + pub fn on_mouse_move(&mut self, character_pos: usize, cx: &mut impl EditableTextStateNotifier) { if self.is_selecting && self.click_count == 1 { self.select_to(character_pos, cx); } diff --git a/crates/gpui_elements/src/editable_text/text_area_element.rs b/crates/gpui_elements/src/editable_text/text_area_element.rs index 35adb38811..6977c5731b 100644 --- a/crates/gpui_elements/src/editable_text/text_area_element.rs +++ b/crates/gpui_elements/src/editable_text/text_area_element.rs @@ -1,6 +1,6 @@ use crate::editable_text::{ - InitStorage, StateBackedEditableText, TextAreaState, - actions::{DEFAULT_INPUT_CONTEXT, EditableInputActionElement}, + EditableTextAreaState, InitStorage, StateBackedEditableText, + actions::{DEFAULT_INPUT_CONTEXT, EditableTextActionElement}, shared_element::{self, EditableTextElement}, }; use gpui::{ @@ -9,8 +9,8 @@ use gpui::{ }; use std::{cell::RefCell, rc::Rc}; -pub fn text_area(id: impl Into) -> TextAreaElement { - let mut this = TextAreaElement { +pub fn text_area(id: impl Into) -> EditableTextAreaElement { + let mut this = EditableTextAreaElement { interactivity: Interactivity::new(), state_entity: Rc::new(RefCell::new(WeakEntity::new_invalid())), init_storage: InitStorage::default(), @@ -25,17 +25,17 @@ pub fn text_area(id: impl Into) -> TextAreaElement { } // TODO: Disabled flag/state? -pub struct TextAreaElement { +pub struct EditableTextAreaElement { interactivity: Interactivity, // Populated on first render with an entity stored/attached to the view. // This reference is shared with the action handlers, which are processed between renders // and therefore cannot otherwise access state attached to the view. - state_entity: Rc>>, + state_entity: Rc>>, init_storage: InitStorage, placeholder: Option, } -impl TextAreaElement { +impl EditableTextAreaElement { pub fn placeholder(mut self, text: impl Into) -> Self { self.placeholder = Some(text.into()); self @@ -47,38 +47,39 @@ impl TextAreaElement { } } -impl InteractiveElement for TextAreaElement { +impl InteractiveElement for EditableTextAreaElement { fn interactivity(&mut self) -> &mut Interactivity { &mut self.interactivity } } -impl StatefulInteractiveElement for TextAreaElement {} +// forced implementation since the API for the element doesnt use Stateful +impl StatefulInteractiveElement for EditableTextAreaElement {} -impl Styled for TextAreaElement { +impl Styled for EditableTextAreaElement { fn style(&mut self) -> &mut StyleRefinement { &mut self.interactivity.base_style } } -impl IntoElement for TextAreaElement { +impl IntoElement for EditableTextAreaElement { type Element = Self; fn into_element(self) -> Self::Element { self } } -impl StateBackedEditableText for TextAreaElement { - type State = TextAreaState; +impl StateBackedEditableText for EditableTextAreaElement { + type State = EditableTextAreaState; } -impl EditableInputActionElement for TextAreaElement { +impl EditableTextActionElement for EditableTextAreaElement { fn state_entity_rc(&self) -> &Rc>> { &self.state_entity } } -impl EditableTextElement for TextAreaElement { +impl EditableTextElement for EditableTextAreaElement { fn init_state(&self, cx: &mut gpui::prelude::Context) -> Self::State { Self::State::new(self.init_storage.exec(cx), cx) } @@ -88,8 +89,8 @@ impl EditableTextElement for TextAreaElement { } } -impl Element for TextAreaElement { - type RequestLayoutState = shared_element::LayoutState; +impl Element for EditableTextAreaElement { + type RequestLayoutState = shared_element::LayoutState; type PrepaintState = shared_element::PrepaintState; fn id(&self) -> Option { diff --git a/crates/gpui_elements/src/editable_text/text_area_state.rs b/crates/gpui_elements/src/editable_text/text_area_state.rs index 9968873014..9b25ab0cfb 100644 --- a/crates/gpui_elements/src/editable_text/text_area_state.rs +++ b/crates/gpui_elements/src/editable_text/text_area_state.rs @@ -1,5 +1,5 @@ use crate::editable_text::{ - TextBoundary, TextInputStateBase, TextStateNotifier, UnicodeTextStorage, + EditableTextStateBase, EditableTextStateNotifier, TextBoundary, UnicodeTextStorage, notify::{TextChanged, TextHistoryPushed}, }; use gpui::{ @@ -8,34 +8,34 @@ use gpui::{ }; use std::ops::Range; -pub struct TextAreaState { - internal: TextInputStateBase, +pub struct EditableTextAreaState { + internal: EditableTextStateBase, } -impl EventEmitter for TextAreaState {} -impl EventEmitter for TextAreaState {} +impl EventEmitter for EditableTextAreaState {} +impl EventEmitter for EditableTextAreaState {} -impl std::ops::Deref for TextAreaState { - type Target = TextInputStateBase; +impl std::ops::Deref for EditableTextAreaState { + type Target = EditableTextStateBase; fn deref(&self) -> &Self::Target { &self.internal } } -impl std::ops::DerefMut for TextAreaState { +impl std::ops::DerefMut for EditableTextAreaState { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.internal } } -impl TextAreaState { +impl EditableTextAreaState { pub fn new(storage: impl Into>, cx: &mut Context) -> Self { - let internal = TextInputStateBase::new(storage, cx); + let internal = EditableTextStateBase::new(storage, cx); Self { internal } } } -impl TextStateNotifier for Context<'_, TextAreaState> { +impl EditableTextStateNotifier for Context<'_, EditableTextAreaState> { fn notify_changed(&mut self) { self.notify(); } @@ -49,7 +49,7 @@ impl TextStateNotifier for Context<'_, TextAreaState> { } } -impl EntityInputHandler for TextAreaState { +impl EntityInputHandler for EditableTextAreaState { fn text_for_range( &mut self, range_utf16: Range, @@ -92,7 +92,6 @@ impl EntityInputHandler for TextAreaState { let range_utf8 = self.internal.ime_resolve_range(range_utf16); self.internal .replace_text_in_range_bytes(range_utf8, text_to_insert, cx); - //self.mark_layout_dirty(); //cx.emit(CursorTrigger::PauseBlinkingForUserAction); cx.emit_text_changed(TextChanged); cx.notify_changed(); @@ -116,7 +115,6 @@ impl EntityInputHandler for TextAreaState { &new_selected_range_utf16, text_to_insert.len(), ); - //self.mark_layout_dirty(); cx.emit_text_changed(TextChanged); cx.notify_changed(); } @@ -146,7 +144,7 @@ impl EntityInputHandler for TextAreaState { } use super::actions::*; -impl<'app> EditableTextActionHandler<'app> for TextAreaState { +impl<'app> EditableTextActionHandler<'app> for EditableTextAreaState { type Context = gpui::Context<'app, Self>; fn escape(&mut self, _: &Escape, window: &mut Window, cx: &mut Self::Context) {