Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support directional key commands #227

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Proton/Proton.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@
1BFFEF1C23C334D200D2BA35 /* InlineEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BFFEF1B23C334D200D2BA35 /* InlineEditorView.swift */; };
1BFFEF1F23C3366200D2BA35 /* MockAttachmentOffsetProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BFFEF1E23C3366200D2BA35 /* MockAttachmentOffsetProvider.swift */; };
1BFFEF2123C33EA900D2BA35 /* PanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BFFEF2023C33EA900D2BA35 /* PanelView.swift */; };
7D0C8C6B2ACFD56100D2F5D1 /* MockKeyboardPress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0C8C6A2ACFD56100D2F5D1 /* MockKeyboardPress.swift */; };
7D0C8C6D2AD25D4A00D2F5D1 /* EditorKeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0C8C6C2AD25D4A00D2F5D1 /* EditorKeyTests.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -307,6 +309,8 @@
1BFFEF1B23C334D200D2BA35 /* InlineEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineEditorView.swift; sourceTree = "<group>"; };
1BFFEF1E23C3366200D2BA35 /* MockAttachmentOffsetProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAttachmentOffsetProvider.swift; sourceTree = "<group>"; };
1BFFEF2023C33EA900D2BA35 /* PanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PanelView.swift; sourceTree = "<group>"; };
7D0C8C6A2ACFD56100D2F5D1 /* MockKeyboardPress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockKeyboardPress.swift; sourceTree = "<group>"; };
7D0C8C6C2AD25D4A00D2F5D1 /* EditorKeyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorKeyTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -460,6 +464,7 @@
1B45CDDA23C04BE3001EB196 /* RichTextViewSnapshotTests.swift */,
1B4B60C5247F692D002B63CF /* ListsSnapshotTests.swift */,
1BFDC810254AA11F00BD83BD /* ListParserTests.swift */,
7D0C8C6C2AD25D4A00D2F5D1 /* EditorKeyTests.swift */,
);
path = Core;
sourceTree = "<group>";
Expand Down Expand Up @@ -487,6 +492,7 @@
1B82570323C48C350033A0A9 /* MockRichTextViewDelegate.swift */,
1B30A3632489E11F00FA1D48 /* MockRichTextViewListDelegate.swift */,
1B30A3612489DC7B00FA1D48 /* MockListFormattingProvider.swift */,
7D0C8C6A2ACFD56100D2F5D1 /* MockKeyboardPress.swift */,
);
path = Mocks;
sourceTree = "<group>";
Expand Down Expand Up @@ -1094,6 +1100,7 @@
1BFFEF1723C333B400D2BA35 /* EditorTestViewController.swift in Sources */,
1B45CDCE23BF18A5001EB196 /* XCTestHelpers.swift in Sources */,
1B45CDDB23C04BE3001EB196 /* RichTextViewSnapshotTests.swift in Sources */,
7D0C8C6B2ACFD56100D2F5D1 /* MockKeyboardPress.swift in Sources */,
1B2BC0D823CF17E300407DEE /* EditorContentTransformerTests.swift in Sources */,
1B45CDD923C0484A001EB196 /* RichTextViewTests.swift in Sources */,
1BC25B542448768200FF88AC /* EditorContextDelegateTests.swift in Sources */,
Expand All @@ -1108,6 +1115,7 @@
1B45CDCB23BF1621001EB196 /* MockDefaultTextFormattingProvider.swift in Sources */,
1BD185D0284D839C001F4FBC /* GridViewAttachmentSnapshotTests.swift in Sources */,
1B45CDA923BEED0E001EB196 /* AutogrowingTextViewTests.swift in Sources */,
7D0C8C6D2AD25D4A00D2F5D1 /* EditorKeyTests.swift in Sources */,
1BD185BF284C2A66001F4FBC /* GridTests.swift in Sources */,
1B3D19CA2483484A00B3AB59 /* EditorListsSnapshotTests.swift in Sources */,
1B183D9223CEEED900AE83E5 /* EditorContentEncoderTests.swift in Sources */,
Expand Down
15 changes: 15 additions & 0 deletions Proton/Sources/Swift/Core/RichTextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,21 @@ class RichTextView: AutogrowingTextView {
getNestedEditors(for: self)
}

override public func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
guard #available(iOS 13.4, *) else { return }
var handled: Bool = false
presses.forEach { press in
guard
let key = press.key,
let editorKey = EditorKey(key.charactersIgnoringModifiers)
else { return }
richTextViewDelegate?.richTextView(self, shouldHandle: editorKey, modifierFlags: key.modifierFlags, at: self.selectedRange, handled: &handled)
}
if handled == false {
super.pressesBegan(presses, with: event)
}
}

private func getNestedEditors(for containerView: UIView) -> [RichTextView] {
var textViews = [RichTextView]()
for view in containerView.subviews {
Expand Down
12 changes: 12 additions & 0 deletions Proton/Sources/Swift/Core/RichTextViewDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,25 @@ public enum EditorKey {
case enter
case backspace
case tab
case left
case right
case up
case down

init?(_ string: String) {
switch string {
case "\t":
self = .tab
case "\n", "\r":
self = .enter
case UIKeyCommand.inputUpArrow:
self = .up
case UIKeyCommand.inputDownArrow:
self = .down
case UIKeyCommand.inputLeftArrow:
self = .left
case UIKeyCommand.inputRightArrow:
self = .right
default:
return nil
}
Expand Down
4 changes: 4 additions & 0 deletions Proton/Sources/Swift/Editor/EditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ open class EditorView: UIView {
set { richTextView.inputView = newValue }
}

open override var isFirstResponder: Bool {
richTextView.isFirstResponder
}

required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ public class ListTextProcessor: TextProcessing {
else { return }

editor.deleteBackward()

default:
break
}
}

Expand Down
53 changes: 53 additions & 0 deletions Proton/Tests/Core/EditorKeyTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// EditorKeyTests.swift
// ProtonTests
//
// Created by Hon Thi on 8/10/2023.
// Copyright © 2023 Rajdeep Kwatra. All rights reserved.
//
rajdeep marked this conversation as resolved.
Show resolved Hide resolved
// 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.
//

import XCTest
@testable import Proton

final class EditorKeyTests: XCTestCase {
func test_ReturnsTab() {
XCTAssertEqual(EditorKey("\t"), EditorKey.tab)
}

func test_ReturnsEnter() {
XCTAssertEqual(EditorKey("\r"), EditorKey.enter)
XCTAssertEqual(EditorKey("\n"), EditorKey.enter)
}

func test_ReturnsUp() {
XCTAssertEqual(EditorKey(UIKeyCommand.inputUpArrow), EditorKey.up)
}

func test_ReturnsDown() {
XCTAssertEqual(EditorKey(UIKeyCommand.inputDownArrow), EditorKey.down)
}

func test_ReturnsLeft() {
XCTAssertEqual(EditorKey(UIKeyCommand.inputLeftArrow), EditorKey.left)
}

func test_ReturnsRight() {
XCTAssertEqual(EditorKey(UIKeyCommand.inputRightArrow), EditorKey.right)
}

func testReturnsNil() {
XCTAssertNil(EditorKey("any"))
}
}
48 changes: 48 additions & 0 deletions Proton/Tests/Core/Mocks/MockKeyboardPress.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// MockKeyboardPress.swift
// ProtonTests
//
// Created by Hon Thi on 6/10/2023.
// Copyright © 2023 Rajdeep Kwatra. All rights reserved.
//
// 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.
//

import UIKit

@available(iOS 13.4, *)
class MockUIPress: UIPress {
var _characters: String
override var key: UIKey? {
MockUIKey(characters: _characters)
}

init(characters: String) {
self._characters = characters
}
}

@available(iOS 13.4, *)
class MockUIKey: UIKey {
var _characters: String
override var charactersIgnoringModifiers: String { _characters }

init(characters: String) {
self._characters = characters
super.init()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
23 changes: 23 additions & 0 deletions Proton/Tests/Core/RichTextViewContextTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,29 @@ class RichTextViewContextTests: XCTestCase {
waitForExpectations(timeout: 1.0)
}

func testReceiveRightKey() {
guard #available(iOS 13.4, *) else { return }
rajdeep marked this conversation as resolved.
Show resolved Hide resolved
let testExpectation = expectation(description: #function)
let mockTextViewDelegate = MockRichTextViewDelegate()

let context = RichTextEditorContext.default
let textView = RichTextView(context: context)
textView.richTextViewDelegate = mockTextViewDelegate
context.textViewDidBeginEditing(textView)
textView.text = "Sample text"

let selectedRange = NSRange.zero
textView.selectedRange = selectedRange

mockTextViewDelegate.onShouldHandleKey = { _, key, _, range, _ in
XCTAssertEqual(key, EditorKey.right)
testExpectation.fulfill()
}

textView.pressesBegan(Set(arrayLiteral: MockUIPress(characters: UIKeyCommand.inputRightArrow)), with: nil)
waitForExpectations(timeout: 1.0)
}

func testInvokesTextDidChange() {
let testExpectation = expectation(description: #function)
let mockTextViewDelegate = MockRichTextViewDelegate()
Expand Down