1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | import 'package:flutter/services.dart'; |
6 | |
7 | import 'actions.dart'; |
8 | |
9 | /// An [Intent] to send the event straight to the engine. |
10 | /// |
11 | /// See also: |
12 | /// |
13 | /// * [DefaultTextEditingShortcuts], which triggers this [Intent]. |
14 | class DoNothingAndStopPropagationTextIntent extends Intent { |
15 | /// Creates an instance of [DoNothingAndStopPropagationTextIntent]. |
16 | const DoNothingAndStopPropagationTextIntent(); |
17 | } |
18 | |
19 | /// A text editing related [Intent] that performs an operation towards a given |
20 | /// direction of the current caret location. |
21 | abstract class DirectionalTextEditingIntent extends Intent { |
22 | /// Creates a [DirectionalTextEditingIntent]. |
23 | const DirectionalTextEditingIntent( |
24 | this.forward, |
25 | ); |
26 | |
27 | /// Whether the input field, if applicable, should perform the text editing |
28 | /// operation from the current caret location towards the end of the document. |
29 | /// |
30 | /// Unless otherwise specified by the recipient of this intent, this parameter |
31 | /// uses the logical order of characters in the string to determine the |
32 | /// direction, and is not affected by the writing direction of the text. |
33 | final bool forward; |
34 | } |
35 | |
36 | /// Deletes the character before or after the caret location, based on whether |
37 | /// `forward` is true. |
38 | /// |
39 | /// {@template flutter.widgets.TextEditingIntents.logicalOrder} |
40 | /// {@endtemplate} |
41 | /// |
42 | /// Typically a text field will not respond to this intent if it has no active |
43 | /// caret ([TextSelection.isValid] is false for the current selection). |
44 | class DeleteCharacterIntent extends DirectionalTextEditingIntent { |
45 | /// Creates a [DeleteCharacterIntent]. |
46 | const DeleteCharacterIntent({ required bool forward }) : super(forward); |
47 | } |
48 | |
49 | /// Deletes from the current caret location to the previous or next word |
50 | /// boundary, based on whether `forward` is true. |
51 | class DeleteToNextWordBoundaryIntent extends DirectionalTextEditingIntent { |
52 | /// Creates a [DeleteToNextWordBoundaryIntent]. |
53 | const DeleteToNextWordBoundaryIntent({ required bool forward }) : super(forward); |
54 | } |
55 | |
56 | /// Deletes from the current caret location to the previous or next soft or hard |
57 | /// line break, based on whether `forward` is true. |
58 | class DeleteToLineBreakIntent extends DirectionalTextEditingIntent { |
59 | /// Creates a [DeleteToLineBreakIntent]. |
60 | const DeleteToLineBreakIntent({ required bool forward }) : super(forward); |
61 | } |
62 | |
63 | /// A [DirectionalTextEditingIntent] that moves the caret or the selection to a |
64 | /// new location. |
65 | abstract class DirectionalCaretMovementIntent extends DirectionalTextEditingIntent { |
66 | /// Creates a [DirectionalCaretMovementIntent]. |
67 | const DirectionalCaretMovementIntent( |
68 | super.forward, |
69 | this.collapseSelection, |
70 | [ |
71 | this.collapseAtReversal = false, |
72 | this.continuesAtWrap = false, |
73 | ] |
74 | ) : assert(!collapseSelection || !collapseAtReversal); |
75 | |
76 | /// Whether this [Intent] should make the selection collapsed (so it becomes a |
77 | /// caret), after the movement. |
78 | /// |
79 | /// When [collapseSelection] is false, the input field typically only moves |
80 | /// the current [TextSelection.extent] to the new location, while maintains |
81 | /// the current [TextSelection.base] location. |
82 | /// |
83 | /// When [collapseSelection] is true, the input field typically should move |
84 | /// both the [TextSelection.base] and the [TextSelection.extent] to the new |
85 | /// location. |
86 | final bool collapseSelection; |
87 | |
88 | /// Whether to collapse the selection when it would otherwise reverse order. |
89 | /// |
90 | /// For example, consider when forward is true and the extent is before the |
91 | /// base. If collapseAtReversal is true, then this will cause the selection to |
92 | /// collapse at the base. If it's false, then the extent will be placed at the |
93 | /// linebreak, reversing the order of base and offset. |
94 | /// |
95 | /// Cannot be true when collapseSelection is true. |
96 | final bool collapseAtReversal; |
97 | |
98 | /// Whether or not to continue to the next line at a wordwrap. |
99 | /// |
100 | /// If true, when an [Intent] to go to the beginning/end of a wordwrapped line |
101 | /// is received and the selection is already at the beginning/end of the line, |
102 | /// then the selection will be moved to the next/previous line. If false, the |
103 | /// selection will remain at the wordwrap. |
104 | final bool continuesAtWrap; |
105 | } |
106 | |
107 | /// Extends, or moves the current selection from the current |
108 | /// [TextSelection.extent] position to the previous or the next character |
109 | /// boundary. |
110 | class ExtendSelectionByCharacterIntent extends DirectionalCaretMovementIntent { |
111 | /// Creates an [ExtendSelectionByCharacterIntent]. |
112 | const ExtendSelectionByCharacterIntent({ |
113 | required bool forward, |
114 | required bool collapseSelection, |
115 | }) : super(forward, collapseSelection); |
116 | } |
117 | |
118 | /// Extends, or moves the current selection from the current |
119 | /// [TextSelection.extent] position to the previous or the next word |
120 | /// boundary. |
121 | class ExtendSelectionToNextWordBoundaryIntent extends DirectionalCaretMovementIntent { |
122 | /// Creates an [ExtendSelectionToNextWordBoundaryIntent]. |
123 | const ExtendSelectionToNextWordBoundaryIntent({ |
124 | required bool forward, |
125 | required bool collapseSelection, |
126 | }) : super(forward, collapseSelection); |
127 | } |
128 | |
129 | /// Extends, or moves the current selection from the current |
130 | /// [TextSelection.extent] position to the previous or the next word |
131 | /// boundary, or the [TextSelection.base] position if it's closer in the move |
132 | /// direction. |
133 | /// |
134 | /// This [Intent] typically has the same effect as an |
135 | /// [ExtendSelectionToNextWordBoundaryIntent], except it collapses the selection |
136 | /// when the order of [TextSelection.base] and [TextSelection.extent] would |
137 | /// reverse. |
138 | /// |
139 | /// This is typically only used on MacOS. |
140 | class ExtendSelectionToNextWordBoundaryOrCaretLocationIntent extends DirectionalCaretMovementIntent { |
141 | /// Creates an [ExtendSelectionToNextWordBoundaryOrCaretLocationIntent]. |
142 | const ExtendSelectionToNextWordBoundaryOrCaretLocationIntent({ |
143 | required bool forward, |
144 | }) : super(forward, false, true); |
145 | } |
146 | |
147 | /// Expands the current selection to the document boundary in the direction |
148 | /// given by [forward]. |
149 | /// |
150 | /// Unlike [ExpandSelectionToLineBreakIntent], the extent will be moved, which |
151 | /// matches the behavior on MacOS. |
152 | /// |
153 | /// See also: |
154 | /// |
155 | /// [ExtendSelectionToDocumentBoundaryIntent], which is similar but always |
156 | /// moves the extent. |
157 | class ExpandSelectionToDocumentBoundaryIntent extends DirectionalCaretMovementIntent { |
158 | /// Creates an [ExpandSelectionToDocumentBoundaryIntent]. |
159 | const ExpandSelectionToDocumentBoundaryIntent({ |
160 | required bool forward, |
161 | }) : super(forward, false); |
162 | } |
163 | |
164 | /// Expands the current selection to the closest line break in the direction |
165 | /// given by [forward]. |
166 | /// |
167 | /// Either the base or extent can move, whichever is closer to the line break. |
168 | /// The selection will never shrink. |
169 | /// |
170 | /// This behavior is common on MacOS. |
171 | /// |
172 | /// See also: |
173 | /// |
174 | /// [ExtendSelectionToLineBreakIntent], which is similar but always moves the |
175 | /// extent. |
176 | class ExpandSelectionToLineBreakIntent extends DirectionalCaretMovementIntent { |
177 | /// Creates an [ExpandSelectionToLineBreakIntent]. |
178 | const ExpandSelectionToLineBreakIntent({ |
179 | required bool forward, |
180 | }) : super(forward, false); |
181 | } |
182 | |
183 | /// Extends, or moves the current selection from the current |
184 | /// [TextSelection.extent] position to the closest line break in the direction |
185 | /// given by [forward]. |
186 | /// |
187 | /// See also: |
188 | /// |
189 | /// [ExpandSelectionToLineBreakIntent], which is similar but always increases |
190 | /// the size of the selection. |
191 | class ExtendSelectionToLineBreakIntent extends DirectionalCaretMovementIntent { |
192 | /// Creates an [ExtendSelectionToLineBreakIntent]. |
193 | const ExtendSelectionToLineBreakIntent({ |
194 | required bool forward, |
195 | required bool collapseSelection, |
196 | bool collapseAtReversal = false, |
197 | bool continuesAtWrap = false, |
198 | }) : assert(!collapseSelection || !collapseAtReversal), |
199 | super(forward, collapseSelection, collapseAtReversal, continuesAtWrap); |
200 | } |
201 | |
202 | /// Extends, or moves the current selection from the current |
203 | /// [TextSelection.extent] position to the closest position on the adjacent |
204 | /// line. |
205 | class ExtendSelectionVerticallyToAdjacentLineIntent extends DirectionalCaretMovementIntent { |
206 | /// Creates an [ExtendSelectionVerticallyToAdjacentLineIntent]. |
207 | const ExtendSelectionVerticallyToAdjacentLineIntent({ |
208 | required bool forward, |
209 | required bool collapseSelection, |
210 | }) : super(forward, collapseSelection); |
211 | } |
212 | |
213 | /// Expands, or moves the current selection from the current |
214 | /// [TextSelection.extent] position to the closest position on the adjacent |
215 | /// page. |
216 | class ExtendSelectionVerticallyToAdjacentPageIntent extends DirectionalCaretMovementIntent { |
217 | /// Creates an [ExtendSelectionVerticallyToAdjacentPageIntent]. |
218 | const ExtendSelectionVerticallyToAdjacentPageIntent({ |
219 | required bool forward, |
220 | required bool collapseSelection, |
221 | }) : super(forward, collapseSelection); |
222 | } |
223 | |
224 | /// Extends, or moves the current selection from the current |
225 | /// [TextSelection.extent] position to the previous or the next paragraph |
226 | /// boundary. |
227 | class ExtendSelectionToNextParagraphBoundaryIntent extends DirectionalCaretMovementIntent { |
228 | /// Creates an [ExtendSelectionToNextParagraphBoundaryIntent]. |
229 | const ExtendSelectionToNextParagraphBoundaryIntent({ |
230 | required bool forward, |
231 | required bool collapseSelection, |
232 | }) : super(forward, collapseSelection); |
233 | } |
234 | |
235 | /// Extends, or moves the current selection from the current |
236 | /// [TextSelection.extent] position to the previous or the next paragraph |
237 | /// boundary depending on the [forward] parameter. |
238 | /// |
239 | /// This [Intent] typically has the same effect as an |
240 | /// [ExtendSelectionToNextParagraphBoundaryIntent], except it collapses the selection |
241 | /// when the order of [TextSelection.base] and [TextSelection.extent] would |
242 | /// reverse. |
243 | /// |
244 | /// This is typically only used on MacOS. |
245 | class ExtendSelectionToNextParagraphBoundaryOrCaretLocationIntent extends DirectionalCaretMovementIntent { |
246 | /// Creates an [ExtendSelectionToNextParagraphBoundaryOrCaretLocationIntent]. |
247 | const ExtendSelectionToNextParagraphBoundaryOrCaretLocationIntent({ |
248 | required bool forward, |
249 | }) : super(forward, false, true); |
250 | } |
251 | |
252 | /// Extends, or moves the current selection from the current |
253 | /// [TextSelection.extent] position to the start or the end of the document. |
254 | /// |
255 | /// See also: |
256 | /// |
257 | /// [ExtendSelectionToDocumentBoundaryIntent], which is similar but always |
258 | /// increases the size of the selection. |
259 | class ExtendSelectionToDocumentBoundaryIntent extends DirectionalCaretMovementIntent { |
260 | /// Creates an [ExtendSelectionToDocumentBoundaryIntent]. |
261 | const ExtendSelectionToDocumentBoundaryIntent({ |
262 | required bool forward, |
263 | required bool collapseSelection, |
264 | }) : super(forward, collapseSelection); |
265 | } |
266 | |
267 | /// Scrolls to the beginning or end of the document depending on the [forward] |
268 | /// parameter. |
269 | class ScrollToDocumentBoundaryIntent extends DirectionalTextEditingIntent { |
270 | /// Creates a [ScrollToDocumentBoundaryIntent]. |
271 | const ScrollToDocumentBoundaryIntent({ |
272 | required bool forward, |
273 | }) : super(forward); |
274 | } |
275 | |
276 | /// Scrolls up or down by page depending on the [forward] parameter. |
277 | /// Extends the selection up or down by page based on the [forward] parameter. |
278 | class ExtendSelectionByPageIntent extends DirectionalTextEditingIntent { |
279 | /// Creates a [ExtendSelectionByPageIntent]. |
280 | const ExtendSelectionByPageIntent({ |
281 | required bool forward, |
282 | }) : super(forward); |
283 | } |
284 | |
285 | /// An [Intent] to select everything in the field. |
286 | class SelectAllTextIntent extends Intent { |
287 | /// Creates an instance of [SelectAllTextIntent]. |
288 | const SelectAllTextIntent(this.cause); |
289 | |
290 | /// {@template flutter.widgets.TextEditingIntents.cause} |
291 | /// The [SelectionChangedCause] that triggered the intent. |
292 | /// {@endtemplate} |
293 | final SelectionChangedCause cause; |
294 | } |
295 | |
296 | /// An [Intent] that represents a user interaction that attempts to copy or cut |
297 | /// the current selection in the field. |
298 | class CopySelectionTextIntent extends Intent { |
299 | const CopySelectionTextIntent._(this.cause, this.collapseSelection); |
300 | |
301 | /// Creates an [Intent] that represents a user interaction that attempts to |
302 | /// cut the current selection in the field. |
303 | const CopySelectionTextIntent.cut(SelectionChangedCause cause) : this._(cause, true); |
304 | |
305 | /// An [Intent] that represents a user interaction that attempts to copy the |
306 | /// current selection in the field. |
307 | static const CopySelectionTextIntent copy = CopySelectionTextIntent._(SelectionChangedCause.keyboard, false); |
308 | |
309 | /// {@macro flutter.widgets.TextEditingIntents.cause} |
310 | final SelectionChangedCause cause; |
311 | |
312 | /// Whether the original text needs to be removed from the input field if the |
313 | /// copy action was successful. |
314 | final bool collapseSelection; |
315 | } |
316 | |
317 | /// An [Intent] to paste text from [Clipboard] to the field. |
318 | class PasteTextIntent extends Intent { |
319 | /// Creates an instance of [PasteTextIntent]. |
320 | const PasteTextIntent(this.cause); |
321 | |
322 | /// {@macro flutter.widgets.TextEditingIntents.cause} |
323 | final SelectionChangedCause cause; |
324 | } |
325 | |
326 | /// An [Intent] that represents a user interaction that attempts to go back to |
327 | /// the previous editing state. |
328 | class RedoTextIntent extends Intent { |
329 | /// Creates a [RedoTextIntent]. |
330 | const RedoTextIntent(this.cause); |
331 | |
332 | /// {@macro flutter.widgets.TextEditingIntents.cause} |
333 | final SelectionChangedCause cause; |
334 | } |
335 | |
336 | /// An [Intent] that represents a user interaction that attempts to modify the |
337 | /// current [TextEditingValue] in an input field. |
338 | class ReplaceTextIntent extends Intent { |
339 | /// Creates a [ReplaceTextIntent]. |
340 | const ReplaceTextIntent(this.currentTextEditingValue, this.replacementText, this.replacementRange, this.cause); |
341 | |
342 | /// The [TextEditingValue] that this [Intent]'s action should perform on. |
343 | final TextEditingValue currentTextEditingValue; |
344 | |
345 | /// The text to replace the original text within the [replacementRange] with. |
346 | final String replacementText; |
347 | |
348 | /// The range of text in [currentTextEditingValue] that needs to be replaced. |
349 | final TextRange replacementRange; |
350 | |
351 | /// {@macro flutter.widgets.TextEditingIntents.cause} |
352 | final SelectionChangedCause cause; |
353 | } |
354 | |
355 | /// An [Intent] that represents a user interaction that attempts to go back to |
356 | /// the previous editing state. |
357 | class UndoTextIntent extends Intent { |
358 | /// Creates an [UndoTextIntent]. |
359 | const UndoTextIntent(this.cause); |
360 | |
361 | /// {@macro flutter.widgets.TextEditingIntents.cause} |
362 | final SelectionChangedCause cause; |
363 | } |
364 | |
365 | /// An [Intent] that represents a user interaction that attempts to change the |
366 | /// selection in an input field. |
367 | class UpdateSelectionIntent extends Intent { |
368 | /// Creates an [UpdateSelectionIntent]. |
369 | const UpdateSelectionIntent(this.currentTextEditingValue, this.newSelection, this.cause); |
370 | |
371 | /// The [TextEditingValue] that this [Intent]'s action should perform on. |
372 | final TextEditingValue currentTextEditingValue; |
373 | |
374 | /// The new [TextSelection] the input field should adopt. |
375 | final TextSelection newSelection; |
376 | |
377 | /// {@macro flutter.widgets.TextEditingIntents.cause} |
378 | final SelectionChangedCause cause; |
379 | } |
380 | |
381 | /// An [Intent] that represents a user interaction that attempts to swap the |
382 | /// characters immediately around the cursor. |
383 | class TransposeCharactersIntent extends Intent { |
384 | /// Creates a [TransposeCharactersIntent]. |
385 | const TransposeCharactersIntent(); |
386 | } |
387 | |