add versioning to storage so that the ui can re-render on storage change

This commit is contained in:
temportalflux
2026-07-11 09:31:07 -04:00
parent 479ee65c8b
commit 3260184b5c
3 changed files with 48 additions and 20 deletions
@@ -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) {
@@ -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<Pixels>,
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<Pixels>) -> Self {
pub fn new(text_style: TextStyle, wrap_width: Option<Pixels>, 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<TextLineSegment> {
pub(super) fn line_segments(&self) -> &Vec<TextLineSegment> {
&self.layout_data.lines
}
pub fn build_wrapped_lines(
pub(super) fn build_wrapped_lines(
content: &str,
wrapping: &TextLayoutWrapping,
window: &Window,
@@ -18,13 +18,15 @@ pub(super) struct InitStorage(Option<Rc<dyn Fn(&mut App) -> Box<dyn UnicodeTextS
impl InitStorage {
pub fn exec(&self, cx: &mut App) -> Box<dyn UnicodeTextStorage> {
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<String> 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<usize>, text: &str) {
self.replace_range(range, &text);
self.value.replace_range(range, &text);
self.version = self.version.wrapping_add(1);
}
}