[web] Changes to EditableState to be able to handle Framework text selection shortcuts (flutter/engine#37097)
This commit is contained in:
@@ -666,13 +666,13 @@ class EditingState {
|
||||
this.text,
|
||||
int? baseOffset,
|
||||
int? extentOffset,
|
||||
this.composingBaseOffset,
|
||||
this.composingExtentOffset
|
||||
this.composingBaseOffset = -1,
|
||||
this.composingExtentOffset = -1
|
||||
}) :
|
||||
// Don't allow negative numbers. Pick the smallest selection index for base.
|
||||
baseOffset = math.max(0, math.min(baseOffset ?? 0, extentOffset ?? 0)),
|
||||
// Don't allow negative numbers. Pick the greatest selection index for extent.
|
||||
extentOffset = math.max(0, math.max(baseOffset ?? 0, extentOffset ?? 0));
|
||||
// Don't allow negative numbers.
|
||||
baseOffset = math.max(0, baseOffset ?? 0),
|
||||
// Don't allow negative numbers.
|
||||
extentOffset = math.max(0, extentOffset ?? 0);
|
||||
|
||||
/// Creates an [EditingState] instance using values from an editing state Map
|
||||
/// coming from Flutter.
|
||||
@@ -707,8 +707,8 @@ class EditingState {
|
||||
text: text,
|
||||
baseOffset: selectionBase,
|
||||
extentOffset: selectionExtent,
|
||||
composingBaseOffset: composingBase,
|
||||
composingExtentOffset: composingExtent
|
||||
composingBaseOffset: composingBase ?? -1,
|
||||
composingExtentOffset: composingExtent ?? -1
|
||||
);
|
||||
}
|
||||
|
||||
@@ -736,6 +736,11 @@ class EditingState {
|
||||
}
|
||||
}
|
||||
|
||||
// Pick the smallest selection index for base.
|
||||
int get minOffset => math.min(baseOffset ?? 0, extentOffset ?? 0);
|
||||
// Pick the greatest selection index for extent.
|
||||
int get maxOffset => math.max(baseOffset ?? 0, extentOffset ?? 0);
|
||||
|
||||
EditingState copyWith({
|
||||
String? text,
|
||||
int? baseOffset,
|
||||
@@ -773,10 +778,10 @@ class EditingState {
|
||||
final int? extentOffset;
|
||||
|
||||
/// The offset at which [CompositionAwareMixin.composingText] begins, if any.
|
||||
final int? composingBaseOffset;
|
||||
final int composingBaseOffset;
|
||||
|
||||
/// The offset at which [CompositionAwareMixin.composingText] terminates, if any.
|
||||
final int? composingExtentOffset;
|
||||
final int composingExtentOffset;
|
||||
|
||||
/// Whether the current editing state is valid or not.
|
||||
bool get isValid => baseOffset! >= 0 && extentOffset! >= 0;
|
||||
@@ -796,8 +801,8 @@ class EditingState {
|
||||
}
|
||||
return other is EditingState &&
|
||||
other.text == text &&
|
||||
other.baseOffset == baseOffset &&
|
||||
other.extentOffset == extentOffset &&
|
||||
other.minOffset == minOffset &&
|
||||
other.maxOffset == maxOffset &&
|
||||
other.composingBaseOffset == composingBaseOffset &&
|
||||
other.composingExtentOffset == composingExtentOffset;
|
||||
}
|
||||
@@ -825,12 +830,12 @@ class EditingState {
|
||||
if (domInstanceOfString(domElement, 'HTMLInputElement')) {
|
||||
final DomHTMLInputElement element = domElement! as DomHTMLInputElement;
|
||||
element.value = text;
|
||||
element.setSelectionRange(baseOffset!, extentOffset!);
|
||||
element.setSelectionRange(minOffset, maxOffset);
|
||||
} else if (domInstanceOfString(domElement, 'HTMLTextAreaElement')) {
|
||||
final DomHTMLTextAreaElement element = domElement! as
|
||||
DomHTMLTextAreaElement;
|
||||
element.value = text;
|
||||
element.setSelectionRange(baseOffset!, extentOffset!);
|
||||
element.setSelectionRange(minOffset, maxOffset);
|
||||
} else {
|
||||
throw UnsupportedError('Unsupported DOM element type: <${domElement?.tagName}> (${domElement.runtimeType})');
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ Future<void> testMain() async {
|
||||
expect(
|
||||
editingStrategy.lastEditingState,
|
||||
isA<EditingState>()
|
||||
.having((EditingState editingState) => editingState.composingBaseOffset!,
|
||||
.having((EditingState editingState) => editingState.composingBaseOffset,
|
||||
'composingBaseOffset', beforeComposingText.length - composingText.length)
|
||||
.having((EditingState editingState) => editingState.composingExtentOffset,
|
||||
'composingExtentOffset', beforeComposingText.length));
|
||||
|
||||
@@ -1557,8 +1557,8 @@ Future<void> testMain() async {
|
||||
'text': 'something',
|
||||
'selectionBase': 9,
|
||||
'selectionExtent': 9,
|
||||
'composingBase': null,
|
||||
'composingExtent': null
|
||||
'composingBase': -1,
|
||||
'composingExtent': -1
|
||||
}
|
||||
],
|
||||
);
|
||||
@@ -1583,8 +1583,8 @@ Future<void> testMain() async {
|
||||
'text': 'something',
|
||||
'selectionBase': 2,
|
||||
'selectionExtent': 5,
|
||||
'composingBase': null,
|
||||
'composingExtent': null
|
||||
'composingBase': -1,
|
||||
'composingExtent': -1
|
||||
}
|
||||
],
|
||||
);
|
||||
@@ -1641,8 +1641,8 @@ Future<void> testMain() async {
|
||||
'deltaEnd': -1,
|
||||
'selectionBase': 2,
|
||||
'selectionExtent': 5,
|
||||
'composingBase': null,
|
||||
'composingExtent': null
|
||||
'composingBase': -1,
|
||||
'composingExtent': -1
|
||||
}
|
||||
],
|
||||
}
|
||||
@@ -1724,8 +1724,8 @@ Future<void> testMain() async {
|
||||
'text': 'something',
|
||||
'selectionBase': 9,
|
||||
'selectionExtent': 9,
|
||||
'composingBase': null,
|
||||
'composingExtent': null
|
||||
'composingBase': -1,
|
||||
'composingExtent': -1
|
||||
}
|
||||
},
|
||||
],
|
||||
@@ -1796,8 +1796,8 @@ Future<void> testMain() async {
|
||||
'text': 'something\nelse',
|
||||
'selectionBase': 14,
|
||||
'selectionExtent': 14,
|
||||
'composingBase': null,
|
||||
'composingExtent': null
|
||||
'composingBase': -1,
|
||||
'composingExtent': -1
|
||||
}
|
||||
],
|
||||
);
|
||||
@@ -1812,8 +1812,8 @@ Future<void> testMain() async {
|
||||
'text': 'something\nelse',
|
||||
'selectionBase': 2,
|
||||
'selectionExtent': 5,
|
||||
'composingBase': null,
|
||||
'composingExtent': null
|
||||
'composingBase': -1,
|
||||
'composingExtent': -1
|
||||
}
|
||||
],
|
||||
);
|
||||
@@ -2231,6 +2231,34 @@ Future<void> testMain() async {
|
||||
);
|
||||
});
|
||||
|
||||
test('Sets default composing offsets if none given', () {
|
||||
final EditingState editingState =
|
||||
EditingState(text: 'Test', baseOffset: 2, extentOffset: 4);
|
||||
final EditingState editingStateFromFrameworkMsg =
|
||||
EditingState.fromFrameworkMessage(<String, dynamic>{
|
||||
'selectionBase': 10,
|
||||
'selectionExtent': 4,
|
||||
});
|
||||
|
||||
expect(editingState.composingBaseOffset, -1);
|
||||
expect(editingState.composingExtentOffset, -1);
|
||||
|
||||
expect(editingStateFromFrameworkMsg.composingBaseOffset, -1);
|
||||
expect(editingStateFromFrameworkMsg.composingExtentOffset, -1);
|
||||
});
|
||||
|
||||
test('Correctly identifies min and max offsets', () {
|
||||
final EditingState flippedEditingState =
|
||||
EditingState(baseOffset: 10, extentOffset: 4);
|
||||
final EditingState normalEditingState =
|
||||
EditingState(baseOffset: 2, extentOffset: 6);
|
||||
|
||||
expect(flippedEditingState.minOffset, 4);
|
||||
expect(flippedEditingState.maxOffset, 10);
|
||||
expect(normalEditingState.minOffset, 2);
|
||||
expect(normalEditingState.maxOffset, 6);
|
||||
});
|
||||
|
||||
test('Configure input element from the editing state', () {
|
||||
final DomHTMLInputElement input =
|
||||
defaultTextEditingRoot.querySelector('input')! as DomHTMLInputElement;
|
||||
@@ -2264,6 +2292,20 @@ Future<void> testMain() async {
|
||||
expect(textArea.selectionEnd, 2);
|
||||
});
|
||||
|
||||
test('Configure input element editing state for a flipped base and extent',
|
||||
() {
|
||||
final DomHTMLInputElement input =
|
||||
defaultTextEditingRoot.querySelector('input')! as DomHTMLInputElement;
|
||||
editingState =
|
||||
EditingState(text: 'Hello World', baseOffset: 10, extentOffset: 2);
|
||||
|
||||
editingState.applyToDomElement(input);
|
||||
|
||||
expect(input.value, 'Hello World');
|
||||
expect(input.selectionStart, 2);
|
||||
expect(input.selectionEnd, 10);
|
||||
});
|
||||
|
||||
test('Get Editing State from input element', () {
|
||||
final DomHTMLInputElement input =
|
||||
defaultTextEditingRoot.querySelector('input')! as DomHTMLInputElement;
|
||||
@@ -2318,6 +2360,17 @@ Future<void> testMain() async {
|
||||
expect(editingState1 != editingState3, isTrue);
|
||||
});
|
||||
|
||||
test('Takes flipped base and extent offsets into account', () {
|
||||
final EditingState flippedEditingState =
|
||||
EditingState(baseOffset: 10, extentOffset: 4);
|
||||
final EditingState normalEditingState =
|
||||
EditingState(baseOffset: 4, extentOffset: 10);
|
||||
|
||||
expect(normalEditingState, flippedEditingState);
|
||||
|
||||
expect(normalEditingState == flippedEditingState, isTrue);
|
||||
});
|
||||
|
||||
test('takes composition range into account', () {
|
||||
final EditingState editingState1 = EditingState(composingBaseOffset: 1, composingExtentOffset: 2);
|
||||
final EditingState editingState2 = EditingState(composingBaseOffset: 1, composingExtentOffset: 2);
|
||||
|
||||
Reference in New Issue
Block a user