diff --git a/lib/src/models/documents/document.dart b/lib/src/models/documents/document.dart index 1b4d5aef..597d9025 100644 --- a/lib/src/models/documents/document.dart +++ b/lib/src/models/documents/document.dart @@ -170,6 +170,12 @@ class Document { return (res.node as Line).collectAllStyles(res.offset, len); } + /// Returns all styles for any character within the specified text range. + List> collectAllStylesWithOffset(int index, int len) { + final res = queryChild(index); + return (res.node as Line).collectAllStylesWithOffsets(res.offset, len); + } + /// Returns plain text within the specified text range. String getPlainText(int index, int len) { final res = queryChild(index); diff --git a/lib/src/models/documents/nodes/line.dart b/lib/src/models/documents/nodes/line.dart index a8cca75c..e87451f1 100644 --- a/lib/src/models/documents/nodes/line.dart +++ b/lib/src/models/documents/nodes/line.dart @@ -458,6 +458,44 @@ class Line extends Container { return result; } + /// Returns all styles for any character within the specified text range. + List> collectAllStylesWithOffsets( + int offset, + int len, { + int beg = 0, + }) { + final local = math.min(length - offset, len); + final result = >[]; + + final data = queryChild(offset, true); + var node = data.node as Leaf?; + if (node != null) { + var pos = 0; + pos = node.length - data.offset; + result.add(OffsetValue(node.documentOffset, node.style, node.length)); + while (!node!.isLast && pos < local) { + node = node.next as Leaf; + result.add(OffsetValue(node.documentOffset, node.style, node.length)); + pos += node.length; + } + } + + result.add(OffsetValue(documentOffset, style, length)); + if (parent is Block) { + final block = parent as Block; + result.add(OffsetValue(block.documentOffset, block.style, block.length)); + } + + final remaining = len - local; + if (remaining > 0 && nextLine != null) { + final rest = + nextLine!.collectAllStylesWithOffsets(0, remaining, beg: local); + result.addAll(rest); + } + + return result; + } + /// Returns plain text within the specified text range. String getPlainText(int offset, int len) { final plainText = StringBuffer(); diff --git a/lib/src/models/structs/offset_value.dart b/lib/src/models/structs/offset_value.dart index 0f9e558b..58275458 100644 --- a/lib/src/models/structs/offset_value.dart +++ b/lib/src/models/structs/offset_value.dart @@ -1,5 +1,6 @@ class OffsetValue { - OffsetValue(this.offset, this.value); + OffsetValue(this.offset, this.value, [this.length]); final int offset; + final int? length; final T value; } diff --git a/lib/src/widgets/controller.dart b/lib/src/widgets/controller.dart index 30396377..529e393f 100644 --- a/lib/src/widgets/controller.dart +++ b/lib/src/widgets/controller.dart @@ -101,6 +101,14 @@ class QuillController extends ChangeNotifier { // Increases or decreases the indent of the current selection by 1. void indentSelection(bool isIncrease) { + if (selection.isCollapsed) { + _indentSelectionFormat(isIncrease); + } else { + _indentSelectionEachLine(isIncrease); + } + } + + void _indentSelectionFormat(bool isIncrease) { final indent = getSelectionStyle().attributes[Attribute.indent.key]; if (indent == null) { if (isIncrease) { @@ -119,6 +127,37 @@ class QuillController extends ChangeNotifier { formatSelection(Attribute.getIndentLevel(indent.value - 1)); } + void _indentSelectionEachLine(bool isIncrease) { + final styles = document.collectAllStylesWithOffset( + selection.start, + selection.end - selection.start, + ); + for (final style in styles) { + final indent = style.value.attributes[Attribute.indent.key]; + final formatIndex = math.max(style.offset, selection.start); + final formatLength = math.min( + style.offset + (style.length ?? 0), + selection.end, + ) - style.offset; + Attribute? formatAttribute; + if (indent == null) { + if (isIncrease) { + formatAttribute = Attribute.indentL1; + } + } else if (indent.value == 1 && !isIncrease) { + formatAttribute = Attribute.clone(Attribute.indentL1, null); + } else if (isIncrease) { + formatAttribute = Attribute.getIndentLevel(indent.value + 1); + } else { + formatAttribute = Attribute.getIndentLevel(indent.value - 1); + } + if (formatAttribute != null) { + document.format(formatIndex, formatLength, formatAttribute); + } + } + notifyListeners(); + } + /// Returns all styles for each node within selection List> getAllIndividualSelectionStyles() { final styles = document.collectAllIndividualStyles(