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
3fd3447f
Unverified
Commit
3fd3447f
authored
Jan 05, 2021
by
Kate Lovett
Committed by
GitHub
Jan 05, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add PrioritizedIntents to support multiple shortcut configurations (#72560)
parent
542084d0
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
135 additions
and
1 deletion
+135
-1
actions.dart
packages/flutter/lib/src/widgets/actions.dart
+47
-0
app.dart
packages/flutter/lib/src/widgets/app.dart
+7
-1
shortcuts_test.dart
packages/flutter/test/widgets/shortcuts_test.dart
+81
-0
No files found.
packages/flutter/lib/src/widgets/actions.dart
View file @
3fd3447f
...
...
@@ -1321,3 +1321,50 @@ class DismissIntent extends Intent {
///
/// This is an abstract class that serves as a base class for dismiss actions.
abstract
class
DismissAction
extends
Action
<
DismissIntent
>
{}
/// An [Intent] that evaluates a series of specified [orderedIntents] for
/// execution.
class
PrioritizedIntents
extends
Intent
{
/// Creates a set of const [PrioritizedIntents].
const
PrioritizedIntents
({
required
this
.
orderedIntents
,
})
:
assert
(
orderedIntents
!=
null
);
/// List of intents to be evaluated in order for execution. When an
/// [Action.isEnabled] returns true, that action will be invoked and
/// progression through the ordered intents stops.
final
List
<
Intent
>
orderedIntents
;
}
/// An [Action] that iterates through a list of [Intent]s, invoking the first
/// that is enabled.
class
PrioritizedAction
extends
Action
<
PrioritizedIntents
>
{
late
Action
<
dynamic
>
_selectedAction
;
late
Intent
_selectedIntent
;
@override
bool
isEnabled
(
PrioritizedIntents
intent
)
{
final
FocusNode
?
focus
=
primaryFocus
;
if
(
focus
==
null
||
focus
.
context
==
null
)
return
false
;
for
(
final
Intent
candidateIntent
in
intent
.
orderedIntents
)
{
final
Action
<
Intent
>?
candidateAction
=
Actions
.
maybeFind
<
Intent
>(
focus
.
context
!,
intent:
candidateIntent
,
);
if
(
candidateAction
!=
null
&&
candidateAction
.
isEnabled
(
candidateIntent
))
{
_selectedAction
=
candidateAction
;
_selectedIntent
=
candidateIntent
;
return
true
;
}
}
return
false
;
}
@override
Object
?
invoke
(
PrioritizedIntents
intent
)
{
assert
(
_selectedAction
!=
null
);
assert
(
_selectedIntent
!=
null
);
_selectedAction
.
invoke
(
_selectedIntent
);
}
}
packages/flutter/lib/src/widgets/app.dart
View file @
3fd3447f
...
...
@@ -1024,7 +1024,12 @@ class WidgetsApp extends StatefulWidget {
// Default shortcuts for the web platform.
static
final
Map
<
LogicalKeySet
,
Intent
>
_defaultWebShortcuts
=
<
LogicalKeySet
,
Intent
>{
// Activation
LogicalKeySet
(
LogicalKeyboardKey
.
space
):
const
ActivateIntent
(),
LogicalKeySet
(
LogicalKeyboardKey
.
space
):
const
PrioritizedIntents
(
orderedIntents:
<
Intent
>[
ActivateIntent
(),
ScrollIntent
(
direction:
AxisDirection
.
down
,
type:
ScrollIncrementType
.
page
),
]
),
// On the web, enter activates buttons, but not other controls.
LogicalKeySet
(
LogicalKeyboardKey
.
enter
):
const
ButtonActivateIntent
(),
...
...
@@ -1100,6 +1105,7 @@ class WidgetsApp extends StatefulWidget {
PreviousFocusIntent:
PreviousFocusAction
(),
DirectionalFocusIntent:
DirectionalFocusAction
(),
ScrollIntent:
ScrollAction
(),
PrioritizedIntents:
PrioritizedAction
(),
};
@override
...
...
packages/flutter/test/widgets/shortcuts_test.dart
View file @
3fd3447f
...
...
@@ -579,5 +579,86 @@ void main() {
expect
(
description
[
0
],
equalsIgnoringHashCodes
(
'manager: ShortcutManager#00000(shortcuts: {})'
));
expect
(
description
[
1
],
equalsIgnoringHashCodes
(
'shortcuts: {{Key A + Key B}: ActivateIntent#00000}'
));
});
testWidgets
(
'Shortcuts support multiple intents'
,
(
WidgetTester
tester
)
async
{
tester
.
binding
.
focusManager
.
highlightStrategy
=
FocusHighlightStrategy
.
alwaysTraditional
;
bool
?
value
=
true
;
Widget
buildApp
()
{
return
MaterialApp
(
shortcuts:
<
LogicalKeySet
,
Intent
>{
LogicalKeySet
(
LogicalKeyboardKey
.
space
):
const
PrioritizedIntents
(
orderedIntents:
<
Intent
>[
ActivateIntent
(),
ScrollIntent
(
direction:
AxisDirection
.
down
,
type:
ScrollIncrementType
.
page
),
]
),
LogicalKeySet
(
LogicalKeyboardKey
.
tab
):
const
NextFocusIntent
(),
LogicalKeySet
(
LogicalKeyboardKey
.
pageUp
):
const
ScrollIntent
(
direction:
AxisDirection
.
up
,
type:
ScrollIncrementType
.
page
),
},
home:
Material
(
child:
Center
(
child:
ListView
(
primary:
true
,
children:
<
Widget
>
[
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
Checkbox
(
value:
value
,
onChanged:
(
bool
?
newValue
)
=>
setState
(()
{
value
=
newValue
;
}),
focusColor:
Colors
.
orange
[
500
],
);
},
),
Container
(
color:
Colors
.
blue
,
height:
1000
,
)
],
),
),
),
);
}
await
tester
.
pumpWidget
(
buildApp
());
await
tester
.
pumpAndSettle
();
expect
(
tester
.
binding
.
focusManager
.
primaryFocus
!.
toStringShort
(),
equalsIgnoringHashCodes
(
'FocusScopeNode#00000(_ModalScopeState<dynamic> Focus Scope [PRIMARY FOCUS])'
),
);
final
ScrollController
controller
=
PrimaryScrollController
.
of
(
tester
.
element
(
find
.
byType
(
ListView
))
)!;
expect
(
controller
.
position
.
pixels
,
0.0
);
expect
(
value
,
isTrue
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
space
);
await
tester
.
pumpAndSettle
();
// ScrollView scrolls
expect
(
controller
.
position
.
pixels
,
448.0
);
expect
(
value
,
isTrue
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
pageUp
);
await
tester
.
pumpAndSettle
();
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
pumpAndSettle
();
// Focus is now on the checkbox.
expect
(
tester
.
binding
.
focusManager
.
primaryFocus
!.
toStringShort
(),
equalsIgnoringHashCodes
(
'FocusNode#00000([PRIMARY FOCUS])'
),
);
expect
(
value
,
isTrue
);
expect
(
controller
.
position
.
pixels
,
0.0
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
space
);
await
tester
.
pumpAndSettle
();
// Checkbox is toggled, scroll view does not scroll.
expect
(
value
,
isFalse
);
expect
(
controller
.
position
.
pixels
,
0.0
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
space
);
await
tester
.
pumpAndSettle
();
expect
(
value
,
isTrue
);
expect
(
controller
.
position
.
pixels
,
0.0
);
});
});
}
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