diff --git a/crates/gpui_elements/src/editable_text/input_element.rs b/crates/gpui_elements/src/editable_text/input_element.rs index a8a55e37cd..e17bc65332 100644 --- a/crates/gpui_elements/src/editable_text/input_element.rs +++ b/crates/gpui_elements/src/editable_text/input_element.rs @@ -206,8 +206,12 @@ impl Element for TextInputElement { // TODO: no wrapping in single-line let wrap_width = Some(bounds.size.width); - let wrapping = TextLayoutWrapping::new(request_layout.text_style.clone(), wrap_width); let showing_placeholder = request_layout.state.update(cx, |state, _cx| { + let wrapping = TextLayoutWrapping::new( + request_layout.text_style.clone(), + wrap_width, + state.storage().version(), + ); let show_placeholder = state.storage().content_utf8().is_empty(); state.layout_data.bounds = bounds; if state.layout_wrapping.integrate(wrapping) { diff --git a/crates/gpui_elements/src/editable_text/shared_state.rs b/crates/gpui_elements/src/editable_text/shared_state.rs index 865b9b5eb1..be643359dc 100644 --- a/crates/gpui_elements/src/editable_text/shared_state.rs +++ b/crates/gpui_elements/src/editable_text/shared_state.rs @@ -4,7 +4,7 @@ use crate::editable_text::{ }; use gpui::{ App, Bounds, ClipboardItem, Entity, FocusHandle, Focusable, Hsla, NavigationDirection, Pixels, - Point, ShapedLine, SharedString, TextRun, TextStyle, UTF16Selection, Window, WrappedLine, + Point, SharedString, TextRun, TextStyle, UTF16Selection, Window, WrappedLine, }; use std::{ops::Range, sync::Arc}; @@ -58,25 +58,32 @@ pub struct TextInputStateBase { pub(super) layout_data: TextInputLayoutData, } -#[derive(Default)] +#[derive(PartialEq)] pub(super) struct TextLayoutWrapping { text_style: TextStyle, wrap_width: Option, - dirty: bool, + last_seen_storage_version: u16, +} +impl Default for TextLayoutWrapping { + fn default() -> Self { + Self { + text_style: Default::default(), + wrap_width: Default::default(), + last_seen_storage_version: u16::MAX, + } + } } impl TextLayoutWrapping { - pub fn new(text_style: TextStyle, wrap_width: Option) -> Self { + pub fn new(text_style: TextStyle, wrap_width: Option, storage_version: u16) -> Self { Self { text_style, wrap_width, - dirty: false, + last_seen_storage_version: storage_version, } } pub fn integrate(&mut self, other: Self) -> bool { - let dirty = self.dirty - || self.wrap_width != other.wrap_width - || self.text_style != other.text_style; + let dirty = *self != other; *self = other; dirty } @@ -125,10 +132,7 @@ impl TextInputStateBase { focus_handle: cx.focus_handle(), - layout_wrapping: TextLayoutWrapping { - dirty: true, - ..TextLayoutWrapping::default() - }, + layout_wrapping: TextLayoutWrapping::default(), layout_data: TextInputLayoutData::default(), } } @@ -166,11 +170,11 @@ impl TextInputStateBase { } impl TextInputStateBase { - pub fn line_segments(&self) -> &Vec { + pub(super) fn line_segments(&self) -> &Vec { &self.layout_data.lines } - pub fn build_wrapped_lines( + pub(super) fn build_wrapped_lines( content: &str, wrapping: &TextLayoutWrapping, window: &Window, diff --git a/crates/gpui_elements/src/editable_text/storage.rs b/crates/gpui_elements/src/editable_text/storage.rs index e5b2422e65..5da6debeee 100644 --- a/crates/gpui_elements/src/editable_text/storage.rs +++ b/crates/gpui_elements/src/editable_text/storage.rs @@ -18,13 +18,15 @@ pub(super) struct InitStorage(Option Box Box { match &self.0 { - None => Box::new(String::new()), + None => Box::new(StringStorage::default()), Some(init) => (*init)(cx), } } } pub trait UnicodeTextStorage { + fn version(&self) -> u16; + /// Returns a reference to the utf8 string. fn content_utf8(&self) -> &str; @@ -215,16 +217,34 @@ pub trait UnicodeTextStorage { } } -impl UnicodeTextStorage for String { +#[derive(Default)] +pub struct StringStorage { + value: String, + version: u16, +} +impl From for StringStorage { + fn from(value: String) -> Self { + Self { + value, + version: u16::default(), + } + } +} +impl UnicodeTextStorage for StringStorage { + fn version(&self) -> u16 { + self.version + } + fn content_utf8(&self) -> &str { - self.as_str() + self.value.as_str() } fn len_utf16(&self) -> usize { - self.len() + self.value.chars().map(|c| c.len_utf16()).sum() } fn replace_range(&mut self, range: Range, text: &str) { - self.replace_range(range, &text); + self.value.replace_range(range, &text); + self.version = self.version.wrapping_add(1); } }