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
5import 'package:flutter/services.dart';
6
7import '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].
14class 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.
21abstract 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).
44class 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.
51class 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.
58class 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.
65abstract 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.
110class 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.
121class 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.
140class 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.
157class 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.
176class 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.
191class 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.
205class 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.
216class 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.
227class 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.
245class 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.
259class 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.
269class 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.
278class 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.
286class 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.
298class 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.
318class 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.
328class 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.
338class 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.
357class 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.
367class 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.
383class TransposeCharactersIntent extends Intent {
384 /// Creates a [TransposeCharactersIntent].
385 const TransposeCharactersIntent();
386}
387