//! Edit text. use crate::text::highlighter::{self, Highlighter}; use crate::text::{LineHeight, Wrapping}; use crate::{Pixels, Point, Rectangle, Size}; use std::borrow::Cow; 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 true if the [`Editor`] has no contents. fn is_empty(&self) -> bool; /// 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; /// Returns the text of the given line in the [`Editor`], if it exists. fn line(&self, index: usize) -> Option>; /// 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; /// Returns the minimum boundaries to fit the current contents of /// the [`Editor`]. fn min_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_wrapping: Wrapping, new_highlighter: &mut impl Highlighter, ); /// Runs a text [`Highlighter`] in the [`Editor`]. fn highlight( &mut self, font: Self::Font, highlighter: &mut H, format_highlight: impl Fn(&H::Highlight) -> highlighter::Format, ); } /// 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, /// Select the entire buffer. SelectAll, /// 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), /// 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), } /// A line of an [`Editor`]. #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct Line<'a> { /// The raw text of the [`Line`]. pub text: Cow<'a, str>, /// The line ending of the [`Line`]. pub ending: LineEnding, } /// The line ending of a [`Line`]. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub enum LineEnding { /// Use `\n` for line ending (POSIX-style) #[default] Lf, /// Use `\r\n` for line ending (Windows-style) CrLf, /// Use `\r` for line ending (many legacy systems) Cr, /// Use `\n\r` for line ending (some legacy systems) LfCr, /// No line ending None, } impl LineEnding { /// Gets the string representation of the [`LineEnding`]. pub fn as_str(self) -> &'static str { match self { Self::Lf => "\n", Self::CrLf => "\r\n", Self::Cr => "\r", Self::LfCr => "\n\r", Self::None => "", } } }