Skip to content

Commit

Permalink
Updated documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
rajdeep committed Apr 8, 2020
1 parent 1fc5e77 commit 716e8a4
Show file tree
Hide file tree
Showing 21 changed files with 196 additions and 46 deletions.
4 changes: 0 additions & 4 deletions Proton/Proton.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
1B45CDD023C007AF001EB196 /* RichTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B45CDCF23C007AF001EB196 /* RichTextView.swift */; };
1B45CDD223C00856001EB196 /* TextContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B45CDD123C00856001EB196 /* TextContainer.swift */; };
1B45CDD523C00927001EB196 /* EditorContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B45CDD423C00927001EB196 /* EditorContent.swift */; };
1B45CDD723C00A0A001EB196 /* EditorAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B45CDD623C00A0A001EB196 /* EditorAttribute.swift */; };
1B45CDD923C0484A001EB196 /* RichTextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B45CDD823C0484A001EB196 /* RichTextViewTests.swift */; };
1B45CDDB23C04BE3001EB196 /* RichTextViewSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B45CDDA23C04BE3001EB196 /* RichTextViewSnapshotTests.swift */; };
1B45CDDD23C05340001EB196 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B45CDDC23C05340001EB196 /* Attachment.swift */; };
Expand Down Expand Up @@ -158,7 +157,6 @@
1B45CDCF23C007AF001EB196 /* RichTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RichTextView.swift; sourceTree = "<group>"; };
1B45CDD123C00856001EB196 /* TextContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextContainer.swift; sourceTree = "<group>"; };
1B45CDD423C00927001EB196 /* EditorContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorContent.swift; sourceTree = "<group>"; };
1B45CDD623C00A0A001EB196 /* EditorAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorAttribute.swift; sourceTree = "<group>"; };
1B45CDD823C0484A001EB196 /* RichTextViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RichTextViewTests.swift; sourceTree = "<group>"; };
1B45CDDA23C04BE3001EB196 /* RichTextViewSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RichTextViewSnapshotTests.swift; sourceTree = "<group>"; };
1B45CDDC23C05340001EB196 /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -453,7 +451,6 @@
isa = PBXGroup;
children = (
1B45CDD423C00927001EB196 /* EditorContent.swift */,
1B45CDD623C00A0A001EB196 /* EditorAttribute.swift */,
1B45CDDE23C053FB001EB196 /* EditorContentIdentifying.swift */,
1B004EC623C1C287007893AA /* EditorView.swift */,
1BFFEF0B23C30D5200D2BA35 /* EditorContentView.swift */,
Expand Down Expand Up @@ -864,7 +861,6 @@
1B45CDE623C05942001EB196 /* AttachmentSize.swift in Sources */,
1B975AFB23CD3E9B00EC410C /* RendererView.swift in Sources */,
1B164F1023D1BE9900A0869A /* AttributesDecoding.swift in Sources */,
1B45CDD723C00A0A001EB196 /* EditorAttribute.swift in Sources */,
1B45CDC723BF14DF001EB196 /* NSAttributedString+Range.swift in Sources */,
1B45CDD223C00856001EB196 /* TextContainer.swift in Sources */,
1BBAC3CF23CD5A1B0088A1C8 /* UITextRangeExtensions.swift in Sources */,
Expand Down
42 changes: 42 additions & 0 deletions Proton/Sources/Attachment/Attachment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ open class Attachment: NSTextAttachment, BoundsObserving {
return updatedString
}

/// Attributed string representation of the `Attachment`. This can be used directly to replace a range of text in `EditorView`
/// ### Usage Example ###
/// ```
/// let attachment = Attachment(PanelView(), size: .fullWidth)
/// let attrString = NSMutableAttributedString(string: "This is a test string")
/// attrString.append(attachment.string)
/// editor.attributedText = attrString
/// ```
public var string: NSAttributedString {
guard let isBlockAttachment = isBlockAttachment else { return NSAttributedString(string: "<UNKNOWN CONTENT TYPE>") }
// let key = isBlockAttachment == true ? NSAttributedString.Key.contentType: NSAttributedString.Key.inlineContentType
Expand All @@ -131,8 +139,12 @@ open class Attachment: NSTextAttachment, BoundsObserving {
}
}

/// `EditorView` containing this attachment
public private(set) var containerEditorView: EditorView?

/// Name of the content for the `EditorView`
/// - SeeAlso:
/// `EditorView`
public var containerContentName: EditorContent.Name? {
return containerEditorView?.contentName
}
Expand All @@ -141,6 +153,10 @@ open class Attachment: NSTextAttachment, BoundsObserving {
return containerEditorView?.richTextView
}

/// Causes invalidation of layout of the attachment when the containing view bounds are changed
/// - Parameter bounds: Updated bounds
/// - SeeAlso:
/// `BoundsObserving`
public func didChangeBounds(_ bounds: CGRect) {
invalidateLayout()
}
Expand All @@ -155,15 +171,21 @@ open class Attachment: NSTextAttachment, BoundsObserving {
}
}

/// Bounds of the container
public var containerBounds: CGRect? {
return containerTextView?.bounds
}

/// The bounds rectangle, which describes the attachment's location and size in its own coordinate system.
public override var bounds: CGRect {
didSet { view.bounds = bounds }
}

// This cannot be made convenience init as it prevents this being called from a class that inherits from `Attachment`
/// Initializes the attachment with the given content view
/// - Parameters:
/// - contentView: Content view to be hosted within the attachment
/// - size: Size rule for attachment
public init<AttachmentView: UIView & BlockContent>(_ contentView: AttachmentView, size: AttachmentSize) {
self.view = AttachmentContentView(name: contentView.name, frame: contentView.frame)
self.size = size
Expand All @@ -174,6 +196,10 @@ open class Attachment: NSTextAttachment, BoundsObserving {
}

// This cannot be made convenience init as it prevents this being called from a class that inherits from `Attachment`
/// Initializes the attachment with the given content view
/// - Parameters:
/// - contentView: Content view to be hosted within the attachment
/// - size: Size rule for attachment
public init<AttachmentView: UIView & InlineContent>(_ contentView: AttachmentView, size: AttachmentSize) {
self.view = AttachmentContentView(name: contentView.name, frame: contentView.frame)
self.size = size
Expand Down Expand Up @@ -231,6 +257,7 @@ open class Attachment: NSTextAttachment, BoundsObserving {
view.removeFromSuperview()
}

/// Removes this attachment from the `EditorView` it is contained in.
public func removeFromContainer() {
guard let containerTextView = containerTextView,
let range = containerTextView.attributedText.rangeFor(attachment: self) else {
Expand All @@ -239,14 +266,23 @@ open class Attachment: NSTextAttachment, BoundsObserving {
containerTextView.textStorage.replaceCharacters(in: range, with: "")
}

/// Range of this attachment in it's container
public func rangeInContainer() -> NSRange? {
return containerTextView?.attributedText.rangeFor(attachment: self)
}

/// Invoked when attributes are added in the containing `EditorView` in the range of string in which this attachment is contained.
/// - Parameters:
/// - range: Affected range
/// - attributes: Attributes applied
open func addedAttributesOnContainingRange(rangeInContainer range: NSRange, attributes: [NSAttributedString.Key: Any]) {

}

// Invoked when attributes are removed in the containing `EditorView` in the range of string in which this attachment is contained.
/// - Parameters:
/// - range: Affected range
/// - attributes: Attributes removed
open func removedAttributesFromContainingRange(rangeInContainer range: NSRange, attributes: [NSAttributedString.Key]) {

}
Expand All @@ -256,6 +292,12 @@ open class Attachment: NSTextAttachment, BoundsObserving {
fatalError("init(coder:) has not been implemented")
}

/// Returns the calculated bounds for the attachment based on size rule and content view provided during initialization.
/// - Parameters:
/// - textContainer: Text container for attachment
/// - lineFrag: Line fragment containing the attachment
/// - position: Position in the text container.
/// - charIndex: Character index
public override func attachmentBounds(for textContainer: NSTextContainer?, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect {
guard let textContainer = textContainer else { return .zero }

Expand Down
5 changes: 4 additions & 1 deletion Proton/Sources/Attachment/Focusable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@

import Foundation

/// Describes an object capable of gaining focus
/// Describes an object capable of gaining focus.
/// - Note:
/// If a content view in an `Attachment` is made `Focusable`, `setFocus` will automatically be called when the
/// attachment with a focusable view is added to the editor.
public protocol Focusable {
func setFocus()
}
3 changes: 3 additions & 0 deletions Proton/Sources/Base/NSAttributedString+ContentTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@ extension NSAttributedString.Key {
}

public extension NSAttributedString.Key {
/// Applying this attribute with value of `true` to a range of text makes that text non-focusable.
/// The content can still be deleted and selected but cursor cannot be moved to non-focusable range
/// using taps or mouse/keys (macOS Catalyst)
static let noFocus = NSAttributedString.Key("_noFocus")
}
33 changes: 0 additions & 33 deletions Proton/Sources/Editor/EditorAttribute.swift

This file was deleted.

13 changes: 8 additions & 5 deletions Proton/Sources/Editor/EditorContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,17 @@ public struct EditorContent {
}

public extension EditorContent {

/// Name for the content within the Editor. All the content (text and attachments) must have
/// a name. By default, text contained in Editor is considered a paragraph.
struct Name: Hashable, Equatable, RawRepresentable {
public var rawValue: String

public static let paragraph = Name("paragraph")
public static let viewOnly = Name("viewOnly")
public static let newline = Name("newline")
public static let text = Name("text")
public static let unknown = Name("unknown")
public static let paragraph = Name("_paragraph")
public static let viewOnly = Name("_viewOnly")
public static let newline = Name("_newline")
public static let text = Name("_text")
public static let unknown = Name("_unknown")

public init(rawValue: String) {
self.rawValue = rawValue
Expand Down
4 changes: 4 additions & 0 deletions Proton/Sources/Editor/EditorContentIdentifying.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public protocol EditorContentIdentifying {
// Convenience type for a UIView that can be placed within the Editor as the content of an `Attachment`
typealias AttachmentView = UIView & EditorContentIdentifying

/// Block type content hosted within the editor. Any attachment containing `BlockContent` will have a new line character appended
/// after the attachment on insertion.
public protocol BlockContent: EditorContentIdentifying { }

/// Inline content hosted within the editor. Any attachment containing `InlineContent` will have a whitespace character appended
/// after the attachment on insertion.
public protocol InlineContent: EditorContentIdentifying { }
3 changes: 3 additions & 0 deletions Proton/Sources/Editor/EditorContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
import Foundation
import UIKit

/// Describes a view contained in `Attachment` that contains a single `EditorView`.
/// This is a helper protocol that can be applied to the view so that
/// basic properties and functions are made available on the view as passthrough.
public protocol EditorContentView: Focusable {
var editor: EditorView { get }

Expand Down
36 changes: 36 additions & 0 deletions Proton/Sources/Editor/EditorViewDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,48 @@

import Foundation

/// Describes an object interested in listening to events raised from EditorView
public protocol EditorViewDelegate: AnyObject {

/// Invoked when a special key like `enter`, `tab` etc. is intercepted in the `Editor`
/// - Parameters:
/// - editor: Editor view receiving the event.
/// - key: Key that is intercepted.
/// - range: Range of the key in editor
/// - handled: Set to `true` to hijack the key press i.e. when `true`, the key press is not passed to the `Editor`
func editor(_ editor: EditorView, didReceiveKey key: EditorKey, at range: NSRange, handled: inout Bool)

/// Invoked when editor receives focus.
/// - Parameters:
/// - editor: Editor view receiving the event.
/// - range: Range where focus is received.
func editor(_ editor: EditorView, didReceiveFocusAt range: NSRange)

/// Invoked when editor loses the focus.
/// - Parameters:
/// - editor: Editor view receiving the event.
/// - range: Range from where focus is lost.
func editor(_ editor: EditorView, didLoseFocusFrom range: NSRange)

/// Invoked when text is changed in editor.
/// - Parameters:
/// - editor: Editor view receiving the event.
/// - range: Range where text is modified.
func editor(_ editor: EditorView, didChangeTextAt range: NSRange)

/// Invoked when the selection range changes in the editor as a result of moving the cursor using keys/mouse or taps.
/// - Parameters:
/// - editor: Editor view receiving the event.
/// - range: Range where selection is changed.
/// - attributes: Attributes at the updated range.
/// - contentType: Name of the content at the updated range.
func editor(_ editor: EditorView, didChangeSelectionAt range: NSRange, attributes: [NSAttributedString.Key: Any], contentType: EditorContent.Name)

/// Invoked when text processors are executed in the editor.
/// - Parameters:
/// - editor: Editor view receiving the event.
/// - processors: Processors that are executed.
/// - range: Range where processors are executed.
func editor(_ editor: EditorView, didExecuteProcessors processors: [TextProcessing], at range: NSRange)
}

Expand Down
1 change: 1 addition & 0 deletions Proton/Sources/EditorCommand/BoldCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import Foundation
import UIKit

/// Editor command that toggles Bold attribute to the selected range in the Editor.
public class BoldCommand: FontTraitToggleCommand {
public init() {
super.init(name: CommandName("_BoldCommand"), trait: .traitBold)
Expand Down
2 changes: 1 addition & 1 deletion Proton/Sources/EditorCommand/EditorCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ public protocol EditorCommand: AnyObject {

public extension EditorCommand {
func canExecute(on editor: EditorView) -> Bool {
return editor.registeredCommands?.contains { $0 === self } ?? true
return editor.registeredCommands?.contains { $0.name == self.name } ?? true
}
}
1 change: 1 addition & 0 deletions Proton/Sources/EditorCommand/FontTraitToggleCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import Foundation
import UIKit

/// Editor command that toggles given font trait to the selected range in the Editor.
public class FontTraitToggleCommand: EditorCommand {
public let trait: UIFontDescriptor.SymbolicTraits

Expand Down
1 change: 1 addition & 0 deletions Proton/Sources/EditorCommand/ItalicsCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import Foundation
import UIKit

/// Editor command that toggles Italics attribute to the selected range in the Editor.
public class ItalicsCommand: FontTraitToggleCommand {
public init() {
super.init(name: CommandName("_ItalicsCommand"), trait: .traitItalic)
Expand Down
Loading

0 comments on commit 716e8a4

Please sign in to comment.