Skip to content
This repository has been archived by the owner on Sep 27, 2024. It is now read-only.

Commit

Permalink
Fix pending format inconsistencies
Browse files Browse the repository at this point in the history
  • Loading branch information
aringenbach committed Sep 26, 2023
1 parent 861ea18 commit e1c4d66
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public class WysiwygComposerViewModel: WysiwygComposerViewModelProtocol, Observa
.foregroundColor: parserStyle.textColor]
}

private var hasPendingFormats = false
private(set) var hasPendingFormats = false

// MARK: - Public

Expand Down Expand Up @@ -183,8 +183,8 @@ public extension WysiwygComposerViewModel {
let update = model.apply(action)
if update.textUpdate() == .keep {
hasPendingFormats = true
} else if action == .codeBlock || action == .quote, attributedContent.selection.length == 0 {
// Add code block/quote as a pending format to improve block display.
} else if attributedContent.selection.length == 0, action.requiresReapplyFormattingOnEmptySelection {
// Set pending format if current action requires it.
hasPendingFormats = true
}
applyUpdate(update)
Expand Down Expand Up @@ -479,7 +479,12 @@ private extension WysiwygComposerViewModel {
do {
let htmlSelection = NSRange(location: Int(start), length: Int(end - start))
let textSelection = try attributedContent.text.attributedRange(from: htmlSelection)
attributedContent.selection = textSelection
if textSelection != attributedContent.selection {
attributedContent.selection = textSelection
// Ensure we re-apply required pending formats when switching to a zero-length selection.
// This fixes selecting in and out of a list / quote / etc
hasPendingFormats = textSelection.length == 0 && !model.reversedActions.isEmpty
}
Logger.viewModel.logDebug(["Sel(att): \(textSelection)",
"Sel: \(htmlSelection)"],
functionName: #function)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// Copyright 2023 The Matrix.org Foundation C.I.C
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

extension ComposerAction {
/// Returns `true` if action requires all current formatting to be re-applied on
/// next character stroke when triggered on an empty selection.
var requiresReapplyFormattingOnEmptySelection: Bool {
switch self {
case .bold, .italic, .strikeThrough, .underline, .inlineCode, .link, .undo, .redo:
return false
case .orderedList, .unorderedList, .indent, .unindent, .codeBlock, .quote:
return true
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,37 @@ final class WysiwygComposerViewModelTests: XCTestCase {
.contains([.traitBold, .traitItalic])
)
}

func testPendingFormatFlagInNewList() {
viewModel.apply(.bold)
viewModel.apply(.italic)
mockTrailingTyping("Text")
viewModel.enter()
// After creating a list, pending format flag is on
viewModel.apply(.orderedList)
XCTAssertTrue(viewModel.hasPendingFormats)
// Typing consumes the flag
mockTrailingTyping("Item")
XCTAssertFalse(viewModel.hasPendingFormats)
// Creating a second list item re-enables the flag
viewModel.enter()
XCTAssertTrue(viewModel.hasPendingFormats)
}

func testPendingFormatFlagAfterReselectingListItem() {
viewModel.apply(.bold)
viewModel.apply(.italic)
mockTrailingTyping("Text")
viewModel.enter()
viewModel.apply(.orderedList)
let inListSelection = viewModel.attributedContent.selection
let insertedText = "Text"
mockTyping(insertedText, at: 0)
// After re-selecting the empty list item, pending format flag is still on
viewModel.select(range: NSRange(location: inListSelection.location + insertedText.utf16Length,
length: inListSelection.length))
XCTAssertTrue(viewModel.hasPendingFormats)
}
}

// MARK: - WysiwygTestExpectation
Expand Down Expand Up @@ -216,6 +247,7 @@ extension WysiwygComposerViewModelTests {
if shouldAcceptChange {
// Force apply since the text view should've updated by itself
viewModel.textView.apply(viewModel.attributedContent)
viewModel.didUpdateText()
}
}

Expand All @@ -239,6 +271,7 @@ extension WysiwygComposerViewModelTests {
if shouldAcceptChange {
// Force apply since the text view should've updated by itself
viewModel.textView.apply(viewModel.attributedContent)
viewModel.didUpdateText()
}
}

Expand Down

0 comments on commit e1c4d66

Please sign in to comment.