diff options
author | 2024-01-19 20:41:52 +0100 | |
---|---|---|
committer | 2024-01-19 20:41:52 +0100 | |
commit | 1781068e1c3a65551db1e832fdbaddba99124051 (patch) | |
tree | 60e0b3854cc0541712572fbb0e56f14435951ea9 /core/src/text | |
parent | 41dec5bd203ff5b1574a33a17d5f7358ae1beea2 (diff) | |
parent | 7ae7fcb89855002519bab752fd3686106ce448db (diff) | |
download | iced-1781068e1c3a65551db1e832fdbaddba99124051.tar.gz iced-1781068e1c3a65551db1e832fdbaddba99124051.tar.bz2 iced-1781068e1c3a65551db1e832fdbaddba99124051.zip |
Merge branch 'master' into remove-vertex-indexing
Diffstat (limited to 'core/src/text')
-rw-r--r-- | core/src/text/editor.rs | 181 | ||||
-rw-r--r-- | core/src/text/highlighter.rs | 88 | ||||
-rw-r--r-- | core/src/text/paragraph.rs | 59 |
3 files changed, 328 insertions, 0 deletions
diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs new file mode 100644 index 00000000..f3c6e342 --- /dev/null +++ b/core/src/text/editor.rs @@ -0,0 +1,181 @@ +//! Edit text. +use crate::text::highlighter::{self, Highlighter}; +use crate::text::LineHeight; +use crate::{Pixels, Point, Rectangle, Size}; + +use std::sync::Arc; + +/// A component that can be used by widgets to edit multi-line text. +pub trait Editor: Sized + Default { + /// The font of the [`Editor`]. + type Font: Copy + PartialEq + Default; + + /// Creates a new [`Editor`] laid out with the given text. + fn with_text(text: &str) -> Self; + + /// Returns the current [`Cursor`] of the [`Editor`]. + fn cursor(&self) -> Cursor; + + /// Returns the current cursor position of the [`Editor`]. + /// + /// Line and column, respectively. + fn cursor_position(&self) -> (usize, usize); + + /// Returns the current selected text of the [`Editor`]. + fn selection(&self) -> Option<String>; + + /// Returns the text of the given line in the [`Editor`], if it exists. + fn line(&self, index: usize) -> Option<&str>; + + /// Returns the amount of lines in the [`Editor`]. + fn line_count(&self) -> usize; + + /// Performs an [`Action`] on the [`Editor`]. + fn perform(&mut self, action: Action); + + /// Returns the current boundaries of the [`Editor`]. + fn bounds(&self) -> Size; + + /// Updates the [`Editor`] with some new attributes. + fn update( + &mut self, + new_bounds: Size, + new_font: Self::Font, + new_size: Pixels, + new_line_height: LineHeight, + new_highlighter: &mut impl Highlighter, + ); + + /// Runs a text [`Highlighter`] in the [`Editor`]. + fn highlight<H: Highlighter>( + &mut self, + font: Self::Font, + highlighter: &mut H, + format_highlight: impl Fn(&H::Highlight) -> highlighter::Format<Self::Font>, + ); +} + +/// An interaction with an [`Editor`]. +#[derive(Debug, Clone, PartialEq)] +pub enum Action { + /// Apply a [`Motion`]. + Move(Motion), + /// Select text with a given [`Motion`]. + Select(Motion), + /// Select the word at the current cursor. + SelectWord, + /// Select the line at the current cursor. + SelectLine, + /// Perform an [`Edit`]. + Edit(Edit), + /// Click the [`Editor`] at the given [`Point`]. + Click(Point), + /// Drag the mouse on the [`Editor`] to the given [`Point`]. + Drag(Point), + /// Scroll the [`Editor`] a certain amount of lines. + Scroll { + /// The amount of lines to scroll. + lines: i32, + }, +} + +impl Action { + /// Returns whether the [`Action`] is an editing action. + pub fn is_edit(&self) -> bool { + matches!(self, Self::Edit(_)) + } +} + +/// An action that edits text. +#[derive(Debug, Clone, PartialEq)] +pub enum Edit { + /// Insert the given character. + Insert(char), + /// Paste the given text. + Paste(Arc<String>), + /// Break the current line. + Enter, + /// Delete the previous character. + Backspace, + /// Delete the next character. + Delete, +} + +/// A cursor movement. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Motion { + /// Move left. + Left, + /// Move right. + Right, + /// Move up. + Up, + /// Move down. + Down, + /// Move to the left boundary of a word. + WordLeft, + /// Move to the right boundary of a word. + WordRight, + /// Move to the start of the line. + Home, + /// Move to the end of the line. + End, + /// Move to the start of the previous window. + PageUp, + /// Move to the start of the next window. + PageDown, + /// Move to the start of the text. + DocumentStart, + /// Move to the end of the text. + DocumentEnd, +} + +impl Motion { + /// Widens the [`Motion`], if possible. + pub fn widen(self) -> Self { + match self { + Self::Left => Self::WordLeft, + Self::Right => Self::WordRight, + Self::Home => Self::DocumentStart, + Self::End => Self::DocumentEnd, + _ => self, + } + } + + /// Returns the [`Direction`] of the [`Motion`]. + pub fn direction(&self) -> Direction { + match self { + Self::Left + | Self::Up + | Self::WordLeft + | Self::Home + | Self::PageUp + | Self::DocumentStart => Direction::Left, + Self::Right + | Self::Down + | Self::WordRight + | Self::End + | Self::PageDown + | Self::DocumentEnd => Direction::Right, + } + } +} + +/// A direction in some text. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Direction { + /// <- + Left, + /// -> + Right, +} + +/// The cursor of an [`Editor`]. +#[derive(Debug, Clone)] +pub enum Cursor { + /// Cursor without a selection + Caret(Point), + + /// Cursor selecting a range of text + Selection(Vec<Rectangle>), +} diff --git a/core/src/text/highlighter.rs b/core/src/text/highlighter.rs new file mode 100644 index 00000000..a0535228 --- /dev/null +++ b/core/src/text/highlighter.rs @@ -0,0 +1,88 @@ +//! Highlight text. +use crate::Color; + +use std::ops::Range; + +/// A type capable of highlighting text. +/// +/// A [`Highlighter`] highlights lines in sequence. When a line changes, +/// it must be notified and the lines after the changed one must be fed +/// again to the [`Highlighter`]. +pub trait Highlighter: 'static { + /// The settings to configure the [`Highlighter`]. + type Settings: PartialEq + Clone; + + /// The output of the [`Highlighter`]. + type Highlight; + + /// The highlight iterator type. + type Iterator<'a>: Iterator<Item = (Range<usize>, Self::Highlight)> + where + Self: 'a; + + /// Creates a new [`Highlighter`] from its [`Self::Settings`]. + fn new(settings: &Self::Settings) -> Self; + + /// Updates the [`Highlighter`] with some new [`Self::Settings`]. + fn update(&mut self, new_settings: &Self::Settings); + + /// Notifies the [`Highlighter`] that the line at the given index has changed. + fn change_line(&mut self, line: usize); + + /// Highlights the given line. + /// + /// If a line changed prior to this, the first line provided here will be the + /// line that changed. + fn highlight_line(&mut self, line: &str) -> Self::Iterator<'_>; + + /// Returns the current line of the [`Highlighter`]. + /// + /// If `change_line` has been called, this will normally be the least index + /// that changed. + fn current_line(&self) -> usize; +} + +/// A highlighter that highlights nothing. +#[derive(Debug, Clone, Copy)] +pub struct PlainText; + +impl Highlighter for PlainText { + type Settings = (); + type Highlight = (); + + type Iterator<'a> = std::iter::Empty<(Range<usize>, Self::Highlight)>; + + fn new(_settings: &Self::Settings) -> Self { + Self + } + + fn update(&mut self, _new_settings: &Self::Settings) {} + + fn change_line(&mut self, _line: usize) {} + + fn highlight_line(&mut self, _line: &str) -> Self::Iterator<'_> { + std::iter::empty() + } + + fn current_line(&self) -> usize { + usize::MAX + } +} + +/// The format of some text. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Format<Font> { + /// The [`Color`] of the text. + pub color: Option<Color>, + /// The `Font` of the text. + pub font: Option<Font>, +} + +impl<Font> Default for Format<Font> { + fn default() -> Self { + Self { + color: None, + font: None, + } + } +} diff --git a/core/src/text/paragraph.rs b/core/src/text/paragraph.rs new file mode 100644 index 00000000..de1fb74d --- /dev/null +++ b/core/src/text/paragraph.rs @@ -0,0 +1,59 @@ +use crate::alignment; +use crate::text::{Difference, Hit, Text}; +use crate::{Point, Size}; + +/// A text paragraph. +pub trait Paragraph: Sized + Default { + /// The font of this [`Paragraph`]. + type Font: Copy + PartialEq; + + /// Creates a new [`Paragraph`] laid out with the given [`Text`]. + fn with_text(text: Text<'_, Self::Font>) -> Self; + + /// Lays out the [`Paragraph`] with some new boundaries. + fn resize(&mut self, new_bounds: Size); + + /// Compares the [`Paragraph`] with some desired [`Text`] and returns the + /// [`Difference`]. + fn compare(&self, text: Text<'_, Self::Font>) -> Difference; + + /// Returns the horizontal alignment of the [`Paragraph`]. + fn horizontal_alignment(&self) -> alignment::Horizontal; + + /// Returns the vertical alignment of the [`Paragraph`]. + fn vertical_alignment(&self) -> alignment::Vertical; + + /// Returns the minimum boundaries that can fit the contents of the + /// [`Paragraph`]. + fn min_bounds(&self) -> Size; + + /// Tests whether the provided point is within the boundaries of the + /// [`Paragraph`], returning information about the nearest character. + fn hit_test(&self, point: Point) -> Option<Hit>; + + /// Returns the distance to the given grapheme index in the [`Paragraph`]. + fn grapheme_position(&self, line: usize, index: usize) -> Option<Point>; + + /// Updates the [`Paragraph`] to match the given [`Text`], if needed. + fn update(&mut self, text: Text<'_, Self::Font>) { + match self.compare(text) { + Difference::None => {} + Difference::Bounds => { + self.resize(text.bounds); + } + Difference::Shape => { + *self = Self::with_text(text); + } + } + } + + /// Returns the minimum width that can fit the contents of the [`Paragraph`]. + fn min_width(&self) -> f32 { + self.min_bounds().width + } + + /// Returns the minimum height that can fit the contents of the [`Paragraph`]. + fn min_height(&self) -> f32 { + self.min_bounds().height + } +} |