T21a round 3 fix, after the cap
- TextInput records the selection each edit replaces, where every edit passes; the block editor's undo goes back to it, for typing, delete, cut and paste - TextInput's layout geometry moves to forms/text/geometry.rs - Reviews: T21a done, fixed after the cap, no fourth review
This commit is contained in:
1 parent
187f54253e
commit
14a8990793
7 files changed
+109
-74
No files matched your search
@@ -0,0 +1,68 @@
|
||||
use gpui::{Bounds, Pixels, Point};
|
||||
|
||||
use super::TextInput;
|
||||
|
||||
impl TextInput {
|
||||
/// The caret's box in window coordinates, once laid out.
|
||||
pub fn caret_bounds(&self) -> Option<Bounds<Pixels>> {
|
||||
self.bounds_for(self.cursor())
|
||||
}
|
||||
|
||||
/// An offset's box in the window, from the last layout; later offsets clamp to its end.
|
||||
pub fn bounds_for(&self, offset: usize) -> Option<Bounds<Pixels>> {
|
||||
let layout = self.layout.as_ref()?;
|
||||
let laid = layout
|
||||
.lines
|
||||
.last()
|
||||
.map_or(0, |(start, line)| start + line.len());
|
||||
let at = self.position_for(self.display_offset(offset).min(laid))?;
|
||||
Some(Bounds::new(
|
||||
layout.bounds.origin + at - self.scroll,
|
||||
gpui::size(Pixels::ZERO, layout.line_height),
|
||||
))
|
||||
}
|
||||
|
||||
/// Top-left of a display offset, relative to the text's origin.
|
||||
pub(crate) fn position_for(&self, display: usize) -> Option<Point<Pixels>> {
|
||||
let layout = self.layout.as_ref()?;
|
||||
if layout.placeholder {
|
||||
return Some(Point::default());
|
||||
}
|
||||
let mut top = Pixels::ZERO;
|
||||
for (start, line) in &layout.lines {
|
||||
if display <= start + line.len() {
|
||||
let at = line.position_for_index(display - start, layout.line_height)?;
|
||||
return Some(gpui::point(at.x, at.y + top));
|
||||
}
|
||||
top += line.size(layout.line_height).height;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// The content offset nearest a window position.
|
||||
pub(crate) fn offset_for_point(&self, point: Point<Pixels>) -> usize {
|
||||
let Some(layout) = self.layout.as_ref() else {
|
||||
return 0;
|
||||
};
|
||||
if layout.placeholder {
|
||||
return 0;
|
||||
}
|
||||
let local = point - layout.bounds.origin + self.scroll;
|
||||
if local.y < Pixels::ZERO {
|
||||
return 0;
|
||||
}
|
||||
let mut top = Pixels::ZERO;
|
||||
for (start, line) in &layout.lines {
|
||||
let height = line.size(layout.line_height).height;
|
||||
if local.y < top + height {
|
||||
let inside = gpui::point(local.x.max(Pixels::ZERO), local.y - top);
|
||||
let index = match line.closest_index_for_position(inside, layout.line_height) {
|
||||
Ok(index) | Err(index) => index,
|
||||
};
|
||||
return self.content_offset(start + index.min(line.len()));
|
||||
}
|
||||
top += height;
|
||||
}
|
||||
self.text.len()
|
||||
}
|
||||
}
|
||||
+10
-63
@@ -1,6 +1,7 @@
|
||||
mod actions;
|
||||
mod edit;
|
||||
mod element;
|
||||
mod geometry;
|
||||
mod highlight;
|
||||
mod ime;
|
||||
|
||||
@@ -61,6 +62,8 @@ pub struct TextInput {
|
||||
highlighter: Option<Highlighter>,
|
||||
disabled: bool,
|
||||
history: History<Snapshot>,
|
||||
/// The selection the last edit replaced.
|
||||
edited_from: Range<usize>,
|
||||
pub(crate) layout: Option<Layout>,
|
||||
pub(crate) scroll: Point<Pixels>,
|
||||
selecting: bool,
|
||||
@@ -109,6 +112,7 @@ impl TextInput {
|
||||
highlighter: None,
|
||||
disabled: false,
|
||||
history: History::default(),
|
||||
edited_from: 0..0,
|
||||
layout: None,
|
||||
scroll: Point::default(),
|
||||
selecting: false,
|
||||
@@ -250,25 +254,6 @@ impl TextInput {
|
||||
self.restart_blink(cx);
|
||||
}
|
||||
|
||||
/// The caret's box in window coordinates, once laid out.
|
||||
pub fn caret_bounds(&self) -> Option<Bounds<Pixels>> {
|
||||
self.bounds_for(self.cursor())
|
||||
}
|
||||
|
||||
/// An offset's box in the window, from the last layout; later offsets clamp to its end.
|
||||
pub fn bounds_for(&self, offset: usize) -> Option<Bounds<Pixels>> {
|
||||
let layout = self.layout.as_ref()?;
|
||||
let laid = layout
|
||||
.lines
|
||||
.last()
|
||||
.map_or(0, |(start, line)| start + line.len());
|
||||
let at = self.position_for(self.display_offset(offset).min(laid))?;
|
||||
Some(Bounds::new(
|
||||
layout.bounds.origin + at - self.scroll,
|
||||
gpui::size(Pixels::ZERO, layout.line_height),
|
||||
))
|
||||
}
|
||||
|
||||
/// Keeps what `filter` and `max_len` allow of `incoming`.
|
||||
fn admit(&self, incoming: &str, keep: usize) -> String {
|
||||
let allowed: String = incoming
|
||||
@@ -281,6 +266,11 @@ impl TextInput {
|
||||
}
|
||||
}
|
||||
|
||||
/// The selection the last edit replaced, for an owner's undo to go back to.
|
||||
pub(crate) fn edited_from(&self) -> Range<usize> {
|
||||
self.edited_from.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn replace(
|
||||
&mut self,
|
||||
range: Range<usize>,
|
||||
@@ -291,6 +281,7 @@ impl TextInput {
|
||||
if self.disabled {
|
||||
return;
|
||||
}
|
||||
self.edited_from = self.selection.clone();
|
||||
let incoming = if self.rows.is_none() {
|
||||
incoming.replace(['\n', '\r'], " ")
|
||||
} else {
|
||||
@@ -423,50 +414,6 @@ impl TextInput {
|
||||
}
|
||||
}
|
||||
|
||||
/// Top-left of a display offset, relative to the text's origin.
|
||||
pub(crate) fn position_for(&self, display: usize) -> Option<Point<Pixels>> {
|
||||
let layout = self.layout.as_ref()?;
|
||||
if layout.placeholder {
|
||||
return Some(Point::default());
|
||||
}
|
||||
let mut top = Pixels::ZERO;
|
||||
for (start, line) in &layout.lines {
|
||||
if display <= start + line.len() {
|
||||
let at = line.position_for_index(display - start, layout.line_height)?;
|
||||
return Some(gpui::point(at.x, at.y + top));
|
||||
}
|
||||
top += line.size(layout.line_height).height;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// The content offset nearest a window position.
|
||||
pub(crate) fn offset_for_point(&self, point: Point<Pixels>) -> usize {
|
||||
let Some(layout) = self.layout.as_ref() else {
|
||||
return 0;
|
||||
};
|
||||
if layout.placeholder {
|
||||
return 0;
|
||||
}
|
||||
let local = point - layout.bounds.origin + self.scroll;
|
||||
if local.y < Pixels::ZERO {
|
||||
return 0;
|
||||
}
|
||||
let mut top = Pixels::ZERO;
|
||||
for (start, line) in &layout.lines {
|
||||
let height = line.size(layout.line_height).height;
|
||||
if local.y < top + height {
|
||||
let inside = gpui::point(local.x.max(Pixels::ZERO), local.y - top);
|
||||
let index = match line.closest_index_for_position(inside, layout.line_height) {
|
||||
Ok(index) | Err(index) => index,
|
||||
};
|
||||
return self.content_offset(start + index.min(line.len()));
|
||||
}
|
||||
top += height;
|
||||
}
|
||||
self.text.len()
|
||||
}
|
||||
|
||||
pub(crate) fn highlights(&self, cx: &App) -> Vec<(Range<usize>, Highlight)> {
|
||||
match (&self.highlighter, self.masked) {
|
||||
(Some(highlight), false) => highlight(&self.text, cx),
|
||||
|
||||
Reference in new issue
Block a user