From 380523afbf1e94535450b4c6859afd74671a848d Mon Sep 17 00:00:00 2001 From: "C. Bess" Date: Mon, 28 Mar 2022 19:47:50 -0500 Subject: [PATCH 01/10] support alternate bullet style per level similar to web browsers, draw different bullets at different levels --- Aztec/Classes/TextKit/LayoutManager.swift | 21 +++++++++++++++++-- .../TextKit/ParagraphProperty/TextList.swift | 15 ++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Aztec/Classes/TextKit/LayoutManager.swift b/Aztec/Classes/TextKit/LayoutManager.swift index 772aa7d23..7f06a8cad 100644 --- a/Aztec/Classes/TextKit/LayoutManager.swift +++ b/Aztec/Classes/TextKit/LayoutManager.swift @@ -213,15 +213,16 @@ private extension LayoutManager { } let characterRange = self.characterRange(forGlyphRange: glyphsToShow, actualGlyphRange: nil) + var firstLevelWidth: CGFloat? textStorage.enumerateParagraphRanges(spanning: characterRange) { (range, enclosingRange) in - guard textStorage.string.isStartOfNewLine(atUTF16Offset: enclosingRange.location), let paragraphStyle = textStorage.attribute(.paragraphStyle, at: enclosingRange.location, effectiveRange: nil) as? ParagraphStyle, let list = paragraphStyle.lists.last else { return } + let attributes = textStorage.attributes(at: enclosingRange.location, effectiveRange: nil) let glyphRange = self.glyphRange(forCharacterRange: enclosingRange, actualCharacterRange: nil) let markerRect = rectForItem(range: glyphRange, origin: origin, paragraphStyle: paragraphStyle) @@ -233,8 +234,24 @@ private extension LayoutManager { start = textStorage.numberOfItems(in: list, at: enclosingRange.location) } } + + // determine indentation level + var indentLevel = 1 + if list.style == .unordered { + // only get the width of the first level once + if firstLevelWidth == nil { + firstLevelWidth = paragraphStyle.indentToFirst(TextList.self) + } + + // calculate current indent level + let indentWidth = paragraphStyle.indentToLast(TextList.self) + if let firstLevelWidth = firstLevelWidth { + indentLevel = Int(indentWidth / firstLevelWidth) + } + } + markerNumber += start - let markerString = list.style.markerText(forItemNumber: markerNumber) + let markerString = list.style.markerText(forItemNumber: markerNumber, indentLevel: indentLevel) drawItem(markerString, in: markerRect, styled: attributes, at: enclosingRange.location) } } diff --git a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift index a2cf28306..f2a9b8d2b 100644 --- a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift +++ b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift @@ -14,10 +14,19 @@ open class TextList: ParagraphProperty { case ordered case unordered - func markerText(forItemNumber number: Int) -> String { + func markerText(forItemNumber number: Int, indentLevel: Int = 1) -> String { switch self { - case .ordered: return "\(number)." - case .unordered: return "\u{2022}" + case .ordered: + return "\(number)." + case .unordered: + switch indentLevel { + case 1: + return "\u{2022}" + case 2: + return "\u{2E30}" + default: + return "\u{2B29}" + } } } } From 4f27745be7f47148da40739badede9264c0e23c1 Mon Sep 17 00:00:00 2001 From: "C. Bess" Date: Mon, 28 Mar 2022 19:51:03 -0500 Subject: [PATCH 02/10] cleanup whitespace --- Aztec/Classes/TextKit/LayoutManager.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Aztec/Classes/TextKit/LayoutManager.swift b/Aztec/Classes/TextKit/LayoutManager.swift index 7f06a8cad..cb55914d7 100644 --- a/Aztec/Classes/TextKit/LayoutManager.swift +++ b/Aztec/Classes/TextKit/LayoutManager.swift @@ -222,7 +222,7 @@ private extension LayoutManager { else { return } - + let attributes = textStorage.attributes(at: enclosingRange.location, effectiveRange: nil) let glyphRange = self.glyphRange(forCharacterRange: enclosingRange, actualCharacterRange: nil) let markerRect = rectForItem(range: glyphRange, origin: origin, paragraphStyle: paragraphStyle) @@ -234,7 +234,7 @@ private extension LayoutManager { start = textStorage.numberOfItems(in: list, at: enclosingRange.location) } } - + // determine indentation level var indentLevel = 1 if list.style == .unordered { @@ -242,14 +242,14 @@ private extension LayoutManager { if firstLevelWidth == nil { firstLevelWidth = paragraphStyle.indentToFirst(TextList.self) } - + // calculate current indent level let indentWidth = paragraphStyle.indentToLast(TextList.self) if let firstLevelWidth = firstLevelWidth { indentLevel = Int(indentWidth / firstLevelWidth) } } - + markerNumber += start let markerString = list.style.markerText(forItemNumber: markerNumber, indentLevel: indentLevel) drawItem(markerString, in: markerRect, styled: attributes, at: enclosingRange.location) From 08c85ecab7c21b3074bcf769dd43c59e3cf21415 Mon Sep 17 00:00:00 2001 From: "C. Bess" Date: Sun, 6 Aug 2023 08:45:15 -0500 Subject: [PATCH 03/10] Squashed commit of the following: commit 4d0522d67b0056ac211466caaa76936cc5b4f947 Merge: 2b54f87d 59d5090e Author: Gio Lodi Date: Thu Mar 9 12:08:36 2023 +1100 Point `bash-cache` plugin to new `a8c-ci-toolkit` location (#1367) commit 59d5090e7deab4c3315ddabc42ee6126280aaee6 Author: Gio Lodi Date: Mon Mar 6 21:10:15 2023 +1100 Remove unused YAML anchor and fix indentation in pipeline commit e0526bfbb672af11bcba479a4553bc283530082b Author: Gio Lodi Date: Mon Mar 6 21:09:53 2023 +1100 Point `bash-cache` plugin to new `a8c-ci-toolkit` location While I was at it, I also updated the version to the latest, 2.13.0. This was done via: ``` find . -type f -name "*.yml" -exec sed -i '' 's/automattic\/bash-cache#[0-9.]\{1,\}/automattic\/a8c-ci-toolkit#2.13.0/g' {} + ``` commit 2b54f87d3d914dee4ccb4ac321c0a2d7656d2861 Merge: b7d8a705 d18d0a8a Author: Gio Lodi Date: Thu Oct 20 16:59:18 2022 +1100 Add `CHANGELOG.md` (#1365) commit d18d0a8ae78b11210c2e9e9f02357ea2b61c84f1 Author: Gio Lodi Date: Thu Oct 20 15:18:16 2022 +1100 Update `PULL_REQUEST_TEMPLATE.md` with note about `CHANGELOG.md` commit 2c73ef593e8fc4b783b89787e1b2aa79512b6d4c Author: Gio Lodi Date: Thu Oct 20 14:32:38 2022 +1100 Add changelog entry about changelog itself commit 212ce2f9892356111fb4b829ca968b164bd6b723 Author: Gio Lodi Date: Thu Oct 20 14:15:12 2022 +1100 Update `CHANGELOG.md` with new format commit b7d8a70582133a329b6e4502e437b7a3cdd4ffb6 Merge: 91cf065d 40afa553 Author: Gio Lodi Date: Tue Oct 4 10:29:42 2022 +1100 Tools upgrade: Ruby 2.7.4, bash-cache 2.8.0 (#1363) commit 40afa553e5d641410ae113c7ea01a4d1a7e46292 Author: Gio Lodi Date: Mon Oct 3 20:00:33 2022 +1100 Use `bash-cache` Buildkite plugin version 2.8.0 This version includes a fix that removes the need to run `gem install bundler` before each step. commit bd8840a917ce0003e68b8d640c0ba34ac892f2ed Author: Gio Lodi Date: Mon Oct 3 19:59:52 2022 +1100 Use Ruby version 2.7.4 commit 91cf065d5541f50a8d9a9549775dc3d4c825eeda Merge: 9d8d9b82 1ce621c2 Author: Diego Rey Mendez Date: Wed Aug 10 15:07:41 2022 +0200 Merge pull request #1353 from cbess/expose-indentation-actions Expose indentation operations commit 9d8d9b822d3d154bb89267498ce24c9cd58fea02 Merge: b16c7636 7eb60c72 Author: Tony Li Date: Mon Jul 25 11:11:13 2022 +1200 Merge pull request #1359 from wordpress-mobile/remove/circle-ci Remove CircleCI commit 7eb60c72a56da974ad320e20c1b0d068ef74f8dc Author: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Fri Jul 22 23:23:27 2022 -0600 Trigger Build commit f91511ec875b80528fc06632be5af456aa069cc5 Author: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Thu Jul 21 23:03:28 2022 -0600 Remove CircleCI commit 1ce621c2cd93c5f883244be06704fffe7225e397 Author: C. Bess Date: Sat Mar 26 12:31:51 2022 -0500 cleanup whitespace commit ed55d4a1107355eafc338c8d96bd580d6128b8fd Author: C. Bess Date: Sat Mar 26 12:31:03 2022 -0500 cleanup whitespace commit 4561534101df45f34df38b19914201cc2b99ab71 Author: C. Bess Date: Sat Mar 26 12:01:30 2022 -0500 expose indentation operations - make increase/decrease indentation actions visible - cleanup code --- .buildkite/pipeline.yml | 11 +---- .buildkite/publish-aztec-pod.sh | 3 -- .buildkite/publish-editor.pod.sh | 3 -- .circleci/config.yml | 60 ---------------------------- .github/PULL_REQUEST_TEMPLATE.md | 3 ++ .ruby-version | 2 +- Aztec/Classes/TextKit/TextView.swift | 42 ++++++++++--------- CHANGELOG.md | 55 +++++++++++++++++++++++++ 8 files changed, 80 insertions(+), 99 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index bc33a6bc9..b19eab5c6 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -1,7 +1,7 @@ # Nodes with values to reuse in the pipeline. common_params: plugins: &common_plugins - - &bash_cache automattic/bash-cache#2.0.0: ~ + - automattic/a8c-ci-toolkit#2.13.0 # Common environment values to use with the `env` key. env: &common_env IMAGE_ID: xcode-13 @@ -14,9 +14,6 @@ steps: - label: "🧪 Build and Test" key: "test" command: | - # See https://github.com/Automattic/bash-cache-buildkite-plugin/issues/16 - gem install bundler:2.3.4 - build_and_test_pod env: *common_env plugins: *common_plugins @@ -27,9 +24,6 @@ steps: - label: "🔬 Validate Podspecs" key: "validate" command: | - # See https://github.com/Automattic/bash-cache-buildkite-plugin/issues/16 - gem install bundler:2.3.4 - validate_podspec env: *common_env plugins: *common_plugins @@ -40,9 +34,6 @@ steps: - label: "🧹 Lint" key: "lint" command: | - # See https://github.com/Automattic/bash-cache-buildkite-plugin/issues/16 - gem install bundler:2.3.4 - lint_pod env: *common_env plugins: *common_plugins diff --git a/.buildkite/publish-aztec-pod.sh b/.buildkite/publish-aztec-pod.sh index 2343776da..a0beab3ba 100644 --- a/.buildkite/publish-aztec-pod.sh +++ b/.buildkite/publish-aztec-pod.sh @@ -5,9 +5,6 @@ SPECS_REPO="git@github.com:wordpress-mobile/cocoapods-specs.git" SLACK_WEBHOOK=$PODS_SLACK_WEBHOOK echo "--- :rubygems: Setting up Gems" -# See https://github.com/Automattic/bash-cache-buildkite-plugin/issues/16 -gem install bundler:2.3.4 - install_gems echo "--- :cocoapods: Publishing Pod to CocoaPods CDN" diff --git a/.buildkite/publish-editor.pod.sh b/.buildkite/publish-editor.pod.sh index f6534c70f..68dbb507c 100644 --- a/.buildkite/publish-editor.pod.sh +++ b/.buildkite/publish-editor.pod.sh @@ -5,9 +5,6 @@ SPECS_REPO="git@github.com:wordpress-mobile/cocoapods-specs.git" SLACK_WEBHOOK=$PODS_SLACK_WEBHOOK echo "--- :rubygems: Setting up Gems" -# See https://github.com/Automattic/bash-cache-buildkite-plugin/issues/16 -gem install bundler:2.3.4 - install_gems echo "--- :cocoapods: Publishing Pod to CocoaPods CDN" diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 900eb1b0d..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,60 +0,0 @@ -version: 2.1 - -orbs: - # Using 1.0 of our Orbs means it will use the latest 1.0.x version from https://github.com/wordpress-mobile/circleci-orbs - ios: wordpress-mobile/ios@1.0 - git: wordpress-mobile/git@1.0 - -xcode_version: &xcode_version - xcode-version: "12.4.0" - -iphone_test_device: &iphone_test_device - device: iPhone 11 - ios-version: "14.4" - -workflows: - test_and_validate: - jobs: - - ios/test: - name: Test - <<: *xcode_version - bundle-install: false - pod-install: false - workspace: Aztec.xcworkspace - scheme: AztecExample - <<: *iphone_test_device - - - ios/validate-podspec: - name: Validate WordPress-Aztec-iOS.podspec - <<: *xcode_version - podspec-path: WordPress-Aztec-iOS.podspec - - - ios/validate-podspec: - name: Validate WordPress-Editor-iOS.podspec - <<: *xcode_version - podspec-path: WordPress-Editor-iOS.podspec - # Reference WordPress-Aztec-iOS.podspec locally so we don't have to get it from the specs repo - additional-parameters: --include-podspecs=WordPress-Aztec-iOS.podspec - - - ios/publish-podspec: - name: Publish WordPress-Aztec-iOS to Trunk - <<: *xcode_version - podspec-path: WordPress-Aztec-iOS.podspec - post-to-slack: true - filters: - tags: - only: /.*/ - branches: - ignore: /.*/ - - - ios/publish-podspec: - name: Publish WordPress-Editor-iOS to Trunk - requires: [ "Publish WordPress-Aztec-iOS to Trunk" ] - <<: *xcode_version - podspec-path: WordPress-Editor-iOS.podspec - post-to-slack: true - filters: - tags: - only: /.*/ - branches: - ignore: /.*/ diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 0977562c4..ace448da6 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,3 +2,6 @@ Fixes # To test: +--- + +- [ ] I have considered if this change warrants release notes and have added them to the appropriate section in the `CHANGELOG.md` if necessary. diff --git a/.ruby-version b/.ruby-version index 2714f5313..a4dd9dba4 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.6.4 +2.7.4 diff --git a/Aztec/Classes/TextKit/TextView.swift b/Aztec/Classes/TextKit/TextView.swift index 2d83526bb..e4e0404bf 100644 --- a/Aztec/Classes/TextKit/TextView.swift +++ b/Aztec/Classes/TextKit/TextView.swift @@ -555,43 +555,44 @@ open class TextView: UITextView { } @objc func handleShiftTab(command: UIKeyCommand) { - - let lists = TextListFormatter.lists(in: typingAttributes) - let quotes = BlockquoteFormatter.blockquotes(in: typingAttributes) - - if let list = lists.last { - indent(list: list, increase: false) - - } else if let quote = quotes.last { - indent(blockquote: quote, increase: false) - - } else { - return - } + decreaseIndent() } @objc func handleTab(command: UIKeyCommand) { - + increaseIndent() + } + + // MARK: - General Indentation + + /// Increases the indentation of the selected range + open func increaseIndent() { let lists = TextListFormatter.lists(in: typingAttributes) let quotes = BlockquoteFormatter.blockquotes(in: typingAttributes) if let list = lists.last, lists.count < maximumListIndentationLevels { indent(list: list) - } else if let quote = quotes.last, quotes.count < maximumBlockquoteIndentationLevels { indent(blockquote: quote) - } else { insertText(String(.tab)) } - } - + /// Decreases the indentation of the selected range + open func decreaseIndent() { + let lists = TextListFormatter.lists(in: typingAttributes) + let quotes = BlockquoteFormatter.blockquotes(in: typingAttributes) + + if let list = lists.last { + indent(list: list, increase: false) + } else if let quote = quotes.last { + indent(blockquote: quote, increase: false) + } + } + // MARK: - Text List indent methods private func indent(list: TextList, increase: Bool = true) { - let formatter = TextListFormatter(style: list.style, placeholderAttributes: nil, increaseDepth: true) let li = LiFormatter(placeholderAttributes: nil) @@ -629,9 +630,6 @@ open class TextView: UITextView { // MARK: - Blockquote indent methods private func indent(blockquote: Blockquote, increase: Bool = true) { - - - let formatter = BlockquoteFormatter(placeholderAttributes: typingAttributes, increaseDepth: true) let targetRange = formatter.applicationRange(for: selectedRange, in: storage) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66e08e7e0..1067a9628 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,58 @@ +# Changelog + +The format of this document is inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + + +## Unreleased + +### Breaking Changes + +_None._ + +### New Features + +_None._ + +### Bug Fixes + +_None._ + +### Internal Changes + +- Add this changelog file. [#1365] + +--- + +_Versions below this precede the Keep a Changelog-inspired formatting._ + + 1.19.8 ------- * Fix Li tag when switching the list style. From 2b59a4db0f182d0bd0aba6ae53693ca578fd446b Mon Sep 17 00:00:00 2001 From: "C. Bess" Date: Sun, 6 Aug 2023 09:39:18 -0500 Subject: [PATCH 04/10] add TextList.IndentStyle - support indent style option --- Aztec/Classes/TextKit/LayoutManager.swift | 7 +++++-- .../Classes/TextKit/ParagraphProperty/TextList.swift | 9 +++++++++ Aztec/Classes/TextKit/TextView.swift | 11 +++++++++++ Example/Example/EditorDemoController.swift | 1 + 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Aztec/Classes/TextKit/LayoutManager.swift b/Aztec/Classes/TextKit/LayoutManager.swift index cb55914d7..06d4011af 100644 --- a/Aztec/Classes/TextKit/LayoutManager.swift +++ b/Aztec/Classes/TextKit/LayoutManager.swift @@ -27,6 +27,9 @@ class LayoutManager: NSLayoutManager { /// var blockquoteBorderWidth: CGFloat = 2 + /// The list indent style + /// + var listIndentStyle: TextList.IndentStyle = .default /// Draws the background, associated to a given Text Range /// @@ -235,9 +238,9 @@ private extension LayoutManager { } } - // determine indentation level var indentLevel = 1 - if list.style == .unordered { + // Determine indentation level, if needed. The indentation level is only used by the standard list style + if list.style == .unordered, listIndentStyle == .standard { // only get the width of the first level once if firstLevelWidth == nil { firstLevelWidth = paragraphStyle.indentToFirst(TextList.self) diff --git a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift index f2a9b8d2b..394141827 100644 --- a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift +++ b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift @@ -31,6 +31,15 @@ open class TextList: ParagraphProperty { } } + /// List Indent Styles + /// + public enum IndentStyle: Int { + /// A default single bullet style for each indentation level + case `default` + /// The standard bullet styles for each indentation level (i.e., HTML style) + case standard + } + public let reversed: Bool public let start: Int? diff --git a/Aztec/Classes/TextKit/TextView.swift b/Aztec/Classes/TextKit/TextView.swift index e4e0404bf..8c6b27e65 100644 --- a/Aztec/Classes/TextKit/TextView.swift +++ b/Aztec/Classes/TextKit/TextView.swift @@ -228,6 +228,17 @@ open class TextView: UITextView { var maximumListIndentationLevels = 7 + /// The list indent style + /// Default is `default`, single style for each level. + public var listIndentStyle: TextList.IndentStyle { + get { + return layout.listIndentStyle + } + set { + layout.listIndentStyle = newValue + } + } + // MARK: - Properties: Blockquotes /// The max levels of quote indentation allowed diff --git a/Example/Example/EditorDemoController.swift b/Example/Example/EditorDemoController.swift index 016a0cb5f..dc567222d 100644 --- a/Example/Example/EditorDemoController.swift +++ b/Example/Example/EditorDemoController.swift @@ -174,6 +174,7 @@ class EditorDemoController: UIViewController { view.addSubview(separatorView) editorView.richTextView.textContainer.lineFragmentPadding = 0 + editorView.richTextView.listIndentStyle = .standard // color setup if #available(iOS 13.0, *) { view.backgroundColor = UIColor.systemBackground From 9568191106c6520f1f627fdc35d5f3b11382d11a Mon Sep 17 00:00:00 2001 From: "C. Bess" Date: Sun, 6 Aug 2023 09:41:48 -0500 Subject: [PATCH 05/10] satisfy hound --- Aztec/Classes/TextKit/ParagraphProperty/TextList.swift | 2 +- Aztec/Classes/TextKit/TextView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift index 394141827..1c9252977 100644 --- a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift +++ b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift @@ -39,7 +39,7 @@ open class TextList: ParagraphProperty { /// The standard bullet styles for each indentation level (i.e., HTML style) case standard } - + public let reversed: Bool public let start: Int? diff --git a/Aztec/Classes/TextKit/TextView.swift b/Aztec/Classes/TextKit/TextView.swift index 8c6b27e65..a543fcba6 100644 --- a/Aztec/Classes/TextKit/TextView.swift +++ b/Aztec/Classes/TextKit/TextView.swift @@ -238,7 +238,7 @@ open class TextView: UITextView { layout.listIndentStyle = newValue } } - + // MARK: - Properties: Blockquotes /// The max levels of quote indentation allowed From 5f45e62cba084ed763587e8f000ae884a2069ed6 Mon Sep 17 00:00:00 2001 From: "C. Bess" Date: Sat, 22 Feb 2025 16:40:39 -0600 Subject: [PATCH 06/10] support multi-style ordered list - support html-style order list, using alphabets and roman numerals - cleanup code --- Aztec/Classes/TextKit/LayoutManager.swift | 10 +-- .../TextKit/ParagraphProperty/TextList.swift | 76 ++++++++++++++++++- Example/Example/EditorDemoController.swift | 2 +- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/Aztec/Classes/TextKit/LayoutManager.swift b/Aztec/Classes/TextKit/LayoutManager.swift index d72276573..92ea55598 100644 --- a/Aztec/Classes/TextKit/LayoutManager.swift +++ b/Aztec/Classes/TextKit/LayoutManager.swift @@ -238,9 +238,9 @@ private extension LayoutManager { } } - var indentLevel = 1 - // Determine indentation level, if needed. The indentation level is only used by the standard list style - if list.style == .unordered, listIndentStyle == .standard { + var indentLevel: Int? + // Determine indentation level, if needed. The indentation level is only determined for the standard list style + if listIndentStyle == .standard { // only get the width of the first level once if firstLevelWidth == nil { firstLevelWidth = paragraphStyle.indentToFirst(TextList.self) @@ -248,9 +248,7 @@ private extension LayoutManager { // calculate current indent level let indentWidth = paragraphStyle.indentToLast(TextList.self) - if let firstLevelWidth = firstLevelWidth { - indentLevel = Int(indentWidth / firstLevelWidth) - } + indentLevel = Int(indentWidth / firstLevelWidth!) } markerNumber += start diff --git a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift index f842b8772..53079d08a 100644 --- a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift +++ b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift @@ -1,6 +1,7 @@ import Foundation import UIKit +fileprivate let DefaultUnorderedListMarkerText = "\u{2022}" // MARK: - Text List // @@ -14,17 +15,36 @@ open class TextList: ParagraphProperty { case ordered case unordered - func markerText(forItemNumber number: Int, indentLevel: Int = 1) -> String { + func markerText(forItemNumber number: Int, indentLevel: Int? = nil) -> String { switch self { case .ordered: - return "\(number)." + if indentLevel == nil { + return "\(number)." + } + + switch indentLevel { + case 1: + return "\(number)." + case 2: + let text = getLetter(for: number) + return "\(text)." + default: + // marker for all levels > 2 + let text = getRomanNumeral(for: number) + return "\(text)." + } case .unordered: + if indentLevel == nil { + return DefaultUnorderedListMarkerText + } + switch indentLevel { case 1: - return "\u{2022}" + return DefaultUnorderedListMarkerText case 2: return "\u{2E30}" default: + // marker for all levels > 2 return "\u{2B29}" } } @@ -108,3 +128,53 @@ open class TextList: ParagraphProperty { return lhs.style == rhs.style && lhs.start == rhs.start && lhs.reversed == rhs.reversed } } + +/// Returns the letters to use as the ordered list marker text +fileprivate func getLetter(for number: Int) -> String { + let listChars = "abcdefghijklmnopqrstuvwxyz" + let charCount = listChars.count + + // for recursion + func convert(_ value: Int) -> String { + if value <= charCount { + return String(listChars[listChars.index(listChars.startIndex, offsetBy: value - 1)]) + } + + let quotient = (value - 1) / charCount + let remainder = (value - 1) % charCount + return convert(quotient) + String(listChars[listChars.index(listChars.startIndex, offsetBy: remainder)]) + } + + return convert(number) +} + +/// Returns the roman numeral to use as the ordered list marker text +fileprivate func getRomanNumeral(for number: Int) -> String { + let romanValues = [ + 1000: "m", 900: "cm", 500: "d", 400: "cd", + 100: "c", 90: "xc", 50: "l", 40: "xl", + 10: "x", 9: "ix", 5: "v", 4: "iv", 1: "i" + ] + + // used for recursion + func convert(_ value: Int) -> String { + guard value > 0 else { + return "" + } + + if let numeral = romanValues[value] { + return numeral + } + + for (key, numeral) in romanValues.sorted(by: { $0.key > $1.key }) { + if value >= key { + // recursive + return numeral + convert(value - key) + } + } + + return "" + } + + return convert(number) +} diff --git a/Example/Example/EditorDemoController.swift b/Example/Example/EditorDemoController.swift index dc567222d..919493846 100644 --- a/Example/Example/EditorDemoController.swift +++ b/Example/Example/EditorDemoController.swift @@ -258,7 +258,7 @@ class EditorDemoController: UIViewController { rightMargin -= view.safeAreaInsets.right scrollInsets.right = -rightMargin - editorView.scrollIndicatorInsets = scrollInsets + editorView.horizontalScrollIndicatorInsets = scrollInsets } func updateTitleHeight() { From 3e03ddf1e09100477c6a97187d4ae3eda3923152 Mon Sep 17 00:00:00 2001 From: "C. Bess" Date: Sat, 22 Feb 2025 17:03:54 -0600 Subject: [PATCH 07/10] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac46b3934..9faf937e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,7 @@ _None._ ### New Features -_None._ +* Added support for alternate bullet styles per level for ordered and unordered lists [#1409] ### Bug Fixes From 05d8eae97a8258bf42b8fd372f846640cf91f15f Mon Sep 17 00:00:00 2001 From: "C. Bess" Date: Sun, 30 Mar 2025 19:34:22 -0500 Subject: [PATCH 08/10] use NSTextList, roman markers --- .../TextKit/ParagraphProperty/TextList.swift | 34 ++----------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift index 53079d08a..47553cc8b 100644 --- a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift +++ b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift @@ -2,6 +2,7 @@ import Foundation import UIKit fileprivate let DefaultUnorderedListMarkerText = "\u{2022}" +fileprivate let romanMarker = NSTextList(markerFormat: .lowercaseRoman, options: 0) // MARK: - Text List // @@ -30,7 +31,7 @@ open class TextList: ParagraphProperty { return "\(text)." default: // marker for all levels > 2 - let text = getRomanNumeral(for: number) + let text = romanMarker.marker(forItemNumber: number) return "\(text)." } case .unordered: @@ -147,34 +148,3 @@ fileprivate func getLetter(for number: Int) -> String { return convert(number) } - -/// Returns the roman numeral to use as the ordered list marker text -fileprivate func getRomanNumeral(for number: Int) -> String { - let romanValues = [ - 1000: "m", 900: "cm", 500: "d", 400: "cd", - 100: "c", 90: "xc", 50: "l", 40: "xl", - 10: "x", 9: "ix", 5: "v", 4: "iv", 1: "i" - ] - - // used for recursion - func convert(_ value: Int) -> String { - guard value > 0 else { - return "" - } - - if let numeral = romanValues[value] { - return numeral - } - - for (key, numeral) in romanValues.sorted(by: { $0.key > $1.key }) { - if value >= key { - // recursive - return numeral + convert(value - key) - } - } - - return "" - } - - return convert(number) -} From 7125615d31b410497413148695a34db57aeedc97 Mon Sep 17 00:00:00 2001 From: "C. Bess" Date: Mon, 31 Mar 2025 19:01:27 -0500 Subject: [PATCH 09/10] prevent negatives --- Aztec/Classes/TextKit/ParagraphProperty/TextList.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift index 47553cc8b..4e42e59e1 100644 --- a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift +++ b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift @@ -146,5 +146,5 @@ fileprivate func getLetter(for number: Int) -> String { return convert(quotient) + String(listChars[listChars.index(listChars.startIndex, offsetBy: remainder)]) } - return convert(number) + return convert(abs(number)) } From e556bc7cf753ded1e7cad232007913c24ca071dd Mon Sep 17 00:00:00 2001 From: "C. Bess" Date: Tue, 1 Apr 2025 19:53:19 -0500 Subject: [PATCH 10/10] change indent style name - use varied --- Aztec/Classes/TextKit/LayoutManager.swift | 2 +- Aztec/Classes/TextKit/ParagraphProperty/TextList.swift | 4 ++-- Example/Example/EditorDemoController.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Aztec/Classes/TextKit/LayoutManager.swift b/Aztec/Classes/TextKit/LayoutManager.swift index 92ea55598..ac5ce1106 100644 --- a/Aztec/Classes/TextKit/LayoutManager.swift +++ b/Aztec/Classes/TextKit/LayoutManager.swift @@ -240,7 +240,7 @@ private extension LayoutManager { var indentLevel: Int? // Determine indentation level, if needed. The indentation level is only determined for the standard list style - if listIndentStyle == .standard { + if listIndentStyle == .varied { // only get the width of the first level once if firstLevelWidth == nil { firstLevelWidth = paragraphStyle.indentToFirst(TextList.self) diff --git a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift index 4e42e59e1..1fbdd5a0b 100644 --- a/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift +++ b/Aztec/Classes/TextKit/ParagraphProperty/TextList.swift @@ -57,8 +57,8 @@ open class TextList: ParagraphProperty { public enum IndentStyle: Int { /// A default single bullet style for each indentation level case `default` - /// The standard bullet styles for each indentation level (i.e., HTML style) - case standard + /// Use a varied (distinct) bullet style for each indentation level (i.e., WYSIWYG style) + case varied } public let reversed: Bool diff --git a/Example/Example/EditorDemoController.swift b/Example/Example/EditorDemoController.swift index 919493846..090801f2b 100644 --- a/Example/Example/EditorDemoController.swift +++ b/Example/Example/EditorDemoController.swift @@ -174,7 +174,7 @@ class EditorDemoController: UIViewController { view.addSubview(separatorView) editorView.richTextView.textContainer.lineFragmentPadding = 0 - editorView.richTextView.listIndentStyle = .standard + editorView.richTextView.listIndentStyle = .varied // color setup if #available(iOS 13.0, *) { view.backgroundColor = UIColor.systemBackground