mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
[FR] Shortcut for toggling checkbox (#1817)
* feat: shortcut for toggling checkbox * refactor: separate checkbox event handler * test: chechbox event handler * chore: remove unused imports * refactor: command to ctrl and enter * refactor: handler to use transactions * test: checkbox event handler * chore: remove unused import * refactor: simplify handler logic
This commit is contained in:
parent
84200ddda4
commit
95ec607482
@ -0,0 +1,38 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
ShortcutEventHandler toggleCheckbox = (editorState, event) {
|
||||
final selection = editorState.service.selectionService.currentSelection.value;
|
||||
final nodes = editorState.service.selectionService.currentSelectedNodes;
|
||||
final checkboxTextNodes = nodes
|
||||
.where(
|
||||
(element) =>
|
||||
element is TextNode &&
|
||||
element.subtype == BuiltInAttributeKey.checkbox,
|
||||
)
|
||||
.toList(growable: false);
|
||||
|
||||
if (selection == null || checkboxTextNodes.isEmpty) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
bool isAllCheckboxesChecked = checkboxTextNodes
|
||||
.every((node) => node.attributes[BuiltInAttributeKey.checkbox] == true);
|
||||
final transaction = editorState.transaction;
|
||||
transaction.afterSelection = selection;
|
||||
|
||||
if (isAllCheckboxesChecked) {
|
||||
//if all the checkboxes are checked, then make all of the checkboxes unchecked
|
||||
for (final node in checkboxTextNodes) {
|
||||
transaction.updateNode(node, {BuiltInAttributeKey.checkbox: false});
|
||||
}
|
||||
} else {
|
||||
//If any one of the checkboxes is unchecked then make all checkboxes checked
|
||||
for (final node in checkboxTextNodes) {
|
||||
transaction.updateNode(node, {BuiltInAttributeKey.checkbox: true});
|
||||
}
|
||||
}
|
||||
|
||||
editorState.apply(transaction);
|
||||
return KeyEventResult.handled;
|
||||
};
|
@ -14,6 +14,7 @@ import 'package:appflowy_editor/src/service/internal_key_event_handlers/format_s
|
||||
import 'package:appflowy_editor/src/service/internal_key_event_handlers/space_on_web_handler.dart';
|
||||
import 'package:appflowy_editor/src/service/internal_key_event_handlers/tab_handler.dart';
|
||||
import 'package:appflowy_editor/src/service/internal_key_event_handlers/whitespace_handler.dart';
|
||||
import 'package:appflowy_editor/src/service/internal_key_event_handlers/checkbox_event_handler.dart';
|
||||
import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
@ -159,6 +160,13 @@ List<ShortcutEvent> builtInShortcutEvents = [
|
||||
linuxCommand: 'ctrl+u',
|
||||
handler: formatUnderlineEventHandler,
|
||||
),
|
||||
ShortcutEvent(
|
||||
key: 'Toggle Checkbox',
|
||||
command: 'meta+enter',
|
||||
windowsCommand: 'ctrl+enter',
|
||||
linuxCommand: 'ctrl+enter',
|
||||
handler: toggleCheckbox,
|
||||
),
|
||||
ShortcutEvent(
|
||||
key: 'Format strikethrough',
|
||||
command: 'meta+shift+s',
|
||||
|
@ -136,6 +136,9 @@ extension on LogicalKeyboardKey {
|
||||
if (this == LogicalKeyboardKey.keyH) {
|
||||
return PhysicalKeyboardKey.keyH;
|
||||
}
|
||||
if (this == LogicalKeyboardKey.keyQ) {
|
||||
return PhysicalKeyboardKey.keyQ;
|
||||
}
|
||||
if (this == LogicalKeyboardKey.keyZ) {
|
||||
return PhysicalKeyboardKey.keyZ;
|
||||
}
|
||||
|
@ -0,0 +1,241 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor/src/service/shortcut_event/built_in_shortcut_events.dart';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import '../../infra/test_editor.dart';
|
||||
|
||||
void main() async {
|
||||
setUpAll(() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
});
|
||||
|
||||
group('checkbox_event_handler_test.dart', () {
|
||||
testWidgets('toggle checkbox with shortcut ctrl+enter', (tester) async {
|
||||
const text = 'Checkbox1';
|
||||
final editor = tester.editor
|
||||
..insertTextNode(
|
||||
'',
|
||||
attributes: {
|
||||
BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox,
|
||||
BuiltInAttributeKey.checkbox: false,
|
||||
},
|
||||
delta: Delta(
|
||||
operations: [TextInsert(text)],
|
||||
),
|
||||
);
|
||||
await editor.startTesting();
|
||||
await editor.updateSelection(
|
||||
Selection.single(path: [0], startOffset: text.length),
|
||||
);
|
||||
|
||||
final checkboxNode = editor.nodeAtPath([0]) as TextNode;
|
||||
expect(checkboxNode.subtype, BuiltInAttributeKey.checkbox);
|
||||
expect(checkboxNode.attributes[BuiltInAttributeKey.checkbox], false);
|
||||
|
||||
for (final event in builtInShortcutEvents) {
|
||||
if (event.key == 'Toggle Checkbox') {
|
||||
event.updateCommand(
|
||||
windowsCommand: 'ctrl+enter',
|
||||
linuxCommand: 'ctrl+enter',
|
||||
macOSCommand: 'meta+enter',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (Platform.isWindows || Platform.isLinux) {
|
||||
await editor.pressLogicKey(
|
||||
LogicalKeyboardKey.enter,
|
||||
isControlPressed: true,
|
||||
);
|
||||
} else {
|
||||
await editor.pressLogicKey(
|
||||
LogicalKeyboardKey.enter,
|
||||
isMetaPressed: true,
|
||||
);
|
||||
}
|
||||
|
||||
expect(checkboxNode.attributes[BuiltInAttributeKey.checkbox], true);
|
||||
|
||||
await editor.updateSelection(
|
||||
Selection.single(path: [0], startOffset: text.length),
|
||||
);
|
||||
|
||||
if (Platform.isWindows || Platform.isLinux) {
|
||||
await editor.pressLogicKey(
|
||||
LogicalKeyboardKey.enter,
|
||||
isControlPressed: true,
|
||||
);
|
||||
} else {
|
||||
await editor.pressLogicKey(
|
||||
LogicalKeyboardKey.enter,
|
||||
isMetaPressed: true,
|
||||
);
|
||||
}
|
||||
|
||||
expect(checkboxNode.attributes[BuiltInAttributeKey.checkbox], false);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'test if all checkboxes get unchecked after toggling them, if all of them were already checked',
|
||||
(tester) async {
|
||||
const text = 'Checkbox';
|
||||
final editor = tester.editor
|
||||
..insertTextNode(
|
||||
'',
|
||||
attributes: {
|
||||
BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox,
|
||||
BuiltInAttributeKey.checkbox: true,
|
||||
},
|
||||
delta: Delta(
|
||||
operations: [TextInsert(text)],
|
||||
),
|
||||
)
|
||||
..insertTextNode(
|
||||
'',
|
||||
attributes: {
|
||||
BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox,
|
||||
BuiltInAttributeKey.checkbox: true,
|
||||
},
|
||||
delta: Delta(
|
||||
operations: [TextInsert(text)],
|
||||
),
|
||||
)
|
||||
..insertTextNode(
|
||||
'',
|
||||
attributes: {
|
||||
BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox,
|
||||
BuiltInAttributeKey.checkbox: true,
|
||||
},
|
||||
delta: Delta(
|
||||
operations: [TextInsert(text)],
|
||||
),
|
||||
);
|
||||
|
||||
await editor.startTesting();
|
||||
await editor.updateSelection(
|
||||
Selection.single(path: [0], startOffset: text.length),
|
||||
);
|
||||
|
||||
final nodes =
|
||||
editor.editorState.service.selectionService.currentSelectedNodes;
|
||||
final checkboxTextNodes = nodes
|
||||
.where(
|
||||
(element) =>
|
||||
element is TextNode &&
|
||||
element.subtype == BuiltInAttributeKey.checkbox,
|
||||
)
|
||||
.toList(growable: false);
|
||||
|
||||
for (final node in checkboxTextNodes) {
|
||||
expect(node.attributes[BuiltInAttributeKey.checkbox], true);
|
||||
}
|
||||
|
||||
for (final event in builtInShortcutEvents) {
|
||||
if (event.key == 'Toggle Checkbox') {
|
||||
event.updateCommand(
|
||||
windowsCommand: 'ctrl+enter',
|
||||
linuxCommand: 'ctrl+enter',
|
||||
macOSCommand: 'meta+enter',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (Platform.isWindows || Platform.isLinux) {
|
||||
await editor.pressLogicKey(
|
||||
LogicalKeyboardKey.enter,
|
||||
isControlPressed: true,
|
||||
);
|
||||
} else {
|
||||
await editor.pressLogicKey(
|
||||
LogicalKeyboardKey.enter,
|
||||
isMetaPressed: true,
|
||||
);
|
||||
}
|
||||
|
||||
for (final node in checkboxTextNodes) {
|
||||
expect(node.attributes[BuiltInAttributeKey.checkbox], false);
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'test if all checkboxes get checked after toggling them, if any one of them were already checked',
|
||||
(tester) async {
|
||||
const text = 'Checkbox';
|
||||
final editor = tester.editor
|
||||
..insertTextNode(
|
||||
'',
|
||||
attributes: {
|
||||
BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox,
|
||||
BuiltInAttributeKey.checkbox: false,
|
||||
},
|
||||
delta: Delta(
|
||||
operations: [TextInsert(text)],
|
||||
),
|
||||
)
|
||||
..insertTextNode(
|
||||
'',
|
||||
attributes: {
|
||||
BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox,
|
||||
BuiltInAttributeKey.checkbox: true,
|
||||
},
|
||||
delta: Delta(
|
||||
operations: [TextInsert(text)],
|
||||
),
|
||||
)
|
||||
..insertTextNode(
|
||||
'',
|
||||
attributes: {
|
||||
BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox,
|
||||
BuiltInAttributeKey.checkbox: false,
|
||||
},
|
||||
delta: Delta(
|
||||
operations: [TextInsert(text)],
|
||||
),
|
||||
);
|
||||
|
||||
await editor.startTesting();
|
||||
await editor.updateSelection(
|
||||
Selection.single(path: [0], startOffset: text.length),
|
||||
);
|
||||
|
||||
final nodes =
|
||||
editor.editorState.service.selectionService.currentSelectedNodes;
|
||||
final checkboxTextNodes = nodes
|
||||
.where(
|
||||
(element) =>
|
||||
element is TextNode &&
|
||||
element.subtype == BuiltInAttributeKey.checkbox,
|
||||
)
|
||||
.toList(growable: false);
|
||||
|
||||
for (final event in builtInShortcutEvents) {
|
||||
if (event.key == 'Toggle Checkbox') {
|
||||
event.updateCommand(
|
||||
windowsCommand: 'ctrl+enter',
|
||||
linuxCommand: 'ctrl+enter',
|
||||
macOSCommand: 'meta+enter',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (Platform.isWindows || Platform.isLinux) {
|
||||
await editor.pressLogicKey(
|
||||
LogicalKeyboardKey.enter,
|
||||
isControlPressed: true,
|
||||
);
|
||||
} else {
|
||||
await editor.pressLogicKey(
|
||||
LogicalKeyboardKey.enter,
|
||||
isMetaPressed: true,
|
||||
);
|
||||
}
|
||||
|
||||
for (final node in checkboxTextNodes) {
|
||||
expect(node.attributes[BuiltInAttributeKey.checkbox], true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user