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
01c98fa9
Unverified
Commit
01c98fa9
authored
May 19, 2021
by
Tong Mu
Committed by
GitHub
May 19, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Character activator (#81807)
parent
7cdd33fe
Changes
4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
289 additions
and
54 deletions
+289
-54
shortcuts.dart
packages/flutter/lib/src/widgets/shortcuts.dart
+148
-26
shortcuts_test.dart
packages/flutter/test/widgets/shortcuts_test.dart
+111
-2
controller.dart
packages/flutter_test/lib/src/controller.dart
+2
-2
event_simulation.dart
packages/flutter_test/lib/src/event_simulation.dart
+28
-24
No files found.
packages/flutter/lib/src/widgets/shortcuts.dart
View file @
01c98fa9
This diff is collapsed.
Click to expand it.
packages/flutter/test/widgets/shortcuts_test.dart
View file @
01c98fa9
...
@@ -9,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart';
...
@@ -9,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart';
typedef
PostInvokeCallback
=
void
Function
({
Action
<
Intent
>
action
,
Intent
intent
,
BuildContext
?
context
,
ActionDispatcher
dispatcher
});
typedef
PostInvokeCallback
=
void
Function
({
Action
<
Intent
>
action
,
Intent
intent
,
BuildContext
?
context
,
ActionDispatcher
dispatcher
});
class
TestAction
extends
CallbackAction
<
Test
Intent
>
{
class
TestAction
extends
CallbackAction
<
Intent
>
{
TestAction
({
TestAction
({
required
OnInvokeCallback
onInvoke
,
required
OnInvokeCallback
onInvoke
,
})
:
assert
(
onInvoke
!=
null
),
})
:
assert
(
onInvoke
!=
null
),
...
@@ -31,10 +31,47 @@ class TestDispatcher extends ActionDispatcher {
...
@@ -31,10 +31,47 @@ class TestDispatcher extends ActionDispatcher {
}
}
}
}
/// An activator that accepts down events that has [key] as the logical key.
///
/// This class is used only to tests. It is intentionally designed poorly by
/// returning null in [triggers], and checks [key] in [accepts].
class
DumbLogicalActivator
extends
ShortcutActivator
{
const
DumbLogicalActivator
(
this
.
key
);
final
LogicalKeyboardKey
key
;
@override
Iterable
<
LogicalKeyboardKey
>?
get
triggers
=>
null
;
@override
bool
accepts
(
RawKeyEvent
event
,
RawKeyboard
state
)
{
return
event
is
RawKeyDownEvent
&&
event
.
logicalKey
==
key
;
}
/// Returns a short and readable description of the key combination.
///
/// Intended to be used in debug mode for logging purposes. In release mode,
/// [debugDescribeKeys] returns an empty string.
@override
String
debugDescribeKeys
()
{
String
result
=
''
;
assert
(()
{
result
=
key
.
keyLabel
;
return
true
;
}());
return
result
;
}
}
class
TestIntent
extends
Intent
{
class
TestIntent
extends
Intent
{
const
TestIntent
();
const
TestIntent
();
}
}
class
TestIntent2
extends
Intent
{
const
TestIntent2
();
}
class
TestShortcutManager
extends
ShortcutManager
{
class
TestShortcutManager
extends
ShortcutManager
{
TestShortcutManager
(
this
.
keys
);
TestShortcutManager
(
this
.
keys
);
...
@@ -49,7 +86,13 @@ class TestShortcutManager extends ShortcutManager {
...
@@ -49,7 +86,13 @@ class TestShortcutManager extends ShortcutManager {
}
}
}
}
Widget
activatorTester
(
ShortcutActivator
activator
,
ValueSetter
<
Intent
>
onInvoke
)
{
Widget
activatorTester
(
ShortcutActivator
activator
,
ValueSetter
<
Intent
>
onInvoke
,
[
ShortcutActivator
?
activator2
,
ValueSetter
<
Intent
>?
onInvoke2
,
])
{
final
bool
hasSecond
=
activator2
!=
null
&&
onInvoke2
!=
null
;
return
Actions
(
return
Actions
(
key:
GlobalKey
(),
key:
GlobalKey
(),
actions:
<
Type
,
Action
<
Intent
>>{
actions:
<
Type
,
Action
<
Intent
>>{
...
@@ -57,10 +100,16 @@ Widget activatorTester(ShortcutActivator activator, ValueSetter<Intent> onInvoke
...
@@ -57,10 +100,16 @@ Widget activatorTester(ShortcutActivator activator, ValueSetter<Intent> onInvoke
onInvoke
(
intent
);
onInvoke
(
intent
);
return
true
;
return
true
;
}),
}),
if
(
hasSecond
)
TestIntent2:
TestAction
(
onInvoke:
(
Intent
intent
)
{
onInvoke2
(
intent
);
}),
},
},
child:
Shortcuts
(
child:
Shortcuts
(
shortcuts:
<
ShortcutActivator
,
Intent
>{
shortcuts:
<
ShortcutActivator
,
Intent
>{
activator:
const
TestIntent
(),
activator:
const
TestIntent
(),
if
(
hasSecond
)
activator2:
const
TestIntent2
(),
},
},
child:
const
Focus
(
child:
const
Focus
(
autofocus:
true
,
autofocus:
true
,
...
@@ -967,5 +1016,65 @@ void main() {
...
@@ -967,5 +1016,65 @@ void main() {
expect
(
value
,
isTrue
);
expect
(
value
,
isTrue
);
expect
(
controller
.
position
.
pixels
,
0.0
);
expect
(
controller
.
position
.
pixels
,
0.0
);
});
});
testWidgets
(
'Shortcuts support activators that returns null in triggers'
,
(
WidgetTester
tester
)
async
{
int
invoked
=
0
;
await
tester
.
pumpWidget
(
activatorTester
(
const
DumbLogicalActivator
(
LogicalKeyboardKey
.
keyC
),
(
Intent
intent
)
{
invoked
+=
1
;
},
const
SingleActivator
(
LogicalKeyboardKey
.
keyC
,
control:
true
),
(
Intent
intent
)
{
invoked
+=
10
;
},
));
await
tester
.
pump
();
// Press KeyC: Accepted by DumbLogicalActivator
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
keyC
);
expect
(
invoked
,
1
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
keyC
);
expect
(
invoked
,
1
);
invoked
=
0
;
// Press ControlLeft + KeyC: Accepted by SingleActivator
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
controlLeft
);
expect
(
invoked
,
0
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
keyC
);
expect
(
invoked
,
10
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
keyC
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
controlLeft
);
expect
(
invoked
,
10
);
invoked
=
0
;
// Press ControlLeft + ShiftLeft + KeyC: Accepted by DumbLogicalActivator
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
shiftLeft
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
controlLeft
);
expect
(
invoked
,
0
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
keyC
);
expect
(
invoked
,
1
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
keyC
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
controlLeft
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
shiftLeft
);
expect
(
invoked
,
1
);
invoked
=
0
;
});
});
group
(
'CharacterActivator'
,
()
{
testWidgets
(
'is triggered on events with correct character'
,
(
WidgetTester
tester
)
async
{
int
invoked
=
0
;
await
tester
.
pumpWidget
(
activatorTester
(
const
CharacterActivator
(
'?'
),
(
Intent
intent
)
{
invoked
+=
1
;
},
));
await
tester
.
pump
();
// Press KeyC: Accepted by DumbLogicalActivator
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
shiftLeft
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
slash
,
character:
'?'
);
expect
(
invoked
,
1
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
slash
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
shiftLeft
);
expect
(
invoked
,
1
);
invoked
=
0
;
});
});
});
}
}
packages/flutter_test/lib/src/controller.dart
View file @
01c98fa9
...
@@ -1021,10 +1021,10 @@ abstract class WidgetController {
...
@@ -1021,10 +1021,10 @@ abstract class WidgetController {
///
///
/// - [sendKeyUpEvent] to simulate the corresponding key up event.
/// - [sendKeyUpEvent] to simulate the corresponding key up event.
/// - [sendKeyEvent] to simulate both the key up and key down in the same call.
/// - [sendKeyEvent] to simulate both the key up and key down in the same call.
Future
<
bool
>
sendKeyDownEvent
(
LogicalKeyboardKey
key
,
{
String
platform
=
_defaultPlatform
})
async
{
Future
<
bool
>
sendKeyDownEvent
(
LogicalKeyboardKey
key
,
{
String
?
character
,
String
platform
=
_defaultPlatform
})
async
{
assert
(
platform
!=
null
);
assert
(
platform
!=
null
);
// Internally wrapped in async guard.
// Internally wrapped in async guard.
return
simulateKeyDownEvent
(
key
,
platform:
platform
);
return
simulateKeyDownEvent
(
key
,
character:
character
,
platform:
platform
);
}
}
/// Simulates sending a physical key up event through the system channel.
/// Simulates sending a physical key up event through the system channel.
...
...
packages/flutter_test/lib/src/event_simulation.dart
View file @
01c98fa9
...
@@ -194,6 +194,7 @@ class KeyEventSimulator {
...
@@ -194,6 +194,7 @@ class KeyEventSimulator {
required
String
platform
,
required
String
platform
,
bool
isDown
=
true
,
bool
isDown
=
true
,
PhysicalKeyboardKey
?
physicalKey
,
PhysicalKeyboardKey
?
physicalKey
,
String
?
character
,
})
{
})
{
assert
(
_osIsSupported
(
platform
),
'Platform
$platform
not supported for key simulation'
);
assert
(
_osIsSupported
(
platform
),
'Platform
$platform
not supported for key simulation'
);
...
@@ -211,27 +212,31 @@ class KeyEventSimulator {
...
@@ -211,27 +212,31 @@ class KeyEventSimulator {
'keymap'
:
platform
,
'keymap'
:
platform
,
};
};
if
(
kIsWeb
)
{
final
String
resultCharacter
=
character
??
_keyLabel
(
key
);
void
assignWeb
()
{
result
[
'code'
]
=
_getWebKeyCode
(
key
);
result
[
'code'
]
=
_getWebKeyCode
(
key
);
result
[
'key'
]
=
_keyLabel
(
key
)
;
result
[
'key'
]
=
resultCharacter
;
result
[
'metaState'
]
=
_getWebModifierFlags
(
key
,
isDown
);
result
[
'metaState'
]
=
_getWebModifierFlags
(
key
,
isDown
);
}
if
(
kIsWeb
)
{
assignWeb
();
return
result
;
return
result
;
}
}
switch
(
platform
)
{
switch
(
platform
)
{
case
'android'
:
case
'android'
:
result
[
'keyCode'
]
=
keyCode
;
result
[
'keyCode'
]
=
keyCode
;
if
(
_keyLabel
(
key
)
.
isNotEmpty
)
{
if
(
resultCharacter
.
isNotEmpty
)
{
result
[
'codePoint'
]
=
_keyLabel
(
key
)
.
codeUnitAt
(
0
);
result
[
'codePoint'
]
=
resultCharacter
.
codeUnitAt
(
0
);
result
[
'character'
]
=
_keyLabel
(
key
)
;
result
[
'character'
]
=
resultCharacter
;
}
}
result
[
'scanCode'
]
=
scanCode
;
result
[
'scanCode'
]
=
scanCode
;
result
[
'metaState'
]
=
_getAndroidModifierFlags
(
key
,
isDown
);
result
[
'metaState'
]
=
_getAndroidModifierFlags
(
key
,
isDown
);
break
;
break
;
case
'fuchsia'
:
case
'fuchsia'
:
result
[
'hidUsage'
]
=
physicalKey
.
usbHidUsage
;
result
[
'hidUsage'
]
=
physicalKey
.
usbHidUsage
;
if
(
_keyLabel
(
key
)
.
isNotEmpty
)
{
if
(
resultCharacter
.
isNotEmpty
)
{
result
[
'codePoint'
]
=
_keyLabel
(
key
)
.
codeUnitAt
(
0
);
result
[
'codePoint'
]
=
resultCharacter
.
codeUnitAt
(
0
);
}
}
result
[
'modifiers'
]
=
_getFuchsiaModifierFlags
(
key
,
isDown
);
result
[
'modifiers'
]
=
_getFuchsiaModifierFlags
(
key
,
isDown
);
break
;
break
;
...
@@ -240,34 +245,33 @@ class KeyEventSimulator {
...
@@ -240,34 +245,33 @@ class KeyEventSimulator {
result
[
'keyCode'
]
=
keyCode
;
result
[
'keyCode'
]
=
keyCode
;
result
[
'scanCode'
]
=
scanCode
;
result
[
'scanCode'
]
=
scanCode
;
result
[
'modifiers'
]
=
_getGlfwModifierFlags
(
key
,
isDown
);
result
[
'modifiers'
]
=
_getGlfwModifierFlags
(
key
,
isDown
);
result
[
'unicodeScalarValues'
]
=
_keyLabel
(
key
).
isNotEmpty
?
_keyLabel
(
key
)
.
codeUnitAt
(
0
)
:
0
;
result
[
'unicodeScalarValues'
]
=
resultCharacter
.
isNotEmpty
?
resultCharacter
.
codeUnitAt
(
0
)
:
0
;
break
;
break
;
case
'macos'
:
case
'macos'
:
result
[
'keyCode'
]
=
scanCode
;
result
[
'keyCode'
]
=
scanCode
;
if
(
_keyLabel
(
key
)
.
isNotEmpty
)
{
if
(
resultCharacter
.
isNotEmpty
)
{
result
[
'characters'
]
=
_keyLabel
(
key
)
;
result
[
'characters'
]
=
resultCharacter
;
result
[
'charactersIgnoringModifiers'
]
=
_keyLabel
(
key
)
;
result
[
'charactersIgnoringModifiers'
]
=
resultCharacter
;
}
}
result
[
'modifiers'
]
=
_getMacOsModifierFlags
(
key
,
isDown
);
result
[
'modifiers'
]
=
_getMacOsModifierFlags
(
key
,
isDown
);
break
;
break
;
case
'ios'
:
case
'ios'
:
result
[
'keyCode'
]
=
scanCode
;
result
[
'keyCode'
]
=
scanCode
;
result
[
'characters'
]
=
_keyLabel
(
key
)
;
result
[
'characters'
]
=
resultCharacter
;
result
[
'charactersIgnoringModifiers'
]
=
_keyLabel
(
key
)
;
result
[
'charactersIgnoringModifiers'
]
=
resultCharacter
;
result
[
'modifiers'
]
=
_getIOSModifierFlags
(
key
,
isDown
);
result
[
'modifiers'
]
=
_getIOSModifierFlags
(
key
,
isDown
);
break
;
break
;
case
'web'
:
result
[
'code'
]
=
_getWebKeyCode
(
key
);
result
[
'key'
]
=
_keyLabel
(
key
);
result
[
'metaState'
]
=
_getWebModifierFlags
(
key
,
isDown
);
break
;
case
'windows'
:
case
'windows'
:
result
[
'keyCode'
]
=
keyCode
;
result
[
'keyCode'
]
=
keyCode
;
result
[
'scanCode'
]
=
scanCode
;
result
[
'scanCode'
]
=
scanCode
;
if
(
_keyLabel
(
key
)
.
isNotEmpty
)
{
if
(
resultCharacter
.
isNotEmpty
)
{
result
[
'characterCodePoint'
]
=
_keyLabel
(
key
)
.
codeUnitAt
(
0
);
result
[
'characterCodePoint'
]
=
resultCharacter
.
codeUnitAt
(
0
);
}
}
result
[
'modifiers'
]
=
_getWindowsModifierFlags
(
key
,
isDown
);
result
[
'modifiers'
]
=
_getWindowsModifierFlags
(
key
,
isDown
);
break
;
case
'web'
:
assignWeb
();
break
;
}
}
return
result
;
return
result
;
}
}
...
@@ -631,12 +635,12 @@ class KeyEventSimulator {
...
@@ -631,12 +635,12 @@ class KeyEventSimulator {
/// See also:
/// See also:
///
///
/// - [simulateKeyUpEvent] to simulate the corresponding key up event.
/// - [simulateKeyUpEvent] to simulate the corresponding key up event.
static
Future
<
bool
>
simulateKeyDownEvent
(
LogicalKeyboardKey
key
,
{
String
?
platform
,
PhysicalKeyboardKey
?
physicalKey
})
async
{
static
Future
<
bool
>
simulateKeyDownEvent
(
LogicalKeyboardKey
key
,
{
String
?
platform
,
PhysicalKeyboardKey
?
physicalKey
,
String
?
character
})
async
{
return
TestAsyncUtils
.
guard
<
bool
>(()
async
{
return
TestAsyncUtils
.
guard
<
bool
>(()
async
{
platform
??=
Platform
.
operatingSystem
;
platform
??=
Platform
.
operatingSystem
;
assert
(
_osIsSupported
(
platform
!),
'Platform
$platform
not supported for key simulation'
);
assert
(
_osIsSupported
(
platform
!),
'Platform
$platform
not supported for key simulation'
);
final
Map
<
String
,
dynamic
>
data
=
getKeyData
(
key
,
platform:
platform
!,
isDown:
true
,
physicalKey:
physicalKey
);
final
Map
<
String
,
dynamic
>
data
=
getKeyData
(
key
,
platform:
platform
!,
isDown:
true
,
physicalKey:
physicalKey
,
character:
character
);
bool
result
=
false
;
bool
result
=
false
;
await
ServicesBinding
.
instance
!.
defaultBinaryMessenger
.
handlePlatformMessage
(
await
ServicesBinding
.
instance
!.
defaultBinaryMessenger
.
handlePlatformMessage
(
SystemChannels
.
keyEvent
.
name
,
SystemChannels
.
keyEvent
.
name
,
...
@@ -715,8 +719,8 @@ class KeyEventSimulator {
...
@@ -715,8 +719,8 @@ class KeyEventSimulator {
/// See also:
/// See also:
///
///
/// - [simulateKeyUpEvent] to simulate the corresponding key up event.
/// - [simulateKeyUpEvent] to simulate the corresponding key up event.
Future
<
bool
>
simulateKeyDownEvent
(
LogicalKeyboardKey
key
,
{
String
?
platform
,
PhysicalKeyboardKey
?
physicalKey
})
{
Future
<
bool
>
simulateKeyDownEvent
(
LogicalKeyboardKey
key
,
{
String
?
platform
,
PhysicalKeyboardKey
?
physicalKey
,
String
?
character
})
{
return
KeyEventSimulator
.
simulateKeyDownEvent
(
key
,
platform:
platform
,
physicalKey:
physicalKey
);
return
KeyEventSimulator
.
simulateKeyDownEvent
(
key
,
platform:
platform
,
physicalKey:
physicalKey
,
character:
character
);
}
}
/// Simulates sending a hardware key up event through the system channel.
/// Simulates sending a hardware key up event through the system channel.
...
...
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