Unverified Commit 1df6e2af authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Simplify the Shortcuts widget diagnostic output (#48265)

This simplifies the diagnostic output for the Shortcuts widget so that if a debugLabel is supplied, then that is printed instead of the full list of shortcut keys in the map. Also, the output of the shortcut map is simplified to have the list of keys presented in more readable text.
parent 51e24a35
......@@ -1364,6 +1364,7 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
assert(_debugCheckLocalizations(appLocale));
return Shortcuts(
shortcuts: widget.shortcuts ?? WidgetsApp.defaultShortcuts,
debugLabel: '<Default WidgetsApp Shortcuts>',
child: Actions(
actions: widget.actions ?? WidgetsApp.defaultActions,
child: DefaultFocusTraversal(
......
......@@ -27,7 +27,7 @@ import 'inherited_notifier.dart';
///
/// * [ShortcutManager], which uses [LogicalKeySet] (a [KeySet] subclass) to
/// define its key map.
class KeySet<T extends KeyboardKey> extends Diagnosticable {
class KeySet<T extends KeyboardKey> {
/// A constructor for making a [KeySet] of up to four keys.
///
/// If you need a set of more than four keys, use [KeySet.fromSet].
......@@ -97,12 +97,6 @@ class KeySet<T extends KeyboardKey> extends Diagnosticable {
int get hashCode {
return hashList(_keys);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<Set<T>>('keys', _keys));
}
}
/// A set of [LogicalKeyboardKey]s that can be used as the keys in a map.
......@@ -116,7 +110,7 @@ class KeySet<T extends KeyboardKey> extends Diagnosticable {
/// This is a thin wrapper around a [Set], but changes the equality comparison
/// from an identity comparison to a contents comparison so that non-identical
/// sets with the same keys in them will compare as equal.
class LogicalKeySet extends KeySet<LogicalKeyboardKey> {
class LogicalKeySet extends KeySet<LogicalKeyboardKey> with DiagnosticableMixin {
/// A constructor for making a [LogicalKeySet] of up to four keys.
///
/// If you need a set of more than four keys, use [LogicalKeySet.fromSet].
......@@ -136,6 +130,71 @@ class LogicalKeySet extends KeySet<LogicalKeyboardKey> {
///
/// The `keys` must not be null.
LogicalKeySet.fromSet(Set<LogicalKeyboardKey> keys) : super.fromSet(keys);
static final Set<LogicalKeyboardKey> _modifiers = <LogicalKeyboardKey>{
LogicalKeyboardKey.alt,
LogicalKeyboardKey.control,
LogicalKeyboardKey.meta,
LogicalKeyboardKey.shift,
};
/// Returns a description of the key set that is short and readable.
///
/// Intended to be used in debug mode for logging purposes.
String debugDescribeKeys() {
final List<LogicalKeyboardKey> sortedKeys = keys.toList()..sort(
(LogicalKeyboardKey a, LogicalKeyboardKey b) {
// Put the modifiers first. If it has a synonym, then it's something
// like shiftLeft, altRight, etc.
final bool aIsModifier = a.synonyms.isNotEmpty || _modifiers.contains(a);
final bool bIsModifier = b.synonyms.isNotEmpty || _modifiers.contains(b);
if (aIsModifier && !bIsModifier) {
return -1;
} else if (bIsModifier && !aIsModifier) {
return 1;
}
return a.debugName.compareTo(b.debugName);
}
);
return sortedKeys.map<String>((LogicalKeyboardKey key) => '${key.debugName}').join(' + ');
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<Set<LogicalKeyboardKey>>('keys', _keys, description: debugDescribeKeys()));
}
}
/// Diagnostics property which handles formatting a `Map<LogicalKeySet, Intent>`
/// (the same type as the [Shortcuts.shortcuts] property) so that it is human-readable.
class ShortcutMapProperty extends DiagnosticsProperty<Map<LogicalKeySet, Intent>> {
/// Create a diagnostics property for `Map<LogicalKeySet, Intent>` objects,
/// which are the same type as the [Shortcuts.shortcuts] property.
///
/// The [showName] and [level] arguments must not be null.
ShortcutMapProperty(
String name,
Map<LogicalKeySet, Intent> value, {
bool showName = true,
Object defaultValue = kNoDefaultValue,
DiagnosticLevel level = DiagnosticLevel.info,
String description,
}) : assert(showName != null),
assert(level != null),
super(
name,
value,
showName: showName,
defaultValue: defaultValue,
level: level,
description: description,
);
@override
String valueToString({ TextTreeConfiguration parentConfiguration }) {
return '{${value.keys.map<String>((LogicalKeySet keySet) => '{${keySet.debugDescribeKeys()}}: ${value[keySet]}').join(', ')}}';
}
}
/// A manager of keyboard shortcut bindings.
......@@ -243,6 +302,7 @@ class Shortcuts extends StatefulWidget {
this.manager,
this.shortcuts,
this.child,
this.debugLabel,
}) : super(key: key);
/// The [ShortcutManager] that will manage the mapping between key
......@@ -268,6 +328,15 @@ class Shortcuts extends StatefulWidget {
/// {@macro flutter.widgets.child}
final Widget child;
/// The debug label that is printed for this node when logged.
///
/// If this label is set, then it will be displayed instead of the shortcut
/// map when logged.
///
/// This allows simplifying the diagnostic output to avoid cluttering it
/// unnecessarily with the default shortcut map.
final String debugLabel;
/// Returns the [ActionDispatcher] that most tightly encloses the given
/// [BuildContext].
///
......@@ -299,8 +368,8 @@ class Shortcuts extends StatefulWidget {
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<ShortcutManager>('manager', manager));
properties.add(DiagnosticsProperty<Map<LogicalKeySet, Intent>>('shortcuts', shortcuts));
properties.add(DiagnosticsProperty<ShortcutManager>('manager', manager, defaultValue: null));
properties.add(ShortcutMapProperty('shortcuts', shortcuts, description: debugLabel?.isNotEmpty ?? false ? debugLabel : null));
}
}
......
......@@ -51,7 +51,7 @@ class TestShortcutManager extends ShortcutManager {
void main() {
group(LogicalKeySet, () {
test('$LogicalKeySet passes parameters correctly.', () {
test('LogicalKeySet passes parameters correctly.', () {
final LogicalKeySet set1 = LogicalKeySet(LogicalKeyboardKey.keyA);
final LogicalKeySet set2 = LogicalKeySet(
LogicalKeyboardKey.keyA,
......@@ -109,7 +109,7 @@ void main() {
LogicalKeyboardKey.keyD,
}));
});
test('$LogicalKeySet works as a map key.', () {
test('LogicalKeySet works as a map key.', () {
final LogicalKeySet set1 = LogicalKeySet(LogicalKeyboardKey.keyA);
final LogicalKeySet set2 = LogicalKeySet(
LogicalKeyboardKey.keyA,
......@@ -146,7 +146,7 @@ void main() {
})),
);
});
test('$KeySet diagnostics work.', () {
test('LogicalKeySet diagnostics work.', () {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
LogicalKeySet(
......@@ -162,14 +162,11 @@ void main() {
.toList();
expect(description.length, equals(1));
expect(
description[0],
equalsIgnoringHashCodes(
'keys: {LogicalKeyboardKey#00000(keyId: "0x00000061", keyLabel: "a", debugName: "Key A"), LogicalKeyboardKey#00000(keyId: "0x00000062", keyLabel: "b", debugName: "Key B")}'));
expect(description[0], equals('keys: Key A + Key B'));
});
});
group(Shortcuts, () {
testWidgets('$ShortcutManager handles shortcuts', (WidgetTester tester) async {
testWidgets('ShortcutManager handles shortcuts', (WidgetTester tester) async {
final GlobalKey containerKey = GlobalKey();
final List<LogicalKeyboardKey> pressedKeys = <LogicalKeyboardKey>[];
final TestShortcutManager testManager = TestShortcutManager(pressedKeys);
......@@ -202,7 +199,7 @@ void main() {
expect(invoked, isTrue);
expect(pressedKeys, equals(<LogicalKeyboardKey>[LogicalKeyboardKey.shiftLeft]));
});
testWidgets("$Shortcuts passes to the next $Shortcuts widget if it doesn't map the key", (WidgetTester tester) async {
testWidgets("Shortcuts passes to the next Shortcuts widget if it doesn't map the key", (WidgetTester tester) async {
final GlobalKey containerKey = GlobalKey();
final List<LogicalKeyboardKey> pressedKeys = <LogicalKeyboardKey>[];
final TestShortcutManager testManager = TestShortcutManager(pressedKeys);
......@@ -240,7 +237,7 @@ void main() {
expect(invoked, isTrue);
expect(pressedKeys, equals(<LogicalKeyboardKey>[LogicalKeyboardKey.shiftLeft]));
});
testWidgets('$Shortcuts can disable a shortcut with Intent.doNothing', (WidgetTester tester) async {
testWidgets('Shortcuts can disable a shortcut with Intent.doNothing', (WidgetTester tester) async {
final GlobalKey containerKey = GlobalKey();
final List<LogicalKeyboardKey> pressedKeys = <LogicalKeyboardKey>[];
final TestShortcutManager testManager = TestShortcutManager(pressedKeys);
......@@ -280,5 +277,53 @@ void main() {
expect(invoked, isFalse);
expect(pressedKeys, isEmpty);
});
test('Shortcuts diagnostics work.', () {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
Shortcuts(shortcuts: <LogicalKeySet, Intent>{LogicalKeySet(
LogicalKeyboardKey.shift,
LogicalKeyboardKey.keyA,
) : const Intent(ActivateAction.key),
LogicalKeySet(
LogicalKeyboardKey.shift,
LogicalKeyboardKey.arrowRight,
) : const DirectionalFocusIntent(TraversalDirection.right)}).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(1));
expect(
description[0],
equalsIgnoringHashCodes(
'shortcuts: {{Shift + Key A}: Intent#00000(key: [<ActivateAction>]), {Shift + Arrow Right}: DirectionalFocusIntent#00000(key: [<DirectionalFocusAction>])}'));
});
test('Shortcuts diagnostics work when debugLabel specified.', () {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
Shortcuts(
debugLabel: '<Debug Label>',
shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(
LogicalKeyboardKey.keyA,
LogicalKeyboardKey.keyB,
): const Intent(ActivateAction.key)
},
).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(1));
expect(description[0], equals('shortcuts: <Debug Label>'));
});
});
}
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