Skip to main content

TextEdit

Struct TextEdit 

Source
pub struct TextEdit {
    pub text: String,
    pub layout: Layout,
    pub caret: Place,
    pub previous_caret: Place,
    pub selection: Selection,
    pub sticky_x: f32,
    pub scroll: (f32, f32),
    pub auto_scroll: bool,
    pub offset: (f32, f32),
    pub centred: bool,
    pub undo: UndoStack,
}
Expand description

A text field’s editing state.

The invariant is one sentence: caret and both ends of selection are places in layout, and layout is what laying text out again would produce.


let mut edit = TextEdit::new("Hello", &config, &metrics, true);
assert_eq!(edit.text, "Hello");
// A fresh control's caret sits at the line header, before the text.
assert_eq!(edit.caret_index(), 0);
assert!(!edit.has_selection());

edit.set_selection(0, 4);
assert_eq!(edit.selected_text(), "Hell");

Fields§

§text: String

The text as the user has it, which may differ from the field’s stored value until the edit commits.

§layout: Layout

The laid-out form of that text.

§caret: Place

Where the caret is.

§previous_caret: Place

The caret’s position before the last movement, which a shift-move anchors on when no anchor exists yet.

§selection: Selection

The selection: directional, empty when its ends agree.

§sticky_x: f32

The column a vertical move aims for, in layout space.

Re-seeded by every horizontal move and every mutation, and deliberately not by a vertical one — which is what lets a run of up-arrows through a ragged paragraph keep returning to the column the caret started in.

§scroll: (f32, f32)

How far the view is scrolled, in layout space.

A distance, not a position: LiveState::shift negates it to move the drawn text. Upstream’s scroll_pos_point_ is the same quantity seeded at rcPlate.left, so ours is upstream’s minus that — see scroll_to_caret.

§auto_scroll: bool

Whether the view follows the caret out of the plate.

True for any text field without the DoNotScroll flag — single-line and multi-line alike. It gates every writer of the scroll position, so a field that declines it never moves its view at all, however far past the plate the caret goes.

§offset: (f32, f32)

The vertical alignment offset the text is drawn with.

A single-line field is drawn vertically centred in its plate, so its glyphs sit below where the layout placed them. Every geometric query — turning a click into a place, a place into a caret rectangle — has to be told about that shift or it works against the wrong box: a click at a real field’s mid-height falls below the content and clamps to the end of the text, silently, putting the caret at the end of the field instead of where it was clicked.

It is recomputed on every relayout because it depends on the content’s height, which an edit changes.

§centred: bool

Whether the field draws its text vertically centred.

§undo: UndoStack

The undo stack.

Implementations§

Source§

impl TextEdit

Source

pub fn new( text: impl Into<String>, config: &Config, metrics: &Metrics<'_>, centred: bool, ) -> TextEdit

An edit control over text, laid out with config.

centred says whether the field draws its text vertically centred, which a single-line field does and a multiline one does not.

The text is normalized to what the layout will hold before it is stored — see normalize_breaks. Storing the caller’s string unchanged would break the invariant this type exists to keep: a single-line field’s layout silently drops line breaks, so a raw "Foo\nBar" would report seven characters while the layout held six, and every index derived from one would miss in the other.


// The text arrives normalized, so the control and its layout agree.
let edit = TextEdit::new("Foo\nBar", &config, &metrics, true);
assert_eq!(edit.text, "FooBar");
assert_eq!(edit.len_chars(), 6);
Source

pub fn caret_index(&self) -> usize

The caret’s flat character index.


let mut edit = TextEdit::new("Hello", &config, &metrics, true);
// A fresh control starts at the line header, index zero.
assert_eq!(edit.caret_index(), 0);
edit.set_caret_index(2);
assert_eq!(edit.caret_index(), 2);
Source

pub fn selection_indices(&self) -> (usize, usize)

The selection’s flat character range, ordered.

Ordered whichever way the selection runs, so a backwards selection answers the same pair as the forwards one over the same run.


let mut edit = TextEdit::new("ABCDEF", &config, &metrics, true);
edit.set_selection(4, 1);
assert_eq!(edit.selection_indices(), (1, 4));
Source

pub fn selected_text(&self) -> String

The selected text, empty when nothing is selected.


let mut edit = TextEdit::new("ABCDEF", &config, &metrics, true);
assert_eq!(edit.selected_text(), "");
edit.set_selection(1, 4);
assert_eq!(edit.selected_text(), "BCD");
Source

pub fn has_selection(&self) -> bool

Whether anything is selected.


let mut edit = TextEdit::new("ABCDEF", &config, &metrics, true);
assert!(!edit.has_selection());
edit.select_all();
assert!(edit.has_selection());
Source

pub fn len_chars(&self) -> usize

How many characters the text holds.

Characters, not bytes: a field of Hebrew letters is as long as it looks.


let edit = TextEdit::new("\u{05D1}\u{05D2}\u{05EA}", &config, &metrics, true);
assert_eq!(edit.len_chars(), 3);
Source

pub fn set_caret_index(&mut self, index: usize)

Moves the caret to a flat character index, collapsing the selection.

The selection is left collapsed at the new caret, which is a live anchor rather than no anchor.


let mut edit = TextEdit::new("ABCDEF", &config, &metrics, true);
edit.select_all();
edit.set_caret_index(2);
assert_eq!(edit.caret_index(), 2);
assert!(!edit.has_selection());
Source

pub fn move_caret_keeping_selection(&mut self, index: usize)

Moves the caret without touching the selection — what a shift-move needs, since it must extend from an anchor the caret is leaving.


let mut edit = TextEdit::new("ABCDEF", &config, &metrics, true);
edit.set_selection(1, 4);
edit.move_caret_keeping_selection(0);
assert_eq!(edit.caret_index(), 0);
// The selection is untouched, unlike after `set_caret_index`.
assert_eq!(edit.selected_text(), "BCD");
Source

pub fn select_all(&mut self)

Selects everything. Records no undo item — selecting is not an edit.


let mut edit = TextEdit::new("Hello", &config, &metrics, true);
edit.select_all();
assert_eq!(edit.selected_text(), "Hello");
assert!(!edit.undo.can_undo(), "selecting records nothing");

// An empty field has nothing to select.
let mut blank = TextEdit::new("", &config, &metrics, true);
blank.select_all();
assert_eq!(blank.selected_text(), "");
Source

pub fn select_none(&mut self)

Drops the selection, leaving a live anchor at the caret.


let mut edit = TextEdit::new("Hello", &config, &metrics, true);
edit.select_all();
edit.select_none();
assert!(!edit.has_selection());
// Collapsed, not reset: the next shift-move extends from here.
assert!(!edit.selection.is_reset());
Source

pub fn set_selection(&mut self, start: i32, end: i32)

Selects a signed character range, the way an embedder asks for one.

The signs are not an accident of the C API and are not clamping: they are three distinct instructions sharing one signature, and the order they are tested in is what makes them unambiguous.

  • (0, negative) selects everything. This is the documented spelling of “to the end”, and it is tested first, so it wins over the rule below even though its end is also negative.
  • (negative, anything) selects nothing. A negative start is not clamped to zero — it clears the selection outright, so (-8, -1) is empty rather than the whole field.
  • otherwise the two are ordered and used as they are, so (23, 12) and (12, 23) select the same run. An end past the text clamps to its end, which is ordinary index saturation rather than a fourth rule.

let mut edit = TextEdit::new("ABCDEFGHIJ", &config, &metrics, true);

// (0, negative) is "to the end".
edit.set_selection(0, -1);
assert_eq!(edit.selected_text(), "ABCDEFGHIJ");

// A negative start selects nothing; it is not clamped to zero.
edit.set_selection(-8, -1);
assert_eq!(edit.selected_text(), "");

// Otherwise the two are ordered, so either way round is the same run.
edit.set_selection(5, 2);
assert_eq!(edit.selected_text(), "CDE");
edit.set_selection(2, 5);
assert_eq!(edit.selected_text(), "CDE");

// An end past the text clamps, which is ordinary saturation.
edit.set_selection(9, 99);
assert_eq!(edit.selected_text(), "J");

Trait Implementations§

Source§

impl Clone for TextEdit

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TextEdit

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for TextEdit

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for TextEdit

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Conv for T

Source§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pipe for T
where T: ?Sized,

Source§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
Source§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
Source§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
Source§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
Source§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
Source§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
Source§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
Source§

impl<T> Tap for T

Source§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
Source§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
Source§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
Source§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
Source§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
Source§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
Source§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
Source§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
Source§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
Source§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> TryConv for T

Source§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.