@@ -226,6 +226,8 @@ class TextEditingElement {
226
226
EditingState _lastEditingState;
227
227
_OnChangeCallback _onChange;
228
228
229
+ SelectionChangeDetection _selectionDetection;
230
+
229
231
final List <StreamSubscription <html.Event >> _subscriptions =
230
232
< StreamSubscription <html.Event >> [];
231
233
@@ -273,6 +275,7 @@ class TextEditingElement {
273
275
274
276
_initDomElement (inputConfig);
275
277
_enabled = true ;
278
+ _selectionDetection = SelectionChangeDetection (domElement);
276
279
_onChange = onChange;
277
280
278
281
// Chrome on Android will hide the onscreen keyboard when you tap outside
@@ -305,6 +308,18 @@ class TextEditingElement {
305
308
_subscriptions
306
309
..add (html.document.onSelectionChange.listen (_handleChange))
307
310
..add (domElement.onInput.listen (_handleChange));
311
+
312
+ // In Firefox, when cursor moves, nor selectionChange neither onInput
313
+ // events are triggered. We are listening to keyup event to decide
314
+ // if the user shifted the cursor.
315
+ // See [SelectionChangeDetection].
316
+ if (browserEngine == BrowserEngine .firefox) {
317
+ _subscriptions.add (domElement.onKeyUp.listen ((event) {
318
+ if (_selectionDetection.detectChange ()) {
319
+ _handleChange (event);
320
+ }
321
+ }));
322
+ }
308
323
}
309
324
310
325
/// Disables the element so it's no longer used for text editing.
@@ -324,6 +339,7 @@ class TextEditingElement {
324
339
_positionInputElementTimer = null ;
325
340
owner.inputPositioned = false ;
326
341
_removeDomElement ();
342
+ _selectionDetection = null ;
327
343
}
328
344
329
345
void _initDomElement (InputConfiguration inputConfig) {
@@ -412,8 +428,7 @@ class TextEditingElement {
412
428
break ;
413
429
}
414
430
415
-
416
- if (owner.inputElementNeedsToBePositioned) {
431
+ if (owner.inputElementNeedsToBePositioned) {
417
432
_preventShiftDuringFocus ();
418
433
}
419
434
@@ -641,9 +656,7 @@ class HybridTextEditing {
641
656
///
642
657
/// See [TextEditingElement._delayBeforePositioning] .
643
658
bool get inputElementNeedsToBePositioned =>
644
- ! inputPositioned &&
645
- _isEditing &&
646
- doesKeyboardShiftInput;
659
+ ! inputPositioned && _isEditing && doesKeyboardShiftInput;
647
660
648
661
/// Flag indicating whether the input element's position is set.
649
662
///
@@ -788,8 +801,8 @@ class HybridTextEditing {
788
801
/// In iOS, the virtual keyboard might shifts the screen up to make input
789
802
/// visible depending on the location of the focused input element.
790
803
bool get doesKeyboardShiftInput =>
791
- browserEngine == BrowserEngine .webkit &&
792
- operatingSystem == OperatingSystem .iOs;
804
+ browserEngine == BrowserEngine .webkit &&
805
+ operatingSystem == OperatingSystem .iOs;
793
806
794
807
/// These style attributes are dynamic throughout the life time of an input
795
808
/// element.
@@ -888,3 +901,62 @@ class _EditableSizeAndTransform {
888
901
final double height;
889
902
final Float64List transform;
890
903
}
904
+
905
+ /// Detects changes in text selection.
906
+ ///
907
+ /// Currently only used in Firefox.
908
+ ///
909
+ /// In Firefox, when cursor moves, neither selectionChange nor onInput
910
+ /// events are triggered. We are listening to keyup event. Selection start,
911
+ /// end values are used to decide if the text cursor moved.
912
+ ///
913
+ /// Specific keycodes are not checked since users/applicatins can bind their own
914
+ /// keys to move the text cursor.
915
+ class SelectionChangeDetection {
916
+ final html.HtmlElement _domElement;
917
+ int _start = - 1 ;
918
+ int _end = - 1 ;
919
+
920
+ SelectionChangeDetection (this ._domElement) {
921
+ if (_domElement is html.InputElement ) {
922
+ html.InputElement element = _domElement;
923
+ _saveSelection (element.selectionStart, element.selectionEnd);
924
+ } else if (_domElement is html.TextAreaElement ) {
925
+ html.TextAreaElement element = _domElement;
926
+ _saveSelection (element.selectionStart, element.selectionEnd);
927
+ } else {
928
+ throw UnsupportedError ('Initialized with unsupported input type' );
929
+ }
930
+ }
931
+
932
+ /// Decides if the selection has changed (cursor moved) compared to the
933
+ /// previous values.
934
+ ///
935
+ /// After each keyup, the start/end values of the selection is compared to the
936
+ /// previously saved start/end values.
937
+ bool detectChange () {
938
+ if (_domElement is html.InputElement ) {
939
+ html.InputElement element = _domElement;
940
+ return _compareSelection (element.selectionStart, element.selectionEnd);
941
+ }
942
+ if (_domElement is html.TextAreaElement ) {
943
+ html.TextAreaElement element = _domElement;
944
+ return _compareSelection (element.selectionStart, element.selectionEnd);
945
+ }
946
+ throw UnsupportedError ('Unsupported input type' );
947
+ }
948
+
949
+ void _saveSelection (int selectionStart, int selectionEnd) {
950
+ _start = selectionStart;
951
+ _end = selectionEnd;
952
+ }
953
+
954
+ bool _compareSelection (int selectionStart, int selectionEnd) {
955
+ if (selectionStart != _start || selectionEnd != _end) {
956
+ _saveSelection (selectionStart, selectionEnd);
957
+ return true ;
958
+ } else {
959
+ return false ;
960
+ }
961
+ }
962
+ }
0 commit comments