iced_core/text/editor.rs
1//! Edit text.
2use crate::text::highlighter::{self, Highlighter};
3use crate::text::{LineHeight, Wrapping};
4use crate::{Pixels, Point, Rectangle, Size};
5
6use std::borrow::Cow;
7use std::sync::Arc;
8
9/// A component that can be used by widgets to edit multi-line text.
10pub trait Editor: Sized + Default {
11 /// The font of the [`Editor`].
12 type Font: Copy + PartialEq + Default;
13
14 /// Creates a new [`Editor`] laid out with the given text.
15 fn with_text(text: &str) -> Self;
16
17 /// Returns true if the [`Editor`] has no contents.
18 fn is_empty(&self) -> bool;
19
20 /// Returns the current [`Cursor`] of the [`Editor`].
21 fn cursor(&self) -> Cursor;
22
23 /// Returns the current cursor position of the [`Editor`].
24 ///
25 /// Line and column, respectively.
26 fn cursor_position(&self) -> (usize, usize);
27
28 /// Returns the current selected text of the [`Editor`].
29 fn selection(&self) -> Option<String>;
30
31 /// Returns the text of the given line in the [`Editor`], if it exists.
32 fn line(&self, index: usize) -> Option<Line<'_>>;
33
34 /// Returns the amount of lines in the [`Editor`].
35 fn line_count(&self) -> usize;
36
37 /// Performs an [`Action`] on the [`Editor`].
38 fn perform(&mut self, action: Action);
39
40 /// Returns the current boundaries of the [`Editor`].
41 fn bounds(&self) -> Size;
42
43 /// Returns the minimum boundaries to fit the current contents of
44 /// the [`Editor`].
45 fn min_bounds(&self) -> Size;
46
47 /// Updates the [`Editor`] with some new attributes.
48 fn update(
49 &mut self,
50 new_bounds: Size,
51 new_font: Self::Font,
52 new_size: Pixels,
53 new_line_height: LineHeight,
54 new_wrapping: Wrapping,
55 new_highlighter: &mut impl Highlighter,
56 );
57
58 /// Runs a text [`Highlighter`] in the [`Editor`].
59 fn highlight<H: Highlighter>(
60 &mut self,
61 font: Self::Font,
62 highlighter: &mut H,
63 format_highlight: impl Fn(&H::Highlight) -> highlighter::Format<Self::Font>,
64 );
65}
66
67/// An interaction with an [`Editor`].
68#[derive(Debug, Clone, PartialEq)]
69pub enum Action {
70 /// Apply a [`Motion`].
71 Move(Motion),
72 /// Select text with a given [`Motion`].
73 Select(Motion),
74 /// Select the word at the current cursor.
75 SelectWord,
76 /// Select the line at the current cursor.
77 SelectLine,
78 /// Select the entire buffer.
79 SelectAll,
80 /// Perform an [`Edit`].
81 Edit(Edit),
82 /// Click the [`Editor`] at the given [`Point`].
83 Click(Point),
84 /// Drag the mouse on the [`Editor`] to the given [`Point`].
85 Drag(Point),
86 /// Scroll the [`Editor`] a certain amount of lines.
87 Scroll {
88 /// The amount of lines to scroll.
89 lines: i32,
90 },
91}
92
93impl Action {
94 /// Returns whether the [`Action`] is an editing action.
95 pub fn is_edit(&self) -> bool {
96 matches!(self, Self::Edit(_))
97 }
98}
99
100/// An action that edits text.
101#[derive(Debug, Clone, PartialEq)]
102pub enum Edit {
103 /// Insert the given character.
104 Insert(char),
105 /// Paste the given text.
106 Paste(Arc<String>),
107 /// Break the current line.
108 Enter,
109 /// Delete the previous character.
110 Backspace,
111 /// Delete the next character.
112 Delete,
113}
114
115/// A cursor movement.
116#[derive(Debug, Clone, Copy, PartialEq)]
117pub enum Motion {
118 /// Move left.
119 Left,
120 /// Move right.
121 Right,
122 /// Move up.
123 Up,
124 /// Move down.
125 Down,
126 /// Move to the left boundary of a word.
127 WordLeft,
128 /// Move to the right boundary of a word.
129 WordRight,
130 /// Move to the start of the line.
131 Home,
132 /// Move to the end of the line.
133 End,
134 /// Move to the start of the previous window.
135 PageUp,
136 /// Move to the start of the next window.
137 PageDown,
138 /// Move to the start of the text.
139 DocumentStart,
140 /// Move to the end of the text.
141 DocumentEnd,
142}
143
144impl Motion {
145 /// Widens the [`Motion`], if possible.
146 pub fn widen(self) -> Self {
147 match self {
148 Self::Left => Self::WordLeft,
149 Self::Right => Self::WordRight,
150 Self::Home => Self::DocumentStart,
151 Self::End => Self::DocumentEnd,
152 _ => self,
153 }
154 }
155
156 /// Returns the [`Direction`] of the [`Motion`].
157 pub fn direction(&self) -> Direction {
158 match self {
159 Self::Left
160 | Self::Up
161 | Self::WordLeft
162 | Self::Home
163 | Self::PageUp
164 | Self::DocumentStart => Direction::Left,
165 Self::Right
166 | Self::Down
167 | Self::WordRight
168 | Self::End
169 | Self::PageDown
170 | Self::DocumentEnd => Direction::Right,
171 }
172 }
173}
174
175/// A direction in some text.
176#[derive(Debug, Clone, Copy, PartialEq, Eq)]
177pub enum Direction {
178 /// <-
179 Left,
180 /// ->
181 Right,
182}
183
184/// The cursor of an [`Editor`].
185#[derive(Debug, Clone)]
186pub enum Cursor {
187 /// Cursor without a selection
188 Caret(Point),
189
190 /// Cursor selecting a range of text
191 Selection(Vec<Rectangle>),
192}
193
194/// A line of an [`Editor`].
195#[derive(Clone, Debug, Default, Eq, PartialEq)]
196pub struct Line<'a> {
197 /// The raw text of the [`Line`].
198 pub text: Cow<'a, str>,
199 /// The line ending of the [`Line`].
200 pub ending: LineEnding,
201}
202
203/// The line ending of a [`Line`].
204#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
205pub enum LineEnding {
206 /// Use `\n` for line ending (POSIX-style)
207 #[default]
208 Lf,
209 /// Use `\r\n` for line ending (Windows-style)
210 CrLf,
211 /// Use `\r` for line ending (many legacy systems)
212 Cr,
213 /// Use `\n\r` for line ending (some legacy systems)
214 LfCr,
215 /// No line ending
216 None,
217}
218
219impl LineEnding {
220 /// Gets the string representation of the [`LineEnding`].
221 pub fn as_str(self) -> &'static str {
222 match self {
223 Self::Lf => "\n",
224 Self::CrLf => "\r\n",
225 Self::Cr => "\r",
226 Self::LfCr => "\n\r",
227 Self::None => "",
228 }
229 }
230}