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
bc54c2fd
Unverified
Commit
bc54c2fd
authored
Jan 07, 2022
by
Tong Mu
Committed by
GitHub
Jan 07, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
RawKeyboard repeat events, and SingleActivator.includeRepeats (#96154)
parent
3e5ddffb
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
127 additions
and
12 deletions
+127
-12
raw_keyboard.dart
packages/flutter/lib/src/services/raw_keyboard.dart
+20
-3
shortcuts.dart
packages/flutter/lib/src/widgets/shortcuts.dart
+15
-4
raw_keyboard_test.dart
packages/flutter/test/services/raw_keyboard_test.dart
+43
-0
shortcuts_test.dart
packages/flutter/test/widgets/shortcuts_test.dart
+49
-5
No files found.
packages/flutter/lib/src/services/raw_keyboard.dart
View file @
bc54c2fd
...
...
@@ -280,10 +280,14 @@ abstract class RawKeyEvent with Diagnosticable {
const
RawKeyEvent
({
required
this
.
data
,
this
.
character
,
this
.
repeat
=
false
,
});
/// Creates a concrete [RawKeyEvent] class from a message in the form received
/// on the [SystemChannels.keyEvent] channel.
///
/// [RawKeyEvent.repeat] will be derived from the current keyboard state,
/// instead of using the message information.
factory
RawKeyEvent
.
fromMessage
(
Map
<
String
,
Object
?>
message
)
{
String
?
character
;
RawKeyEventData
_dataFromWeb
()
{
...
...
@@ -388,10 +392,11 @@ abstract class RawKeyEvent with Diagnosticable {
throw
FlutterError
(
'Unknown keymap for key events:
$keymap
'
);
}
}
final
bool
repeat
=
RawKeyboard
.
instance
.
physicalKeysPressed
.
contains
(
data
.
physicalKey
);
final
String
type
=
message
[
'type'
]!
as
String
;
switch
(
type
)
{
case
'keydown'
:
return
RawKeyDownEvent
(
data:
data
,
character:
character
);
return
RawKeyDownEvent
(
data:
data
,
character:
character
,
repeat:
repeat
);
case
'keyup'
:
return
RawKeyUpEvent
(
data:
data
);
default
:
...
...
@@ -504,6 +509,15 @@ abstract class RawKeyEvent with Diagnosticable {
/// input.
final
String
?
character
;
/// Whether this is a repeated down event.
///
/// When a key is held down, the systems usually fire a down event and then
/// a series of repeated down events. The [repeat] is false for the
/// first event and true for the following events.
///
/// The [repeat] attribute is always false for [RawKeyUpEvent]s.
final
bool
repeat
;
/// Platform-specific information about the key event.
final
RawKeyEventData
data
;
...
...
@@ -512,6 +526,8 @@ abstract class RawKeyEvent with Diagnosticable {
super
.
debugFillProperties
(
properties
);
properties
.
add
(
DiagnosticsProperty
<
LogicalKeyboardKey
>(
'logicalKey'
,
logicalKey
));
properties
.
add
(
DiagnosticsProperty
<
PhysicalKeyboardKey
>(
'physicalKey'
,
physicalKey
));
if
(
this
is
RawKeyDownEvent
)
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'repeat'
,
repeat
));
}
}
...
...
@@ -525,7 +541,8 @@ class RawKeyDownEvent extends RawKeyEvent {
const
RawKeyDownEvent
({
required
RawKeyEventData
data
,
String
?
character
,
})
:
super
(
data:
data
,
character:
character
);
bool
repeat
=
false
,
})
:
super
(
data:
data
,
character:
character
,
repeat:
repeat
);
}
/// The user has released a key on the keyboard.
...
...
@@ -538,7 +555,7 @@ class RawKeyUpEvent extends RawKeyEvent {
const
RawKeyUpEvent
({
required
RawKeyEventData
data
,
String
?
character
,
})
:
super
(
data:
data
,
character:
character
);
})
:
super
(
data:
data
,
character:
character
,
repeat:
false
);
}
/// A callback type used by [RawKeyboard.keyEventHandler] to send key events to
...
...
packages/flutter/lib/src/widgets/shortcuts.dart
View file @
bc54c2fd
...
...
@@ -388,8 +388,7 @@ class ShortcutMapProperty extends DiagnosticsProperty<Map<ShortcutActivator, Int
/// * [CharacterActivator], an activator that represents key combinations
/// that result in the specified character, such as question mark.
class SingleActivator with Diagnosticable implements ShortcutActivator {
/// Triggered when the [trigger] key is pressed or repeated when the
/// modifiers are held.
/// Triggered when the [trigger] key is pressed while the modifiers are held.
///
/// The `trigger` should be the non-modifier key that is pressed after all the
/// modifiers, such as [LogicalKeyboardKey.keyC] as in `Ctrl+C`. It must not be
...
...
@@ -398,8 +397,9 @@ class SingleActivator with Diagnosticable implements ShortcutActivator {
/// The `control`, `shift`, `alt`, and `meta` flags represent whether
/// the respect modifier keys should be held (true) or released (false)
///
/// On each [RawKeyDownEvent] of the [trigger] key, this activator checks
/// whether the specified modifier conditions are met.
/// By default, the activator is checked on all [RawKeyDownEvent] events for
/// the [trigger] key. If `includeRepeats` is false, only the [trigger] key
/// events with a false [RawKeyDownEvent.repeat] attribute will be considered.
///
/// {@tool dartpad}
/// In the following example, the shortcut `Control + C` increases the counter:
...
...
@@ -412,6 +412,7 @@ class SingleActivator with Diagnosticable implements ShortcutActivator {
this.shift = false,
this.alt = false,
this.meta = false,
this.includeRepeats = true,
}) : // The enumerated check with `identical` is cumbersome but the only way
// since const constructors can not call functions such as `==` or
// `Set.contains`. Checking with `identical` might not work when the
...
...
@@ -482,6 +483,14 @@ class SingleActivator with Diagnosticable implements ShortcutActivator {
/// * [LogicalKeyboardKey.metaLeft], [LogicalKeyboardKey.metaRight].
final bool meta;
/// Whether this activator accepts repeat events of the [trigger] key.
///
/// If [includeRepeats] is true, the activator is checked on all
/// [RawKeyDownEvent] events for the [trigger] key. If `includeRepeats` is
/// false, only the [trigger] key events with a false [RawKeyDownEvent.repeat]
/// attribute will be considered.
final bool includeRepeats;
@override
Iterable<LogicalKeyboardKey> get triggers {
return <LogicalKeyboardKey>[trigger];
...
...
@@ -491,6 +500,7 @@ class SingleActivator with Diagnosticable implements ShortcutActivator {
bool accepts(RawKeyEvent event, RawKeyboard state) {
final Set<LogicalKeyboardKey> pressed = state.keysPressed;
return event is RawKeyDownEvent
&& (includeRepeats || !event.repeat)
&& (control == (pressed.contains(LogicalKeyboardKey.controlLeft) || pressed.contains(LogicalKeyboardKey.controlRight)))
&& (shift == (pressed.contains(LogicalKeyboardKey.shiftLeft) || pressed.contains(LogicalKeyboardKey.shiftRight)))
&& (alt == (pressed.contains(LogicalKeyboardKey.altLeft) || pressed.contains(LogicalKeyboardKey.altRight)))
...
...
@@ -522,6 +532,7 @@ class SingleActivator with Diagnosticable implements ShortcutActivator {
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<String>('
keys
', debugDescribeKeys()));
properties.add(FlagProperty('
includeRepeats
', value: includeRepeats, ifFalse: '
excluding
repeats
'));
}
}
...
...
packages/flutter/test/services/raw_keyboard_test.dart
View file @
bc54c2fd
...
...
@@ -598,6 +598,49 @@ void main() {
);
},
skip:
isBrowser
);
// [intended] This is an iOS-specific test.
testWidgets
(
'repeat events'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
late
RawKeyEvent
receivedEvent
;
RawKeyboard
.
instance
.
keyEventHandler
=
(
RawKeyEvent
event
)
{
receivedEvent
=
event
;
return
true
;
};
// Dispatch a down event.
final
Map
<
String
,
dynamic
>
downData
=
KeyEventSimulator
.
getKeyData
(
LogicalKeyboardKey
.
keyA
,
platform:
'windows'
,
);
await
ServicesBinding
.
instance
?.
defaultBinaryMessenger
.
handlePlatformMessage
(
SystemChannels
.
keyEvent
.
name
,
SystemChannels
.
keyEvent
.
codec
.
encodeMessage
(
downData
),
(
ByteData
?
data
)
{},
);
expect
(
receivedEvent
.
repeat
,
false
);
// Dispatch another down event, which should be recognized as a repeat.
await
ServicesBinding
.
instance
?.
defaultBinaryMessenger
.
handlePlatformMessage
(
SystemChannels
.
keyEvent
.
name
,
SystemChannels
.
keyEvent
.
codec
.
encodeMessage
(
downData
),
(
ByteData
?
data
)
{},
);
expect
(
receivedEvent
.
repeat
,
true
);
// Dispatch an up event.
await
ServicesBinding
.
instance
?.
defaultBinaryMessenger
.
handlePlatformMessage
(
SystemChannels
.
keyEvent
.
name
,
SystemChannels
.
keyEvent
.
codec
.
encodeMessage
(
KeyEventSimulator
.
getKeyData
(
LogicalKeyboardKey
.
keyA
,
isDown:
false
,
platform:
'windows'
,
)),
(
ByteData
?
data
)
{},
);
expect
(
receivedEvent
.
repeat
,
false
);
RawKeyboard
.
instance
.
keyEventHandler
=
null
;
},
skip:
isBrowser
);
// [intended] This is a Windows-specific test.
testWidgets
(
'sided modifiers without a side set return all sides on Windows'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
...
...
packages/flutter/test/widgets/shortcuts_test.dart
View file @
bc54c2fd
...
...
@@ -464,6 +464,33 @@ void main() {
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
},
variant:
KeySimulatorTransitModeVariant
.
all
());
testWidgets
(
'rejects repeated events if requested'
,
(
WidgetTester
tester
)
async
{
int
invoked
=
0
;
await
tester
.
pumpWidget
(
activatorTester
(
const
SingleActivator
(
LogicalKeyboardKey
.
keyC
,
control:
true
,
includeRepeats:
false
,
),
(
Intent
intent
)
{
invoked
+=
1
;
},
));
await
tester
.
pump
();
// LCtrl -> KeyC: Accept
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
controlLeft
);
expect
(
invoked
,
0
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
keyC
);
expect
(
invoked
,
1
);
await
tester
.
sendKeyRepeatEvent
(
LogicalKeyboardKey
.
keyC
);
expect
(
invoked
,
1
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
keyC
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
controlLeft
);
expect
(
invoked
,
1
);
invoked
=
0
;
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
},
variant:
KeySimulatorTransitModeVariant
.
all
());
testWidgets
(
'handles Shift-Ctrl-C'
,
(
WidgetTester
tester
)
async
{
int
invoked
=
0
;
await
tester
.
pumpWidget
(
activatorTester
(
...
...
@@ -514,8 +541,8 @@ void main() {
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
});
test
(
'diagnostics.'
,
()
{
{
group
(
'diagnostics.'
,
()
{
test
(
'single key'
,
()
{
final
DiagnosticPropertiesBuilder
builder
=
DiagnosticPropertiesBuilder
();
const
SingleActivator
(
...
...
@@ -528,9 +555,26 @@ void main() {
expect
(
description
.
length
,
equals
(
1
));
expect
(
description
[
0
],
equals
(
'keys: Key A'
));
}
}
);
{
test
(
'no repeats'
,
()
{
final
DiagnosticPropertiesBuilder
builder
=
DiagnosticPropertiesBuilder
();
const
SingleActivator
(
LogicalKeyboardKey
.
keyA
,
includeRepeats:
false
,
).
debugFillProperties
(
builder
);
final
List
<
String
>
description
=
builder
.
properties
.
where
((
DiagnosticsNode
node
)
{
return
!
node
.
isFiltered
(
DiagnosticLevel
.
info
);
}).
map
((
DiagnosticsNode
node
)
=>
node
.
toString
()).
toList
();
expect
(
description
.
length
,
equals
(
2
));
expect
(
description
[
0
],
equals
(
'keys: Key A'
));
expect
(
description
[
1
],
equals
(
'excluding repeats'
));
});
test
(
'combination'
,
()
{
final
DiagnosticPropertiesBuilder
builder
=
DiagnosticPropertiesBuilder
();
const
SingleActivator
(
...
...
@@ -547,7 +591,7 @@ void main() {
expect
(
description
.
length
,
equals
(
1
));
expect
(
description
[
0
],
equals
(
'keys: Control + Alt + Meta + Shift + Key A'
));
}
}
);
});
});
...
...
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