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 /// Indent the current line.
110 Indent,
111 /// Unindent the current line.
112 Unindent,
113 /// Delete the previous character.
114 Backspace,
115 /// Delete the next character.
116 Delete,
117}
118
119/// A cursor movement.
120#[derive(Debug, Clone, Copy, PartialEq)]
121pub enum Motion {
122 /// Move left.
123 Left,
124 /// Move right.
125 Right,
126 /// Move up.
127 Up,
128 /// Move down.
129 Down,
130 /// Move to the left boundary of a word.
131 WordLeft,
132 /// Move to the right boundary of a word.
133 WordRight,
134 /// Move to the start of the line.
135 Home,
136 /// Move to the end of the line.
137 End,
138 /// Move to the start of the previous window.
139 PageUp,
140 /// Move to the start of the next window.
141 PageDown,
142 /// Move to the start of the text.
143 DocumentStart,
144 /// Move to the end of the text.
145 DocumentEnd,
146}
147
148impl Motion {
149 /// Widens the [`Motion`], if possible.
150 pub fn widen(self) -> Self {
151 match self {
152 Self::Left => Self::WordLeft,
153 Self::Right => Self::WordRight,
154 Self::Home => Self::DocumentStart,
155 Self::End => Self::DocumentEnd,
156 _ => self,
157 }
158 }
159
160 /// Returns the [`Direction`] of the [`Motion`].
161 pub fn direction(&self) -> Direction {
162 match self {
163 Self::Left
164 | Self::Up
165 | Self::WordLeft
166 | Self::Home
167 | Self::PageUp
168 | Self::DocumentStart => Direction::Left,
169 Self::Right
170 | Self::Down
171 | Self::WordRight
172 | Self::End
173 | Self::PageDown
174 | Self::DocumentEnd => Direction::Right,
175 }
176 }
177}
178
179/// A direction in some text.
180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
181pub enum Direction {
182 /// <-
183 Left,
184 /// ->
185 Right,
186}
187
188/// The cursor of an [`Editor`].
189#[derive(Debug, Clone)]
190pub enum Cursor {
191 /// Cursor without a selection
192 Caret(Point),
193
194 /// Cursor selecting a range of text
195 Selection(Vec<Rectangle>),
196}
197
198/// A line of an [`Editor`].
199#[derive(Clone, Debug, Default, Eq, PartialEq)]
200pub struct Line<'a> {
201 /// The raw text of the [`Line`].
202 pub text: Cow<'a, str>,
203 /// The line ending of the [`Line`].
204 pub ending: LineEnding,
205}
206
207/// The line ending of a [`Line`].
208#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
209pub enum LineEnding {
210 /// Use `\n` for line ending (POSIX-style)
211 #[default]
212 Lf,
213 /// Use `\r\n` for line ending (Windows-style)
214 CrLf,
215 /// Use `\r` for line ending (many legacy systems)
216 Cr,
217 /// Use `\n\r` for line ending (some legacy systems)
218 LfCr,
219 /// No line ending
220 None,
221}
222
223impl LineEnding {
224 /// Gets the string representation of the [`LineEnding`].
225 pub fn as_str(self) -> &'static str {
226 match self {
227 Self::Lf => "\n",
228 Self::CrLf => "\r\n",
229 Self::Cr => "\r",
230 Self::LfCr => "\n\r",
231 Self::None => "",
232 }
233 }
234}