text_editing_intents.dart 14.9 KB
Newer Older
1 2 3 4
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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 12 13
///
/// See also:
///
///   * [DefaultTextEditingShortcuts], which triggers this [Intent].
14
class DoNothingAndStopPropagationTextIntent extends Intent {
15
  /// Creates an instance of [DoNothingAndStopPropagationTextIntent].
16 17 18
  const DoNothingAndStopPropagationTextIntent();
}

19 20 21 22
/// A text editing related [Intent] that performs an operation towards a given
/// direction of the current caret location.
abstract class DirectionalTextEditingIntent extends Intent {
  /// Creates a [DirectionalTextEditingIntent].
23 24 25
  const DirectionalTextEditingIntent(
    this.forward,
  );
26

27 28 29 30
  /// Whether the input field, if applicable, should perform the text editing
  /// operation from the current caret location towards the end of the document.
  ///
  /// 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 33
  /// direction, and is not affected by the writing direction of the text.
  final bool forward;
34 35
}

36 37
/// Deletes the character before or after the caret location, based on whether
/// `forward` is true.
38
///
39 40
/// {@template flutter.widgets.TextEditingIntents.logicalOrder}
/// {@endtemplate}
41
///
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/// Typically a text field will not respond to this intent if it has no active
/// caret ([TextSelection.isValid] is false for the current selection).
class DeleteCharacterIntent extends DirectionalTextEditingIntent {
  /// Creates a [DeleteCharacterIntent].
  const DeleteCharacterIntent({ required bool forward }) : super(forward);
}

/// Deletes from the current caret location to the previous or next word
/// boundary, based on whether `forward` is true.
class DeleteToNextWordBoundaryIntent extends DirectionalTextEditingIntent {
  /// Creates a [DeleteToNextWordBoundaryIntent].
  const DeleteToNextWordBoundaryIntent({ required bool forward }) : super(forward);
}

/// Deletes from the current caret location to the previous or next soft or hard
/// line break, based on whether `forward` is true.
class DeleteToLineBreakIntent extends DirectionalTextEditingIntent {
  /// Creates a [DeleteToLineBreakIntent].
  const DeleteToLineBreakIntent({ required bool forward }) : super(forward);
}

/// A [DirectionalTextEditingIntent] that moves the caret or the selection to a
/// new location.
abstract class DirectionalCaretMovementIntent extends DirectionalTextEditingIntent {
  /// Creates a [DirectionalCaretMovementIntent].
67
  const DirectionalCaretMovementIntent(
68
    super.forward,
69
    this.collapseSelection,
70 71 72 73
    [
      this.collapseAtReversal = false,
      this.continuesAtWrap = false,
    ]
74
  ) : assert(!collapseSelection || !collapseAtReversal);
75 76 77 78 79 80 81 82 83 84 85 86

  /// Whether this [Intent] should make the selection collapsed (so it becomes a
  /// caret), after the movement.
  ///
  /// When [collapseSelection] is false, the input field typically only moves
  /// the current [TextSelection.extent] to the new location, while maintains
  /// the current [TextSelection.base] location.
  ///
  /// When [collapseSelection] is true, the input field typically should move
  /// both the [TextSelection.base] and the [TextSelection.extent] to the new
  /// location.
  final bool collapseSelection;
87 88 89 90 91 92 93 94 95 96

  /// Whether to collapse the selection when it would otherwise reverse order.
  ///
  /// For example, consider when forward is true and the extent is before the
  /// base. If collapseAtReversal is true, then this will cause the selection to
  /// collapse at the base. If it's false, then the extent will be placed at the
  /// linebreak, reversing the order of base and offset.
  ///
  /// Cannot be true when collapseSelection is true.
  final bool collapseAtReversal;
97 98 99 100 101

  /// Whether or not to continue to the next line at a wordwrap.
  ///
  /// If true, when an [Intent] to go to the beginning/end of a wordwrapped line
  /// 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 104
  /// selection will remain at the wordwrap.
  final bool continuesAtWrap;
105 106
}

107
/// Extends, or moves the current selection from the current
108 109 110 111 112 113 114 115 116 117
/// [TextSelection.extent] position to the previous or the next character
/// boundary.
class ExtendSelectionByCharacterIntent extends DirectionalCaretMovementIntent {
  /// Creates an [ExtendSelectionByCharacterIntent].
  const ExtendSelectionByCharacterIntent({
    required bool forward,
    required bool collapseSelection,
  }) : super(forward, collapseSelection);
}

118
/// Extends, or moves the current selection from the current
119 120 121 122 123 124 125 126 127 128
/// [TextSelection.extent] position to the previous or the next word
/// boundary.
class ExtendSelectionToNextWordBoundaryIntent extends DirectionalCaretMovementIntent {
  /// Creates an [ExtendSelectionToNextWordBoundaryIntent].
  const ExtendSelectionToNextWordBoundaryIntent({
    required bool forward,
    required bool collapseSelection,
  }) : super(forward, collapseSelection);
}

129
/// Extends, or moves the current selection from the current
130 131 132 133 134 135 136 137 138
/// [TextSelection.extent] position to the previous or the next word
/// boundary, or the [TextSelection.base] position if it's closer in the move
/// direction.
///
/// This [Intent] typically has the same effect as an
/// [ExtendSelectionToNextWordBoundaryIntent], except it collapses the selection
/// when the order of [TextSelection.base] and [TextSelection.extent] would
/// reverse.
///
139
/// This is typically only used on MacOS.
140
class ExtendSelectionToNextWordBoundaryOrCaretLocationIntent extends DirectionalCaretMovementIntent {
141 142 143
  /// Creates an [ExtendSelectionToNextWordBoundaryOrCaretLocationIntent].
  const ExtendSelectionToNextWordBoundaryOrCaretLocationIntent({
    required bool forward,
144
  }) : super(forward, false, true);
145 146
}

147 148 149 150 151 152 153 154 155 156
/// Expands the current selection to the document boundary in the direction
/// given by [forward].
///
/// Unlike [ExpandSelectionToLineBreakIntent], the extent will be moved, which
/// matches the behavior on MacOS.
///
/// See also:
///
///   [ExtendSelectionToDocumentBoundaryIntent], which is similar but always
///   moves the extent.
157
class ExpandSelectionToDocumentBoundaryIntent extends DirectionalCaretMovementIntent {
158 159 160
  /// Creates an [ExpandSelectionToDocumentBoundaryIntent].
  const ExpandSelectionToDocumentBoundaryIntent({
    required bool forward,
161
  }) : super(forward, false);
162 163
}

164 165 166 167 168 169 170 171 172 173 174 175
/// Expands the current selection to the closest line break in the direction
/// given by [forward].
///
/// Either the base or extent can move, whichever is closer to the line break.
/// The selection will never shrink.
///
/// This behavior is common on MacOS.
///
/// See also:
///
///   [ExtendSelectionToLineBreakIntent], which is similar but always moves the
///   extent.
176
class ExpandSelectionToLineBreakIntent extends DirectionalCaretMovementIntent {
177 178 179
  /// Creates an [ExpandSelectionToLineBreakIntent].
  const ExpandSelectionToLineBreakIntent({
    required bool forward,
180
  }) : super(forward, false);
181 182 183
}

/// Extends, or moves the current selection from the current
184 185
/// [TextSelection.extent] position to the closest line break in the direction
/// given by [forward].
186 187 188 189 190
///
/// See also:
///
///   [ExpandSelectionToLineBreakIntent], which is similar but always increases
///   the size of the selection.
191 192 193 194 195
class ExtendSelectionToLineBreakIntent extends DirectionalCaretMovementIntent {
  /// Creates an [ExtendSelectionToLineBreakIntent].
  const ExtendSelectionToLineBreakIntent({
    required bool forward,
    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 206 207 208 209 210
class ExtendSelectionVerticallyToAdjacentLineIntent extends DirectionalCaretMovementIntent {
  /// Creates an [ExtendSelectionVerticallyToAdjacentLineIntent].
  const ExtendSelectionVerticallyToAdjacentLineIntent({
    required bool forward,
    required bool collapseSelection,
  }) : super(forward, collapseSelection);
211 212
}

213 214 215 216 217 218 219 220 221 222 223
/// Expands, or moves the current selection from the current
/// [TextSelection.extent] position to the closest position on the adjacent
/// page.
class ExtendSelectionVerticallyToAdjacentPageIntent extends DirectionalCaretMovementIntent {
  /// Creates an [ExtendSelectionVerticallyToAdjacentPageIntent].
  const ExtendSelectionVerticallyToAdjacentPageIntent({
    required bool forward,
    required bool collapseSelection,
  }) : super(forward, collapseSelection);
}

224 225 226 227 228 229 230 231 232 233 234
/// Extends, or moves the current selection from the current
/// [TextSelection.extent] position to the previous or the next paragraph
/// boundary.
class ExtendSelectionToNextParagraphBoundaryIntent extends DirectionalCaretMovementIntent {
  /// Creates an [ExtendSelectionToNextParagraphBoundaryIntent].
  const ExtendSelectionToNextParagraphBoundaryIntent({
    required bool forward,
    required bool collapseSelection,
  }) : super(forward, collapseSelection);
}

235 236 237 238
/// Extends, or moves the current selection from the current
/// [TextSelection.extent] position to the previous or the next paragraph
/// boundary depending on the [forward] parameter.
///
239 240 241 242
/// This [Intent] typically has the same effect as an
/// [ExtendSelectionToNextParagraphBoundaryIntent], except it collapses the selection
/// when the order of [TextSelection.base] and [TextSelection.extent] would
/// reverse.
243 244 245 246 247 248 249 250 251
///
/// This is typically only used on MacOS.
class ExtendSelectionToNextParagraphBoundaryOrCaretLocationIntent extends DirectionalCaretMovementIntent {
  /// Creates an [ExtendSelectionToNextParagraphBoundaryOrCaretLocationIntent].
  const ExtendSelectionToNextParagraphBoundaryOrCaretLocationIntent({
    required bool forward,
  }) : super(forward, false, true);
}

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 256 257 258
///
/// See also:
///
///   [ExtendSelectionToDocumentBoundaryIntent], which is similar but always
///   increases the size of the selection.
259 260 261 262 263 264
class ExtendSelectionToDocumentBoundaryIntent extends DirectionalCaretMovementIntent {
  /// Creates an [ExtendSelectionToDocumentBoundaryIntent].
  const ExtendSelectionToDocumentBoundaryIntent({
    required bool forward,
    required bool collapseSelection,
  }) : super(forward, collapseSelection);
265 266
}

267 268 269 270 271 272 273 274 275
/// Scrolls to the beginning or end of the document depending on the [forward]
/// parameter.
class ScrollToDocumentBoundaryIntent extends DirectionalTextEditingIntent {
  /// Creates a [ScrollToDocumentBoundaryIntent].
  const ScrollToDocumentBoundaryIntent({
    required bool forward,
  }) : super(forward);
}

276 277 278 279 280 281 282 283 284
/// Scrolls up or down by page depending on the [forward] parameter.
/// Extends the selection up or down by page based on the [forward] parameter.
class ExtendSelectionByPageIntent extends DirectionalTextEditingIntent {
  /// Creates a [ExtendSelectionByPageIntent].
  const ExtendSelectionByPageIntent({
    required bool forward,
  }) : super(forward);
}

285 286 287 288
/// An [Intent] to select everything in the field.
class SelectAllTextIntent extends Intent {
  /// Creates an instance of [SelectAllTextIntent].
  const SelectAllTextIntent(this.cause);
289

290 291 292 293
  /// {@template flutter.widgets.TextEditingIntents.cause}
  /// The [SelectionChangedCause] that triggered the intent.
  /// {@endtemplate}
  final SelectionChangedCause cause;
294 295
}

296 297 298 299
/// An [Intent] that represents a user interaction that attempts to copy or cut
/// the current selection in the field.
class CopySelectionTextIntent extends Intent {
  const CopySelectionTextIntent._(this.cause, this.collapseSelection);
300

301 302 303
  /// Creates an [Intent] that represents a user interaction that attempts to
  /// cut the current selection in the field.
  const CopySelectionTextIntent.cut(SelectionChangedCause cause) : this._(cause, true);
304

305 306 307
  /// An [Intent] that represents a user interaction that attempts to copy the
  /// current selection in the field.
  static const CopySelectionTextIntent copy = CopySelectionTextIntent._(SelectionChangedCause.keyboard, false);
308

309 310
  /// {@macro flutter.widgets.TextEditingIntents.cause}
  final SelectionChangedCause cause;
311

312 313 314
  /// Whether the original text needs to be removed from the input field if the
  /// copy action was successful.
  final bool collapseSelection;
315 316
}

317 318 319 320
/// An [Intent] to paste text from [Clipboard] to the field.
class PasteTextIntent extends Intent {
  /// Creates an instance of [PasteTextIntent].
  const PasteTextIntent(this.cause);
321

322 323
  /// {@macro flutter.widgets.TextEditingIntents.cause}
  final SelectionChangedCause cause;
324 325
}

Justin McCandless's avatar
Justin McCandless committed
326 327 328 329 330 331 332 333 334 335
/// An [Intent] that represents a user interaction that attempts to go back to
/// the previous editing state.
class RedoTextIntent extends Intent {
  /// Creates a [RedoTextIntent].
  const RedoTextIntent(this.cause);

  /// {@macro flutter.widgets.TextEditingIntents.cause}
  final SelectionChangedCause cause;
}

336 337 338 339 340
/// An [Intent] that represents a user interaction that attempts to modify the
/// current [TextEditingValue] in an input field.
class ReplaceTextIntent extends Intent {
  /// Creates a [ReplaceTextIntent].
  const ReplaceTextIntent(this.currentTextEditingValue, this.replacementText, this.replacementRange, this.cause);
341

342 343
  /// The [TextEditingValue] that this [Intent]'s action should perform on.
  final TextEditingValue currentTextEditingValue;
344

345 346
  /// The text to replace the original text within the [replacementRange] with.
  final String replacementText;
347

348 349
  /// The range of text in [currentTextEditingValue] that needs to be replaced.
  final TextRange replacementRange;
350

351 352
  /// {@macro flutter.widgets.TextEditingIntents.cause}
  final SelectionChangedCause cause;
353
}
354

Justin McCandless's avatar
Justin McCandless committed
355 356 357 358 359 360 361 362 363 364
/// An [Intent] that represents a user interaction that attempts to go back to
/// the previous editing state.
class UndoTextIntent extends Intent {
  /// Creates an [UndoTextIntent].
  const UndoTextIntent(this.cause);

  /// {@macro flutter.widgets.TextEditingIntents.cause}
  final SelectionChangedCause cause;
}

365 366 367
/// An [Intent] that represents a user interaction that attempts to change the
/// selection in an input field.
class UpdateSelectionIntent extends Intent {
Justin McCandless's avatar
Justin McCandless committed
368
  /// Creates an [UpdateSelectionIntent].
369
  const UpdateSelectionIntent(this.currentTextEditingValue, this.newSelection, this.cause);
370

371 372
  /// The [TextEditingValue] that this [Intent]'s action should perform on.
  final TextEditingValue currentTextEditingValue;
373

374 375
  /// The new [TextSelection] the input field should adopt.
  final TextSelection newSelection;
376

377 378
  /// {@macro flutter.widgets.TextEditingIntents.cause}
  final SelectionChangedCause cause;
379
}
380 381 382 383 384 385 386

/// An [Intent] that represents a user interaction that attempts to swap the
/// characters immediately around the cursor.
class TransposeCharactersIntent extends Intent {
  /// Creates a [TransposeCharactersIntent].
  const TransposeCharactersIntent();
}