diff --git a/VinOutlineKit/Sources/VinOutlineKit/Commands/SortRowsCommand.swift b/VinOutlineKit/Sources/VinOutlineKit/Commands/SortRowsCommand.swift new file mode 100644 index 00000000..110d32d4 --- /dev/null +++ b/VinOutlineKit/Sources/VinOutlineKit/Commands/SortRowsCommand.swift @@ -0,0 +1,48 @@ +// +// Created by Maurice Parker on 9/14/24. +// + +import Foundation + +public final class SortRowsCommand: OutlineCommand { + + var rowMoves = [Outline.RowMove]() + var restoreMoves = [Outline.RowMove]() + + public init(actionName: String, + undoManager: UndoManager, + delegate: OutlineCommandDelegate, + outline: Outline, + rows: [Row]) { + + let displayOrderRows = rows.sortedByDisplayOrder() + + for row in displayOrderRows { + guard let parent = row.parent, let index = parent.firstIndexOfRow(row) else { continue } + restoreMoves.append(Outline.RowMove(row: row, toParent: parent, toChildIndex: index)) + } + + let sortedRows = rows.sorted { left, right in + return (left.topic?.string ?? "").caseInsensitiveCompare(right.topic?.string ?? "") == .orderedAscending + } + + let startIndex = displayOrderRows.first!.parent!.firstIndexOfRow(displayOrderRows.first!) ?? 0 + + for i in 0.. Bool { - guard let parent = rows.first?.parent else { return true } + return !areRowsContiguous(rows: rows) + } - for row in rows { - if !parent.containsRow(row) { - return true + public func isSortRowsUnavailable(rows: [Row]) -> Bool { + guard rows.count > 1 else { return true } + return !areRowsContiguous(rows: rows) + } + + func areRowsContiguous(rows: [Row]) -> Bool { + let sortedRows = rows.sortedByDisplayOrder() + + guard let firstRow = sortedRows.first, + let parent = firstRow.parent, + let firstIndex = parent.firstIndexOfRow(firstRow) else { + return false + } + + for i in 0.. 1 { + command.title = .sortRowsControlLabel + } else { + command.title = .sortRowControlLabel + } case .deleteCurrentRows: if currentRows?.count ?? 0 > 1 { command.title = .deleteRowsControlLabel @@ -1299,6 +1312,11 @@ class EditorViewController: UIViewController, DocumentsActivityItemsConfiguratio groupRows(rows) } + @objc func sortCurrentRows(_ sender: Any?) { + guard let rows = currentRows else { return } + sortRows(rows) + } + @objc func toggleCompleteRows(_ sender: Any?) { guard let outline, let rows = currentRows else { return } if !outline.isCompleteUnavailable(rows: rows) { @@ -2811,7 +2829,10 @@ private extension EditorViewController { } if !outline.isGroupRowsUnavailable(rows: rows) { outlineActions.append(self.groupAction(rows: rows)) -6 } + } + if !outline.isSortRowsUnavailable(rows: rows) { + outlineActions.append(self.sortAction(rows: rows)) + } menuItems.append(UIMenu(title: "", options: .displayInline, children: outlineActions)) var viewActions = [UIAction]() @@ -2941,6 +2962,13 @@ private extension EditorViewController { } } + func sortAction(rows: [Row]) -> UIAction { + let title = rows.count == 1 ? String.sortRowControlLabel : String.sortRowsControlLabel + return UIAction(title: title, image: .sortRows) { [weak self] action in + self?.sortCurrentRows(rows) + } + } + func moveCursorTo(row: Row) { guard let shadowTableIndex = row.shadowTableIndex else { return @@ -3489,6 +3517,17 @@ private extension EditorViewController { command.execute() } + func sortRows(_ rows: [Row]) { + guard let undoManager, let outline else { return } + + let command = SortRowsCommand(actionName: .groupRowsControlLabel, + undoManager: undoManager, + delegate: self, + outline: outline, + rows: rows) + + command.execute() + } func completeRows(_ rows: [Row], rowStrings: RowStrings? = nil) { guard let undoManager, let outline else { return } diff --git a/Zavala/Resources/Localizable.xcstrings b/Zavala/Resources/Localizable.xcstrings index e12db059..3d93beac 100644 --- a/Zavala/Resources/Localizable.xcstrings +++ b/Zavala/Resources/Localizable.xcstrings @@ -1038,6 +1038,12 @@ "Small" : { "comment" : "Control Label: Small" }, + "Sort Row" : { + "comment" : "Control Label: Sort Row" + }, + "Sort Rows" : { + "comment" : "Control Label: Sort Rows" + }, "Split Row" : { "comment" : "Control Label: Split Row" },