Add typing shortcuts (#994)

Start lists with "1." or "-".
Indent / Dedent lists with <Tab> and <Shift+Tab>.
pull/1006/head
Benjamin Quinn 2 years ago committed by GitHub
parent d15174e4b6
commit 6c9698e377
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      lib/src/utils/cast.dart
  2. 20
      lib/src/widgets/controller.dart
  3. 143
      lib/src/widgets/raw_editor.dart
  4. 22
      lib/src/widgets/toolbar/indent_button.dart

@ -0,0 +1 @@
T? castOrNull<T>(dynamic x) => x is T ? x : null;

@ -102,6 +102,26 @@ class QuillController extends ChangeNotifier {
.mergeAll(toggledStyle);
}
// Increases or decreases the indent of the current selection by 1.
void indentSelection(bool isIncrease) {
final indent = getSelectionStyle().attributes[Attribute.indent.key];
if (indent == null) {
if (isIncrease) {
formatSelection(Attribute.indentL1);
}
return;
}
if (indent.value == 1 && !isIncrease) {
formatSelection(Attribute.clone(Attribute.indentL1, null));
return;
}
if (isIncrease) {
formatSelection(Attribute.getIndentLevel(indent.value + 1));
return;
}
formatSelection(Attribute.getIndentLevel(indent.value - 1));
}
/// Returns all styles for each node within selection
List<Tuple2<int, Style>> getAllIndividualSelectionStyles() {
final styles = document.collectAllIndividualStyles(

@ -19,7 +19,9 @@ import '../models/documents/nodes/block.dart';
import '../models/documents/nodes/embeddable.dart';
import '../models/documents/nodes/line.dart';
import '../models/documents/nodes/node.dart';
import '../models/documents/nodes/leaf.dart' as leaf;
import '../models/documents/style.dart';
import '../utils/cast.dart';
import '../utils/delta.dart';
import '../utils/embeds.dart';
import '../utils/platform.dart';
@ -428,6 +430,7 @@ class RawEditorState extends EditorState
actions: _actions,
child: Focus(
focusNode: widget.focusNode,
onKey: _onKey,
child: QuillKeyboardListener(
child: Container(
constraints: constraints,
@ -440,6 +443,125 @@ class RawEditorState extends EditorState
);
}
KeyEventResult _onKey(node, RawKeyEvent event) {
// Don't handle key if there is a meta key pressed.
if (event.isAltPressed || event.isControlPressed || event.isMetaPressed) {
return KeyEventResult.ignored;
}
if (event is! RawKeyDownEvent) {
return KeyEventResult.ignored;
}
// Don't handle key if there is an active selection.
if (controller.selection.baseOffset != controller.selection.extentOffset) {
return KeyEventResult.ignored;
}
// Handle indenting blocks when pressing the tab key.
if (event.logicalKey == LogicalKeyboardKey.tab) {
return _handleTabKey(event);
}
// Handle inserting lists when space is pressed following
// a list initiating phrase.
if (event.logicalKey == LogicalKeyboardKey.space) {
return _handleSpaceKey(event);
}
return KeyEventResult.ignored;
}
KeyEventResult _handleSpaceKey(RawKeyEvent event) {
final child =
controller.document.queryChild(controller.selection.baseOffset);
if (child.node == null) {
return KeyEventResult.ignored;
}
final line = child.node as Line?;
if (line == null) {
return KeyEventResult.ignored;
}
final text = castOrNull<leaf.Text>(line.first);
if (text == null) {
return KeyEventResult.ignored;
}
const olKeyPhrase = '1.';
const ulKeyPhrase = '-';
if (text.value == olKeyPhrase) {
_updateSelectionForKeyPhrase(olKeyPhrase, Attribute.ol);
} else if (text.value == ulKeyPhrase) {
_updateSelectionForKeyPhrase(ulKeyPhrase, Attribute.ul);
} else {
return KeyEventResult.ignored;
}
return KeyEventResult.handled;
}
KeyEventResult _handleTabKey(RawKeyEvent event) {
final child =
controller.document.queryChild(controller.selection.baseOffset);
KeyEventResult insertTabCharacter() {
controller.replaceText(controller.selection.baseOffset, 0, '\t', null);
_moveCursor(1);
return KeyEventResult.handled;
}
if (child.node == null) {
return insertTabCharacter();
}
final node = child.node!;
final parent = node.parent;
if (parent == null || parent is! Block) {
return insertTabCharacter();
}
if (node is! Line || (node.isNotEmpty && node.first is! leaf.Text)) {
return insertTabCharacter();
}
if (node.isNotEmpty && (node.first as leaf.Text).value.isNotEmpty) {
return insertTabCharacter();
}
final parentBlock = parent;
if (parentBlock.style.containsKey(Attribute.ol.key) ||
parentBlock.style.containsKey(Attribute.ul.key) ||
parentBlock.style.containsKey(Attribute.checked.key)) {
controller.indentSelection(!event.isShiftPressed);
return KeyEventResult.handled;
}
return insertTabCharacter();
}
void _moveCursor(int chars) {
final selection = controller.selection;
controller.updateSelection(
controller.selection.copyWith(
baseOffset: selection.baseOffset + chars,
extentOffset: selection.baseOffset + chars),
ChangeSource.LOCAL);
}
void _updateSelectionForKeyPhrase(String phrase, Attribute attribute) {
controller
..formatSelection(attribute)
..replaceText(controller.selection.baseOffset - phrase.length,
phrase.length, '', null);
// It is unclear why the selection moves forward the edit distance.
_moveCursor(-2);
}
void _handleSelectionChanged(
TextSelection selection, SelectionChangedCause cause) {
final oldSelection = controller.selection;
@ -2076,26 +2198,7 @@ class _IndentSelectionAction extends Action<IndentSelectionIntent> {
@override
void invoke(IndentSelectionIntent intent, [BuildContext? context]) {
final indent =
state.controller.getSelectionStyle().attributes[Attribute.indent.key];
if (indent == null) {
if (intent.isIncrease) {
state.controller.formatSelection(Attribute.indentL1);
}
return;
}
if (indent.value == 1 && !intent.isIncrease) {
state.controller
.formatSelection(Attribute.clone(Attribute.indentL1, null));
return;
}
if (intent.isIncrease) {
state.controller
.formatSelection(Attribute.getIndentLevel(indent.value + 1));
return;
}
state.controller
.formatSelection(Attribute.getIndentLevel(indent.value - 1));
state.controller.indentSelection(intent.isIncrease);
}
@override

@ -42,27 +42,7 @@ class _IndentButtonState extends State<IndentButton> {
fillColor: iconFillColor,
borderRadius: widget.iconTheme?.borderRadius ?? 2,
onPressed: () {
final indent = widget.controller
.getSelectionStyle()
.attributes[Attribute.indent.key];
if (indent == null) {
if (widget.isIncrease) {
widget.controller.formatSelection(Attribute.indentL1);
}
return;
}
if (indent.value == 1 && !widget.isIncrease) {
widget.controller
.formatSelection(Attribute.clone(Attribute.indentL1, null));
return;
}
if (widget.isIncrease) {
widget.controller
.formatSelection(Attribute.getIndentLevel(indent.value + 1));
return;
}
widget.controller
.formatSelection(Attribute.getIndentLevel(indent.value - 1));
widget.controller.indentSelection(widget.isIncrease);
},
afterPressed: widget.afterButtonPressed,
);

Loading…
Cancel
Save