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