Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
bf66cc2e
Unverified
Commit
bf66cc2e
authored
Sep 16, 2021
by
Renzo Olivares
Committed by
GitHub
Sep 16, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Framework can receive TextEditingDeltas from engine (#88477)
parent
65d8dd98
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
979 additions
and
1 deletion
+979
-1
services.dart
packages/flutter/lib/services.dart
+1
-0
text_editing_delta.dart
packages/flutter/lib/src/services/text_editing_delta.dart
+386
-0
text_input.dart
packages/flutter/lib/src/services/text_input.dart
+50
-1
editable_text.dart
packages/flutter/lib/src/widgets/editable_text.dart
+9
-0
autofill_test.dart
packages/flutter/test/services/autofill_test.dart
+13
-0
text_editing_delta_test.dart
packages/flutter/test/services/text_editing_delta_test.dart
+217
-0
text_input_test.dart
packages/flutter/test/services/text_input_test.dart
+6
-0
editable_text_test.dart
packages/flutter/test/widgets/editable_text_test.dart
+297
-0
No files found.
packages/flutter/lib/services.dart
View file @
bf66cc2e
...
...
@@ -42,6 +42,7 @@ export 'src/services/system_chrome.dart';
export
'src/services/system_navigator.dart'
;
export
'src/services/system_sound.dart'
;
export
'src/services/text_editing.dart'
;
export
'src/services/text_editing_delta.dart'
;
export
'src/services/text_formatter.dart'
;
export
'src/services/text_input.dart'
;
export
'src/services/text_layout_metrics.dart'
;
packages/flutter/lib/src/services/text_editing_delta.dart
0 → 100644
View file @
bf66cc2e
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/services/text_input.dart
View file @
bf66cc2e
...
...
@@ -23,6 +23,7 @@ import 'platform_channel.dart';
import
'system_channels.dart'
;
import
'system_chrome.dart'
;
import
'text_editing.dart'
;
import
'text_editing_delta.dart'
;
export
'dart:ui'
show
TextAffinity
;
...
...
@@ -467,6 +468,7 @@ class TextInputConfiguration {
this
.
textCapitalization
=
TextCapitalization
.
none
,
this
.
autofillConfiguration
=
AutofillConfiguration
.
disabled
,
this
.
enableIMEPersonalizedLearning
=
true
,
this
.
enableDeltaModel
=
false
,
})
:
assert
(
inputType
!=
null
),
assert
(
obscureText
!=
null
),
smartDashesType
=
smartDashesType
??
(
obscureText
?
SmartDashesType
.
disabled
:
SmartDashesType
.
enabled
),
...
...
@@ -476,7 +478,8 @@ class TextInputConfiguration {
assert
(
keyboardAppearance
!=
null
),
assert
(
inputAction
!=
null
),
assert
(
textCapitalization
!=
null
),
assert
(
enableIMEPersonalizedLearning
!=
null
);
assert
(
enableIMEPersonalizedLearning
!=
null
),
assert
(
enableDeltaModel
!=
null
);
/// The type of information for which to optimize the text input control.
final
TextInputType
inputType
;
...
...
@@ -622,6 +625,7 @@ class TextInputConfiguration {
TextCapitalization
?
textCapitalization
,
bool
?
enableIMEPersonalizedLearning
,
AutofillConfiguration
?
autofillConfiguration
,
bool
?
enableDeltaModel
,
})
{
return
TextInputConfiguration
(
inputType:
inputType
??
this
.
inputType
,
...
...
@@ -636,8 +640,30 @@ class TextInputConfiguration {
keyboardAppearance:
keyboardAppearance
??
this
.
keyboardAppearance
,
enableIMEPersonalizedLearning:
enableIMEPersonalizedLearning
??
this
.
enableIMEPersonalizedLearning
,
autofillConfiguration:
autofillConfiguration
??
this
.
autofillConfiguration
,
enableDeltaModel:
enableDeltaModel
??
this
.
enableDeltaModel
,
);
}
/// Whether to enable that the engine sends text input updates to the
/// framework as [TextEditingDelta]'s or as one [TextEditingValue].
///
/// When this is enabled platform text input updates will
/// come through [TextInputClient.updateEditingValueWithDeltas].
///
/// When this is disabled platform text input updates will come through
/// [TextInputClient.updateEditingValue].
///
/// Enabling this flag results in granular text updates being received from the
/// platforms text input control rather than a single new bulk editing state
/// given by [TextInputClient.updateEditingValue].
///
/// If the platform does not currently support the delta model then updates
/// for the editing state will continue to come through the
/// [TextInputClient.updateEditingValue] channel.
///
/// Defaults to false. Cannot be null.
final
bool
enableDeltaModel
;
/// Returns a representation of this object as a JSON object.
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>?
autofill
=
autofillConfiguration
.
toJson
();
...
...
@@ -655,6 +681,7 @@ class TextInputConfiguration {
'keyboardAppearance'
:
keyboardAppearance
.
toString
(),
'enableIMEPersonalizedLearning'
:
enableIMEPersonalizedLearning
,
if
(
autofill
!=
null
)
'autofill'
:
autofill
,
'enableDeltaModel'
:
enableDeltaModel
,
};
}
}
...
...
@@ -956,6 +983,16 @@ abstract class TextInputClient {
/// formatting.
void
updateEditingValue
(
TextEditingValue
value
);
/// Requests that this client update its editing state by applying the deltas
/// received from the engine.
///
/// The list of [TextEditingDelta]'s are treated as changes that will be applied
/// to the client's editing state. A change is any mutation to the raw text
/// value, or any updates to the selection and/or composing region.
///
/// {@macro flutter.services.TextEditingDelta.optIn}
void
updateEditingValueWithDeltas
(
List
<
TextEditingDelta
>
textEditingDeltas
);
/// Requests that this client perform the given action.
void
performAction
(
TextInputAction
action
);
...
...
@@ -1447,6 +1484,18 @@ class TextInput {
case
'TextInputClient.updateEditingState'
:
_currentConnection
!.
_client
.
updateEditingValue
(
TextEditingValue
.
fromJSON
(
args
[
1
]
as
Map
<
String
,
dynamic
>));
break
;
case
'TextInputClient.updateEditingStateWithDeltas'
:
final
List
<
TextEditingDelta
>
deltas
=
<
TextEditingDelta
>[];
final
Map
<
String
,
dynamic
>
encoded
=
args
[
1
]
as
Map
<
String
,
dynamic
>;
for
(
final
dynamic
encodedDelta
in
encoded
[
'deltas'
])
{
final
TextEditingDelta
delta
=
TextEditingDelta
.
fromJSON
(
encodedDelta
as
Map
<
String
,
dynamic
>);
deltas
.
add
(
delta
);
}
_currentConnection
!.
_client
.
updateEditingValueWithDeltas
(
deltas
);
break
;
case
'TextInputClient.performAction'
:
_currentConnection
!.
_client
.
performAction
(
_toTextInputAction
(
args
[
1
]
as
String
));
break
;
...
...
packages/flutter/lib/src/widgets/editable_text.dart
View file @
bf66cc2e
...
...
@@ -1795,6 +1795,15 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
@override
TextEditingValue
get
currentTextEditingValue
=>
_value
;
@override
void
updateEditingValueWithDeltas
(
List
<
TextEditingDelta
>
textEditingDeltas
)
{
TextEditingValue
value
=
_value
;
for
(
final
TextEditingDelta
delta
in
textEditingDeltas
)
{
value
=
delta
.
apply
(
value
);
}
updateEditingValue
(
value
);
}
@override
void
updateEditingValue
(
TextEditingValue
value
)
{
// This method handles text editing state updates from the platform text
...
...
packages/flutter/test/services/autofill_test.dart
View file @
bf66cc2e
...
...
@@ -106,6 +106,19 @@ class FakeAutofillClient implements TextInputClient, AutofillClient {
latestMethodCall
=
'updateEditingValue'
;
}
@override
void
updateEditingValueWithDeltas
(
List
<
TextEditingDelta
>
textEditingDeltas
)
{
TextEditingValue
newEditingValue
=
currentTextEditingValue
;
for
(
final
TextEditingDelta
delta
in
textEditingDeltas
)
{
newEditingValue
=
delta
.
apply
(
newEditingValue
);
}
currentTextEditingValue
=
newEditingValue
;
latestMethodCall
=
'updateEditingValueWithDeltas'
;
}
@override
AutofillScope
?
currentAutofillScope
;
...
...
packages/flutter/test/services/text_editing_delta_test.dart
0 → 100644
View file @
bf66cc2e
// 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.
import
'dart:convert'
show
jsonDecode
;
import
'package:flutter/services.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
group
(
'TextEditingDeltaInsertion'
,
()
{
test
(
'Verify creation of insertion delta when inserting at a collapsed selection.'
,
()
{
const
String
jsonInsertionDelta
=
'{'
'"oldText": "",'
' "deltaText": "let there be text",'
' "deltaStart": 0,'
' "deltaEnd": 0,'
' "selectionBase": 17,'
' "selectionExtent": 17,'
' "selectionAffinity" : "TextAffinity.downstream" ,'
' "selectionIsDirectional": false,'
' "composingBase": -1,'
' "composingExtent": -1}'
;
final
TextEditingDeltaInsertion
delta
=
TextEditingDelta
.
fromJSON
(
jsonDecode
(
jsonInsertionDelta
)
as
Map
<
String
,
dynamic
>)
as
TextEditingDeltaInsertion
;
const
TextRange
expectedComposing
=
TextRange
.
empty
;
const
int
expectedInsertionOffset
=
0
;
const
TextSelection
expectedSelection
=
TextSelection
.
collapsed
(
offset:
17
);
expect
(
delta
.
oldText
,
''
);
expect
(
delta
.
textInserted
,
'let there be text'
);
expect
(
delta
.
insertionOffset
,
expectedInsertionOffset
);
expect
(
delta
.
selection
,
expectedSelection
);
expect
(
delta
.
composing
,
expectedComposing
);
});
test
(
'Verify creation of insertion delta when inserting at end of composing region.'
,
()
{
const
String
jsonInsertionDelta
=
'{'
'"oldText": "hello worl",'
' "deltaText": "world",'
' "deltaStart": 6,'
' "deltaEnd": 10,'
' "selectionBase": 11,'
' "selectionExtent": 11,'
' "selectionAffinity" : "TextAffinity.downstream",'
' "selectionIsDirectional": false,'
' "composingBase": 6,'
' "composingExtent": 11}'
;
final
TextEditingDeltaInsertion
delta
=
TextEditingDelta
.
fromJSON
(
jsonDecode
(
jsonInsertionDelta
)
as
Map
<
String
,
dynamic
>)
as
TextEditingDeltaInsertion
;
const
TextRange
expectedComposing
=
TextRange
(
start:
6
,
end:
11
);
const
int
expectedInsertionOffset
=
10
;
const
TextSelection
expectedSelection
=
TextSelection
.
collapsed
(
offset:
11
);
expect
(
delta
.
oldText
,
'hello worl'
);
expect
(
delta
.
textInserted
,
'd'
);
expect
(
delta
.
insertionOffset
,
expectedInsertionOffset
);
expect
(
delta
.
selection
,
expectedSelection
);
expect
(
delta
.
composing
,
expectedComposing
);
});
});
group
(
'TextEditingDeltaDeletion'
,
()
{
test
(
'Verify creation of deletion delta when deleting.'
,
()
{
const
String
jsonDeletionDelta
=
'{'
'"oldText": "let there be text.",'
' "deltaText": "",'
' "deltaStart": 1,'
' "deltaEnd": 2,'
' "selectionBase": 1,'
' "selectionExtent": 1,'
' "selectionAffinity" : "TextAffinity.downstream" ,'
' "selectionIsDirectional": false,'
' "composingBase": -1,'
' "composingExtent": -1}'
;
final
TextEditingDeltaDeletion
delta
=
TextEditingDelta
.
fromJSON
(
jsonDecode
(
jsonDeletionDelta
)
as
Map
<
String
,
dynamic
>)
as
TextEditingDeltaDeletion
;
const
TextRange
expectedComposing
=
TextRange
.
empty
;
const
TextRange
expectedDeletedRange
=
TextRange
(
start:
1
,
end:
2
);
const
TextSelection
expectedSelection
=
TextSelection
.
collapsed
(
offset:
1
);
expect
(
delta
.
oldText
,
'let there be text.'
);
expect
(
delta
.
textDeleted
,
'e'
);
expect
(
delta
.
deletedRange
,
expectedDeletedRange
);
expect
(
delta
.
selection
,
expectedSelection
);
expect
(
delta
.
composing
,
expectedComposing
);
});
test
(
'Verify creation of deletion delta when deleting at end of composing region.'
,
()
{
const
String
jsonDeletionDelta
=
'{'
'"oldText": "hello world",'
' "deltaText": "worl",'
' "deltaStart": 6,'
' "deltaEnd": 11,'
' "selectionBase": 10,'
' "selectionExtent": 10,'
' "selectionAffinity" : "TextAffinity.downstream",'
' "selectionIsDirectional": false,'
' "composingBase": 6,'
' "composingExtent": 10}'
;
final
TextEditingDeltaDeletion
delta
=
TextEditingDelta
.
fromJSON
(
jsonDecode
(
jsonDeletionDelta
)
as
Map
<
String
,
dynamic
>)
as
TextEditingDeltaDeletion
;
const
TextRange
expectedComposing
=
TextRange
(
start:
6
,
end:
10
);
const
TextRange
expectedDeletedRange
=
TextRange
(
start:
10
,
end:
11
);
const
TextSelection
expectedSelection
=
TextSelection
.
collapsed
(
offset:
10
);
expect
(
delta
.
oldText
,
'hello world'
);
expect
(
delta
.
textDeleted
,
'd'
);
expect
(
delta
.
deletedRange
,
expectedDeletedRange
);
expect
(
delta
.
selection
,
expectedSelection
);
expect
(
delta
.
composing
,
expectedComposing
);
});
});
group
(
'TextEditingDeltaReplacement'
,
()
{
test
(
'Verify creation of replacement delta when replacing with longer.'
,
()
{
const
String
jsonReplacementDelta
=
'{'
'"oldText": "hello worfi",'
' "deltaText": "working",'
' "deltaStart": 6,'
' "deltaEnd": 11,'
' "selectionBase": 13,'
' "selectionExtent": 13,'
' "selectionAffinity" : "TextAffinity.downstream",'
' "selectionIsDirectional": false,'
' "composingBase": 6,'
' "composingExtent": 13}'
;
final
TextEditingDeltaReplacement
delta
=
TextEditingDelta
.
fromJSON
(
jsonDecode
(
jsonReplacementDelta
)
as
Map
<
String
,
dynamic
>)
as
TextEditingDeltaReplacement
;
const
TextRange
expectedComposing
=
TextRange
(
start:
6
,
end:
13
);
const
TextRange
expectedReplacedRange
=
TextRange
(
start:
6
,
end:
11
);
const
TextSelection
expectedSelection
=
TextSelection
.
collapsed
(
offset:
13
);
expect
(
delta
.
oldText
,
'hello worfi'
);
expect
(
delta
.
textReplaced
,
'worfi'
);
expect
(
delta
.
replacementText
,
'working'
);
expect
(
delta
.
replacedRange
,
expectedReplacedRange
);
expect
(
delta
.
selection
,
expectedSelection
);
expect
(
delta
.
composing
,
expectedComposing
);
});
test
(
'Verify creation of replacement delta when replacing with shorter.'
,
()
{
const
String
jsonReplacementDelta
=
'{'
'"oldText": "hello world",'
' "deltaText": "h",'
' "deltaStart": 6,'
' "deltaEnd": 11,'
' "selectionBase": 7,'
' "selectionExtent": 7,'
' "selectionAffinity" : "TextAffinity.downstream",'
' "selectionIsDirectional": false,'
' "composingBase": 6,'
' "composingExtent": 7}'
;
final
TextEditingDeltaReplacement
delta
=
TextEditingDelta
.
fromJSON
(
jsonDecode
(
jsonReplacementDelta
)
as
Map
<
String
,
dynamic
>)
as
TextEditingDeltaReplacement
;
const
TextRange
expectedComposing
=
TextRange
(
start:
6
,
end:
7
);
const
TextRange
expectedReplacedRange
=
TextRange
(
start:
6
,
end:
11
);
const
TextSelection
expectedSelection
=
TextSelection
.
collapsed
(
offset:
7
);
expect
(
delta
.
oldText
,
'hello world'
);
expect
(
delta
.
textReplaced
,
'world'
);
expect
(
delta
.
replacementText
,
'h'
);
expect
(
delta
.
replacedRange
,
expectedReplacedRange
);
expect
(
delta
.
selection
,
expectedSelection
);
expect
(
delta
.
composing
,
expectedComposing
);
});
test
(
'Verify creation of replacement delta when replacing with same.'
,
()
{
const
String
jsonReplacementDelta
=
'{'
'"oldText": "hello world",'
' "deltaText": "words",'
' "deltaStart": 6,'
' "deltaEnd": 11,'
' "selectionBase": 11,'
' "selectionExtent": 11,'
' "selectionAffinity" : "TextAffinity.downstream",'
' "selectionIsDirectional": false,'
' "composingBase": 6,'
' "composingExtent": 11}'
;
final
TextEditingDeltaReplacement
delta
=
TextEditingDelta
.
fromJSON
(
jsonDecode
(
jsonReplacementDelta
)
as
Map
<
String
,
dynamic
>)
as
TextEditingDeltaReplacement
;
const
TextRange
expectedComposing
=
TextRange
(
start:
6
,
end:
11
);
const
TextRange
expectedReplacedRange
=
TextRange
(
start:
6
,
end:
11
);
const
TextSelection
expectedSelection
=
TextSelection
.
collapsed
(
offset:
11
);
expect
(
delta
.
oldText
,
'hello world'
);
expect
(
delta
.
textReplaced
,
'world'
);
expect
(
delta
.
replacementText
,
'words'
);
expect
(
delta
.
replacedRange
,
expectedReplacedRange
);
expect
(
delta
.
selection
,
expectedSelection
);
expect
(
delta
.
composing
,
expectedComposing
);
});
});
group
(
'TextEditingDeltaNonTextUpdate'
,
()
{
test
(
'Verify non text update delta created.'
,
()
{
const
String
jsonNonTextUpdateDelta
=
'{'
'"oldText": "hello world",'
' "deltaText": "",'
' "deltaStart": -1,'
' "deltaEnd": -1,'
' "selectionBase": 10,'
' "selectionExtent": 10,'
' "selectionAffinity" : "TextAffinity.downstream",'
' "selectionIsDirectional": false,'
' "composingBase": 6,'
' "composingExtent": 11}'
;
final
TextEditingDeltaNonTextUpdate
delta
=
TextEditingDelta
.
fromJSON
(
jsonDecode
(
jsonNonTextUpdateDelta
)
as
Map
<
String
,
dynamic
>)
as
TextEditingDeltaNonTextUpdate
;
const
TextRange
expectedComposing
=
TextRange
(
start:
6
,
end:
11
);
const
TextSelection
expectedSelection
=
TextSelection
.
collapsed
(
offset:
10
);
expect
(
delta
.
oldText
,
'hello world'
);
expect
(
delta
.
selection
,
expectedSelection
);
expect
(
delta
.
composing
,
expectedComposing
);
});
});
}
packages/flutter/test/services/text_input_test.dart
View file @
bf66cc2e
...
...
@@ -119,6 +119,7 @@ void main() {
expect
(
configuration
.
inputType
,
TextInputType
.
text
);
expect
(
configuration
.
readOnly
,
false
);
expect
(
configuration
.
obscureText
,
false
);
expect
(
configuration
.
enableDeltaModel
,
false
);
expect
(
configuration
.
autocorrect
,
true
);
expect
(
configuration
.
actionLabel
,
null
);
expect
(
configuration
.
textCapitalization
,
TextCapitalization
.
none
);
...
...
@@ -450,6 +451,11 @@ class FakeTextInputClient implements TextInputClient {
latestMethodCall
=
'updateEditingValue'
;
}
@override
void
updateEditingValueWithDeltas
(
List
<
TextEditingDelta
>
textEditingDeltas
)
{
latestMethodCall
=
'updateEditingValueWithDeltas'
;
}
@override
void
updateFloatingCursor
(
RawFloatingCursorPoint
point
)
{
latestMethodCall
=
'updateFloatingCursor'
;
...
...
packages/flutter/test/widgets/editable_text_test.dart
View file @
bf66cc2e
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment