Unverified Commit 3a30722f authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Add convenience accessor for primaryFocus (#43859)

This adds accessors for WidgetsBinding.instance.focusManager and WidgetsBinding.instance.focusManager.primaryFocus so that they can be more easily found in IDEs and accessed.

This adds a top level getter for WidgetsBinding.instance.focusManager.primaryFocus called primaryFocus, and a static accessor FocusManager.instance that returns WidgetsBinding.instance.focusManager.
parent 5f7b4818
......@@ -101,7 +101,7 @@ class UndoableActionDispatcher extends ActionDispatcher implements Listenable {
bool get canUndo {
if (_completedActions.isNotEmpty) {
final Intent lastIntent = _completedActions.last.invocationIntent;
return lastIntent.isEnabled(WidgetsBinding.instance.focusManager.primaryFocus.context);
return lastIntent.isEnabled(primaryFocus.context);
}
return false;
}
......@@ -110,7 +110,7 @@ class UndoableActionDispatcher extends ActionDispatcher implements Listenable {
bool get canRedo {
if (_undoneActions.isNotEmpty) {
final Intent lastIntent = _undoneActions.last.invocationIntent;
return lastIntent.isEnabled(WidgetsBinding.instance.focusManager.primaryFocus?.context);
return lastIntent.isEnabled(primaryFocus?.context);
}
return false;
}
......@@ -255,14 +255,14 @@ class UndoableFocusActionBase extends UndoableAction {
@override
void invoke(FocusNode node, Intent intent) {
super.invoke(node, intent);
_previousFocus = WidgetsBinding.instance.focusManager.primaryFocus;
_previousFocus = primaryFocus;
node.requestFocus();
}
@override
void undo() {
if (_previousFocus == null) {
WidgetsBinding.instance.focusManager.primaryFocus?.unfocus();
primaryFocus?.unfocus();
return;
}
if (_previousFocus is FocusScopeNode) {
......@@ -272,7 +272,7 @@ class UndoableFocusActionBase extends UndoableAction {
// Unfocus the current node to remove it from the focused child list of
// the scope.
WidgetsBinding.instance.focusManager.primaryFocus?.unfocus();
primaryFocus?.unfocus();
// and then let the scope node be focused...
}
_previousFocus.requestFocus();
......
......@@ -168,8 +168,8 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
SelectAction.key: _createAction,
if (!kIsWeb) ActivateAction.key: _createAction,
};
_updateHighlightMode(WidgetsBinding.instance.focusManager.highlightMode);
WidgetsBinding.instance.focusManager.addHighlightModeListener(_handleFocusHighlightModeChange);
_updateHighlightMode(FocusManager.instance.highlightMode);
FocusManager.instance.addHighlightModeListener(_handleFocusHighlightModeChange);
}
void _actionHandler(FocusNode node, Intent intent){
......@@ -198,7 +198,7 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
}
void _updateHighlightMode(FocusHighlightMode mode) {
switch (WidgetsBinding.instance.focusManager.highlightMode) {
switch (FocusManager.instance.highlightMode) {
case FocusHighlightMode.touch:
_showHighlight = false;
break;
......
......@@ -508,7 +508,7 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
SelectAction.key: _createAction,
if (!kIsWeb) ActivateAction.key: _createAction,
};
WidgetsBinding.instance.focusManager.addHighlightModeListener(_handleFocusHighlightModeChange);
FocusManager.instance.addHighlightModeListener(_handleFocusHighlightModeChange);
}
@override
......@@ -522,7 +522,7 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
@override
void dispose() {
WidgetsBinding.instance.focusManager.removeHighlightModeListener(_handleFocusHighlightModeChange);
FocusManager.instance.removeHighlightModeListener(_handleFocusHighlightModeChange);
super.dispose();
}
......@@ -650,7 +650,7 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
void _updateFocusHighlights() {
bool showFocus;
switch (WidgetsBinding.instance.focusManager.highlightMode) {
switch (FocusManager.instance.highlightMode) {
case FocusHighlightMode.touch:
showFocus = false;
break;
......
......@@ -196,8 +196,8 @@ class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin {
SelectAction.key: _createAction,
if (!kIsWeb) ActivateAction.key: _createAction,
};
_updateHighlightMode(WidgetsBinding.instance.focusManager.highlightMode);
WidgetsBinding.instance.focusManager.addHighlightModeListener(_handleFocusHighlightModeChange);
_updateHighlightMode(FocusManager.instance.highlightMode);
FocusManager.instance.addHighlightModeListener(_handleFocusHighlightModeChange);
}
void _actionHandler(FocusNode node, Intent intent){
......@@ -216,7 +216,7 @@ class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin {
}
void _updateHighlightMode(FocusHighlightMode mode) {
switch (WidgetsBinding.instance.focusManager.highlightMode) {
switch (FocusManager.instance.highlightMode) {
case FocusHighlightMode.touch:
_showHighlight = false;
break;
......
......@@ -223,8 +223,8 @@ class _SwitchState extends State<Switch> with TickerProviderStateMixin {
SelectAction.key: _createAction,
if (!kIsWeb) ActivateAction.key: _createAction,
};
_updateHighlightMode(WidgetsBinding.instance.focusManager.highlightMode);
WidgetsBinding.instance.focusManager.addHighlightModeListener(_handleFocusHighlightModeChange);
_updateHighlightMode(FocusManager.instance.highlightMode);
FocusManager.instance.addHighlightModeListener(_handleFocusHighlightModeChange);
}
void _actionHandler(FocusNode node, Intent intent){
......@@ -243,7 +243,7 @@ class _SwitchState extends State<Switch> with TickerProviderStateMixin {
}
void _updateHighlightMode(FocusHighlightMode mode) {
switch (WidgetsBinding.instance.focusManager.highlightMode) {
switch (FocusManager.instance.highlightMode) {
case FocusHighlightMode.touch:
_showHighlight = false;
break;
......
......@@ -4,7 +4,6 @@
import 'package:flutter/foundation.dart';
import 'binding.dart';
import 'focus_manager.dart';
import 'framework.dart';
......@@ -153,7 +152,7 @@ class ActionDispatcher extends Diagnosticable {
bool invokeAction(Action action, Intent intent, {FocusNode focusNode}) {
assert(action != null);
assert(intent != null);
focusNode ??= WidgetsBinding.instance.focusManager.primaryFocus;
focusNode ??= primaryFocus;
if (action != null && intent.isEnabled(focusNode.context)) {
action.invoke(focusNode, intent);
return true;
......
......@@ -577,7 +577,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
bool get hasPrimaryFocus => _manager?.primaryFocus == this;
/// Returns the [FocusHighlightMode] that is currently in effect for this node.
FocusHighlightMode get highlightMode => WidgetsBinding.instance.focusManager.highlightMode;
FocusHighlightMode get highlightMode => FocusManager.instance.highlightMode;
/// Returns the nearest enclosing scope node above this node, including
/// this node, if it's a scope.
......@@ -1111,15 +1111,18 @@ enum FocusHighlightStrategy {
/// focus.
///
/// The [FocusManager] is held by the [WidgetsBinding] as
/// [WidgetsBinding.focusManager]. The [FocusManager] is rarely accessed
/// directly. Instead, to find the [FocusScopeNode] for a given [BuildContext],
/// use [FocusScope.of].
/// [WidgetsBinding.focusManager], and can be conveniently accessed using the
/// [focusManager] global accessor.
///
/// To find the [FocusScopeNode] for a given [BuildContext], use
/// [FocusScope.of].
///
/// The [FocusManager] knows nothing about [FocusNode]s other than the one that
/// is currently focused. If a [FocusScopeNode] is removed, then the
/// [FocusManager] will attempt to focus the next [FocusScopeNode] in the focus
/// tree that it maintains, but if the current focus in that [FocusScopeNode] is
/// null, it will stop there, and no [FocusNode] will have focus.
/// is currently focused (accessible via the [primaryFocus] global accessor). If
/// a [FocusScopeNode] is removed, then the [FocusManager] will attempt to focus
/// the next [FocusScopeNode] in the focus tree that it maintains, but if the
/// current focus in that [FocusScopeNode] is null, it will stop there, and no
/// [FocusNode] will have focus.
///
/// See also:
///
......@@ -1130,17 +1133,24 @@ enum FocusHighlightStrategy {
/// [BuildContext].
/// * [FocusScope.of], which provides the nearest ancestor [FocusScopeNode] for
/// a given [BuildContext].
/// * The [focusManager] and [primaryFocus] global accessors, for convenient
/// access from anywhere to the current focus manager state.
class FocusManager with DiagnosticableTreeMixin {
/// Creates an object that manages the focus tree.
///
/// This constructor is rarely called directly. To access the [FocusManager],
/// consider using [WidgetsBinding.focusManager] instead.
/// consider using the [focusManager] accessor instead (which gets it from the
/// [WidgetsBinding] singleton).
FocusManager() {
rootScope._manager = this;
RawKeyboard.instance.addListener(_handleRawKeyEvent);
GestureBinding.instance.pointerRouter.addGlobalRoute(_handlePointerEvent);
}
/// Provides convenient access to the current [FocusManager] singleton from
/// the [WidgetsBinding] instance.
static FocusManager get instance => WidgetsBinding.instance.focusManager;
bool _lastInteractionWasTouch = true;
/// Sets the strategy by which [highlightMode] is determined.
......@@ -1418,6 +1428,10 @@ class FocusManager with DiagnosticableTreeMixin {
}
}
/// Provides convenient access to the current [FocusManager.primaryFocus] from the
/// [WidgetsBinding] instance.
FocusNode get primaryFocus => WidgetsBinding.instance.focusManager.primaryFocus;
/// Returns a text representation of the current focus tree, along with the
/// current attributes on each node.
///
......@@ -1426,7 +1440,7 @@ String debugDescribeFocusTree() {
assert(WidgetsBinding.instance != null);
String result;
assert(() {
result = WidgetsBinding.instance.focusManager.toStringDeep();
result = FocusManager.instance.toStringDeep();
return true;
}());
return result ?? '';
......
......@@ -9,7 +9,6 @@ import 'package:flutter/painting.dart';
import 'actions.dart';
import 'basic.dart';
import 'binding.dart';
import 'editable_text.dart';
import 'focus_manager.dart';
import 'framework.dart';
......@@ -623,7 +622,7 @@ class ReadingOrderTraversalPolicy extends FocusTraversalPolicy with DirectionalF
// If we still didn't find any candidate, use the current node as a
// fallback.
candidate ??= currentNode;
candidate ??= WidgetsBinding.instance.focusManager.rootScope;
candidate ??= FocusManager.instance.rootScope;
return candidate;
}
......@@ -807,7 +806,7 @@ class _RequestFocusActionBase extends Action {
@override
void invoke(FocusNode node, Intent intent) {
_previousFocus = WidgetsBinding.instance.focusManager.primaryFocus;
_previousFocus = primaryFocus;
node.requestFocus();
}
......
......@@ -9,7 +9,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'actions.dart';
import 'binding.dart';
import 'focus_manager.dart';
import 'focus_scope.dart';
import 'framework.dart';
......@@ -209,7 +208,7 @@ class ShortcutManager extends ChangeNotifier with DiagnosticableMixin {
matchedIntent = _shortcuts[LogicalKeySet.fromSet(pseudoKeys)];
}
if (matchedIntent != null) {
final BuildContext primaryContext = WidgetsBinding.instance.focusManager.primaryFocus?.context;
final BuildContext primaryContext = primaryFocus?.context;
if (primaryContext == null) {
return false;
}
......
......@@ -315,7 +315,7 @@ void main() {
),
);
WidgetsBinding.instance.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
focusNode.requestFocus();
await tester.pumpAndSettle();
......
......@@ -121,7 +121,7 @@ void main() {
});
testWidgets('ink response changes color on focus', (WidgetTester tester) async {
WidgetsBinding.instance.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
final FocusNode focusNode = FocusNode(debugLabel: 'Ink Focus');
await tester.pumpWidget(
Material(
......@@ -156,7 +156,7 @@ void main() {
});
testWidgets("ink response doesn't change color on focus when on touch device", (WidgetTester tester) async {
WidgetsBinding.instance.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTouch;
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTouch;
final FocusNode focusNode = FocusNode(debugLabel: 'Ink Focus');
await tester.pumpWidget(Material(
child: Directionality(
......
......@@ -133,7 +133,7 @@ void main() {
),
);
WidgetsBinding.instance.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
focusNode.requestFocus();
await tester.pumpAndSettle();
......@@ -174,7 +174,7 @@ void main() {
),
);
await tester.pumpAndSettle();
WidgetsBinding.instance.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
// Base elevation
Material material = tester.widget<Material>(rawButtonMaterial);
......
......@@ -311,7 +311,7 @@ void main() {
const Key key = Key('test');
const Color focusColor = Color(0xff00ff00);
WidgetsBinding.instance.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
await tester.pumpWidget(
MaterialApp(
home: Center(
......@@ -410,7 +410,7 @@ void main() {
const Key key = Key('test');
const Color hoverColor = Color(0xff00ff00);
WidgetsBinding.instance.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
await tester.pumpWidget(
MaterialApp(
home: Center(
......
......@@ -585,39 +585,38 @@ void main() {
callCount++;
}
final FocusManager focusManager = WidgetsBinding.instance.focusManager;
focusManager.addHighlightModeListener(handleModeChange);
addTearDown(() => focusManager.removeHighlightModeListener(handleModeChange));
FocusManager.instance.addHighlightModeListener(handleModeChange);
addTearDown(() => FocusManager.instance.removeHighlightModeListener(handleModeChange));
expect(callCount, equals(0));
expect(lastMode, isNull);
focusManager.highlightStrategy = FocusHighlightStrategy.automatic;
expect(focusManager.highlightMode, equals(FocusHighlightMode.touch));
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.automatic;
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.touch));
await tester.sendKeyEvent(LogicalKeyboardKey.metaLeft, platform: 'fuchsia');
expect(callCount, equals(1));
expect(lastMode, FocusHighlightMode.traditional);
expect(focusManager.highlightMode, equals(FocusHighlightMode.traditional));
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.traditional));
await tester.tap(find.byType(Container));
expect(callCount, equals(2));
expect(lastMode, FocusHighlightMode.touch);
expect(focusManager.highlightMode, equals(FocusHighlightMode.touch));
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.touch));
final TestGesture gesture = await tester.startGesture(Offset.zero, kind: PointerDeviceKind.mouse);
addTearDown(gesture.removePointer);
await gesture.up();
expect(callCount, equals(3));
expect(lastMode, FocusHighlightMode.traditional);
expect(focusManager.highlightMode, equals(FocusHighlightMode.traditional));
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.traditional));
await tester.tap(find.byType(Container));
expect(callCount, equals(4));
expect(lastMode, FocusHighlightMode.touch);
expect(focusManager.highlightMode, equals(FocusHighlightMode.touch));
focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.touch));
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
expect(callCount, equals(5));
expect(lastMode, FocusHighlightMode.traditional);
expect(focusManager.highlightMode, equals(FocusHighlightMode.traditional));
focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTouch;
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.traditional));
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTouch;
expect(callCount, equals(6));
expect(lastMode, FocusHighlightMode.touch);
expect(focusManager.highlightMode, equals(FocusHighlightMode.touch));
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.touch));
});
testWidgets('implements debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
......
......@@ -237,9 +237,9 @@ void main() {
' PRIMARY FOCUS\n'),
);
expect(WidgetsBinding.instance.focusManager.rootScope, hasAGoodToStringDeep);
expect(FocusManager.instance.rootScope, hasAGoodToStringDeep);
expect(
WidgetsBinding.instance.focusManager.rootScope.toStringDeep(minLevel: DiagnosticLevel.info),
FocusManager.instance.rootScope.toStringDeep(minLevel: DiagnosticLevel.info),
equalsIgnoringHashCodes('FocusScopeNode#00000(Root Focus Scope)\n'
' │ IN FOCUS PATH\n'
' │ focusedChildren: FocusScopeNode#00000(Parent Scope Node)\n'
......@@ -500,7 +500,7 @@ void main() {
FocusScope.of(keyA.currentContext).requestFocus(keyA.currentState.focusNode);
expect(FocusScope.of(keyA.currentContext), equals(childFocusScope));
expect(Focus.of(keyA.currentContext, scopeOk: true), equals(childFocusScope));
WidgetsBinding.instance.focusManager.rootScope.setFirstFocus(FocusScope.of(keyA.currentContext));
FocusManager.instance.rootScope.setFirstFocus(FocusScope.of(keyA.currentContext));
await tester.pumpAndSettle();
......@@ -558,7 +558,7 @@ void main() {
FocusScope.of(keyA.currentContext).requestFocus(keyA.currentState.focusNode);
final FocusScopeNode scope = FocusScope.of(keyA.currentContext);
WidgetsBinding.instance.focusManager.rootScope.setFirstFocus(scope);
FocusManager.instance.rootScope.setFirstFocus(scope);
await tester.pumpAndSettle();
......@@ -634,8 +634,8 @@ void main() {
FocusScope.of(keyA.currentContext).requestFocus(keyA.currentState.focusNode);
final FocusScopeNode aScope = FocusScope.of(keyA.currentContext);
final FocusScopeNode bScope = FocusScope.of(keyB.currentContext);
WidgetsBinding.instance.focusManager.rootScope.setFirstFocus(bScope);
WidgetsBinding.instance.focusManager.rootScope.setFirstFocus(aScope);
FocusManager.instance.rootScope.setFirstFocus(bScope);
FocusManager.instance.rootScope.setFirstFocus(aScope);
await tester.pumpAndSettle();
......@@ -647,7 +647,7 @@ void main() {
await tester.pumpWidget(Container());
expect(WidgetsBinding.instance.focusManager.rootScope.children, isEmpty);
expect(FocusManager.instance.rootScope.children, isEmpty);
});
// By "pinned", it means kept in the tree by a GlobalKey.
......@@ -698,8 +698,8 @@ void main() {
FocusScope.of(keyA.currentContext).requestFocus(keyA.currentState.focusNode);
final FocusScopeNode bScope = FocusScope.of(keyB.currentContext);
final FocusScopeNode aScope = FocusScope.of(keyA.currentContext);
WidgetsBinding.instance.focusManager.rootScope.setFirstFocus(bScope);
WidgetsBinding.instance.focusManager.rootScope.setFirstFocus(aScope);
FocusManager.instance.rootScope.setFirstFocus(bScope);
FocusManager.instance.rootScope.setFirstFocus(aScope);
await tester.pumpAndSettle();
......@@ -781,8 +781,8 @@ void main() {
FocusScope.of(keyA.currentContext).requestFocus(keyA.currentState.focusNode);
final FocusScopeNode bScope = FocusScope.of(keyB.currentContext);
final FocusScopeNode aScope = FocusScope.of(keyA.currentContext);
WidgetsBinding.instance.focusManager.rootScope.setFirstFocus(bScope);
WidgetsBinding.instance.focusManager.rootScope.setFirstFocus(aScope);
FocusManager.instance.rootScope.setFirstFocus(bScope);
FocusManager.instance.rootScope.setFirstFocus(aScope);
await tester.pumpAndSettle();
......@@ -856,7 +856,7 @@ void main() {
FocusScope.of(keyA.currentContext).requestFocus(keyA.currentState.focusNode);
final FocusScopeNode aScope = FocusScope.of(keyA.currentContext);
WidgetsBinding.instance.focusManager.rootScope.setFirstFocus(aScope);
FocusManager.instance.rootScope.setFirstFocus(aScope);
await tester.pumpAndSettle();
......@@ -941,7 +941,7 @@ void main() {
FocusScope.of(keyA.currentContext).requestFocus(keyA.currentState.focusNode);
final FocusScopeNode aScope = FocusScope.of(keyA.currentContext);
WidgetsBinding.instance.focusManager.rootScope.setFirstFocus(aScope);
FocusManager.instance.rootScope.setFirstFocus(aScope);
await tester.pumpAndSettle();
......@@ -1289,7 +1289,7 @@ void main() {
await tester.pumpWidget(Container());
expect(WidgetsBinding.instance.focusManager.rootScope.descendants, isEmpty);
expect(FocusManager.instance.rootScope.descendants, isEmpty);
});
testWidgets('Focus widgets set Semantics information about focus', (WidgetTester tester) async {
final GlobalKey<TestFocusState> key = GlobalKey();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment