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 [`Selection`] of the [`Editor`].
24 fn selection(&self) -> Selection;
25
26 /// Returns the current selected text of the [`Editor`].
27 fn copy(&self) -> Option<String>;
28
29 /// Returns the text of the given line in the [`Editor`], if it exists.
30 fn line(&self, index: usize) -> Option<Line<'_>>;
31
32 /// Returns the amount of lines in the [`Editor`].
33 fn line_count(&self) -> usize;
34
35 /// Performs an [`Action`] on the [`Editor`].
36 fn perform(&mut self, action: Action);
37
38 /// Moves the cursor to the given position.
39 fn move_to(&mut self, cursor: Cursor);
40
41 /// Returns the current boundaries of the [`Editor`].
42 fn bounds(&self) -> Size;
43
44 /// Returns the minimum boundaries to fit the current contents of
45 /// the [`Editor`].
46 fn min_bounds(&self) -> Size;
47
48 /// Returns the hint factor of the [`Editor`].
49 fn hint_factor(&self) -> Option<f32>;
50
51 /// Updates the [`Editor`] with some new attributes.
52 fn update(
53 &mut self,
54 new_bounds: Size,
55 new_font: Self::Font,
56 new_size: Pixels,
57 new_line_height: LineHeight,
58 new_wrapping: Wrapping,
59 new_hint_factor: Option<f32>,
60 new_highlighter: &mut impl Highlighter,
61 );
62
63 /// Runs a text [`Highlighter`] in the [`Editor`].
64 fn highlight<H: Highlighter>(
65 &mut self,
66 font: Self::Font,
67 highlighter: &mut H,
68 format_highlight: impl Fn(&H::Highlight) -> highlighter::Format<Self::Font>,
69 );
70}
71
72/// An interaction with an [`Editor`].
73#[derive(Debug, Clone, PartialEq)]
74pub enum Action {
75 /// Apply a [`Motion`].
76 Move(Motion),
77 /// Select text with a given [`Motion`].
78 Select(Motion),
79 /// Select the word at the current cursor.
80 SelectWord,
81 /// Select the line at the current cursor.
82 SelectLine,
83 /// Select the entire buffer.
84 SelectAll,
85 /// Perform an [`Edit`].
86 Edit(Edit),
87 /// Click the [`Editor`] at the given [`Point`].
88 Click(Point),
89 /// Drag the mouse on the [`Editor`] to the given [`Point`].
90 Drag(Point),
91 /// Scroll the [`Editor`] a certain amount of lines.
92 Scroll {
93 /// The amount of lines to scroll.
94 lines: i32,
95 },
96 /// Undo the last editing action.
97 Undo,
98 /// Redo a previously undone editing action.
99 Redo,
100}
101
102impl Action {
103 /// Returns whether the [`Action`] is an editing action.
104 pub fn is_edit(&self) -> bool {
105 matches!(self, Self::Edit(_) | Self::Undo | Self::Redo)
106 }
107}
108
109/// An action that edits text.
110#[derive(Debug, Clone, PartialEq)]
111pub enum Edit {
112 /// Insert the given character.
113 Insert(char),
114 /// Paste the given text.
115 Paste(Arc<String>),
116 /// Break the current line.
117 Enter,
118 /// Indent the current line.
119 Indent,
120 /// Unindent the current line.
121 Unindent,
122 /// Delete the previous character.
123 Backspace,
124 /// Delete the next character.
125 Delete,
126}
127
128/// A cursor movement.
129#[derive(Debug, Clone, Copy, PartialEq)]
130pub enum Motion {
131 /// Move left.
132 Left,
133 /// Move right.
134 Right,
135 /// Move up.
136 Up,
137 /// Move down.
138 Down,
139 /// Move to the left boundary of a word.
140 WordLeft,
141 /// Move to the right boundary of a word.
142 WordRight,
143 /// Move to the start of the line.
144 Home,
145 /// Move to the end of the line.
146 End,
147 /// Move to the start of the previous window.
148 PageUp,
149 /// Move to the start of the next window.
150 PageDown,
151 /// Move to the start of the text.
152 DocumentStart,
153 /// Move to the end of the text.
154 DocumentEnd,
155}
156
157impl Motion {
158 /// Widens the [`Motion`], if possible.
159 pub fn widen(self) -> Self {
160 match self {
161 Self::Left => Self::WordLeft,
162 Self::Right => Self::WordRight,
163 Self::Home => Self::DocumentStart,
164 Self::End => Self::DocumentEnd,
165 _ => self,
166 }
167 }
168
169 /// Returns the [`Direction`] of the [`Motion`].
170 pub fn direction(&self) -> Direction {
171 match self {
172 Self::Left
173 | Self::Up
174 | Self::WordLeft
175 | Self::Home
176 | Self::PageUp
177 | Self::DocumentStart => Direction::Left,
178 Self::Right
179 | Self::Down
180 | Self::WordRight
181 | Self::End
182 | Self::PageDown
183 | Self::DocumentEnd => Direction::Right,
184 }
185 }
186}
187
188/// A direction in some text.
189#[derive(Debug, Clone, Copy, PartialEq, Eq)]
190pub enum Direction {
191 /// <-
192 Left,
193 /// ->
194 Right,
195}
196
197/// The cursor of an [`Editor`].
198#[derive(Debug, Clone)]
199pub enum Selection {
200 /// Cursor without a selection
201 Caret(Point),
202
203 /// Cursor selecting a range of text
204 Range(Vec<Rectangle>),
205}
206
207/// The range of an [`Editor`].
208#[derive(Debug, Clone, Copy, PartialEq)]
209pub struct Cursor {
210 /// The cursor position.
211 pub position: Position,
212
213 /// The selection position, if any.
214 pub selection: Option<Position>,
215}
216
217/// A cursor position in an [`Editor`].
218#[derive(Debug, Clone, Copy, PartialEq)]
219pub struct Position {
220 /// The line of text.
221 pub line: usize,
222 /// The column in the line.
223 pub column: usize,
224}
225
226/// A line of an [`Editor`].
227#[derive(Clone, Debug, Default, Eq, PartialEq)]
228pub struct Line<'a> {
229 /// The raw text of the [`Line`].
230 pub text: Cow<'a, str>,
231 /// The line ending of the [`Line`].
232 pub ending: LineEnding,
233}
234
235/// The line ending of a [`Line`].
236#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
237pub enum LineEnding {
238 /// Use `\n` for line ending (POSIX-style)
239 #[default]
240 Lf,
241 /// Use `\r\n` for line ending (Windows-style)
242 CrLf,
243 /// Use `\r` for line ending (many legacy systems)
244 Cr,
245 /// Use `\n\r` for line ending (some legacy systems)
246 LfCr,
247 /// No line ending
248 None,
249}
250
251impl LineEnding {
252 /// Gets the string representation of the [`LineEnding`].
253 pub fn as_str(self) -> &'static str {
254 match self {
255 Self::Lf => "\n",
256 Self::CrLf => "\r\n",
257 Self::Cr => "\r",
258 Self::LfCr => "\n\r",
259 Self::None => "",
260 }
261 }
262}