Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
26 changes: 2 additions & 24 deletions Aztec/Classes/Formatters/AttributeFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ protocol AttributeFormatter {
func applicationRange(for range: NSRange, in text: NSAttributedString) -> NSRange

func worksInEmptyRange() -> Bool

func needsEmptyLinePlaceholder() -> Bool
}


Expand Down Expand Up @@ -120,15 +118,9 @@ extension AttributeFormatter {
///
@discardableResult
func applyAttributes(to text: NSMutableAttributedString, at range: NSRange) -> NSRange {
var rangeToApply = applicationRange(for: range, in: text)

if needsEmptyLinePlaceholder() && worksInEmptyRange() && ( rangeToApply.length == 0 || text.length == 0) {
let placeholder = placeholderForEmptyLine(using: placeholderAttributes)
text.insert(placeholder, at: rangeToApply.location)
rangeToApply = NSMakeRange(rangeToApply.location, placeholder.length)
}
let rangeToApply = applicationRange(for: range, in: text)

text.enumerateAttributes(in: rangeToApply, options: []) { (attributes, range, stop) in
text.enumerateAttributes(in: rangeToApply, options: []) { (attributes, range, _) in
let currentAttributes = text.attributes(at: range.location, effectiveRange: nil)
let attributes = apply(to: currentAttributes)
text.addAttributes(attributes, range: range)
Expand Down Expand Up @@ -180,12 +172,6 @@ extension AttributeFormatter {
//
private extension AttributeFormatter {

/// The string to be used when adding attributes to an empty line.
///
func placeholderForEmptyLine(using attributes: [String: Any]?) -> NSAttributedString {
return VisualOnlyElementFactory().zeroWidthSpace(inheritingAttributes: attributes)
}

/// Helper that indicates whether if we should format the specified range, or not.
/// - Note: For convenience reasons, whenever the Text is empty, this helper will return *true*.
///
Expand Down Expand Up @@ -215,10 +201,6 @@ extension CharacterAttributeFormatter {
func worksInEmptyRange() -> Bool {
return false
}

func needsEmptyLinePlaceholder() -> Bool {
return false
}
}


Expand All @@ -236,8 +218,4 @@ extension ParagraphAttributeFormatter {
func worksInEmptyRange() -> Bool {
return true
}

func needsEmptyLinePlaceholder() -> Bool {
return true
}
}
4 changes: 0 additions & 4 deletions Aztec/Classes/Formatters/BlockquoteFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,5 @@ class BlockquoteFormatter: ParagraphAttributeFormatter {
let style = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle
return style?.blockquote != nil
}

func needsEmptyLinePlaceholder() -> Bool {
return false
}
}

19 changes: 15 additions & 4 deletions Aztec/Classes/Formatters/HeaderFormatter.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import Foundation
import UIKit


// MARK: - Header Formatter
//
open class HeaderFormatter: ParagraphAttributeFormatter {

/// Available Heading Types
///
public enum HeaderType: Int {
case none = 0
case h1 = 1
Expand Down Expand Up @@ -37,15 +42,25 @@ open class HeaderFormatter: ParagraphAttributeFormatter {
}
}

/// Heading Level of this formatter
///
let headerLevel: HeaderType

/// Attributes to be added by default
///
let placeholderAttributes: [String : Any]?


/// Designated Initializer
///
init(headerLevel: HeaderType = .h1, placeholderAttributes: [String : Any]? = nil) {
self.headerLevel = headerLevel
self.placeholderAttributes = placeholderAttributes
}


// MARK: - Overwriten Methods

func apply(to attributes: [String : Any]) -> [String: Any] {
var resultingAttributes = attributes
let newParagraphStyle = ParagraphStyle()
Expand Down Expand Up @@ -97,9 +112,5 @@ open class HeaderFormatter: ParagraphAttributeFormatter {
}
return false
}

func needsEmptyLinePlaceholder() -> Bool {
return false
}
}

21 changes: 16 additions & 5 deletions Aztec/Classes/Formatters/PreFormatter.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
import Foundation
import UIKit


// MARK: - Pre Formatter
//
open class PreFormatter: ParagraphAttributeFormatter {

/// Font to be used
///
let monospaceFont: UIFont

/// Attributes to be added by default
///
let placeholderAttributes: [String : Any]?


/// Designated Initializer
///
init(monospaceFont: UIFont = UIFont(descriptor:UIFontDescriptor(name: "Courier", size: 12), size:12), placeholderAttributes: [String : Any]? = nil) {
self.monospaceFont = monospaceFont
self.placeholderAttributes = placeholderAttributes
}


// MARK: - Overwriten Methods

func apply(to attributes: [String : Any]) -> [String: Any] {
var resultingAttributes = attributes
let newParagraphStyle = ParagraphStyle()
Expand All @@ -35,10 +49,7 @@ open class PreFormatter: ParagraphAttributeFormatter {
}

func present(in attributes: [String : Any]) -> Bool {
if let font = attributes[NSFontAttributeName] as? UIFont {
return font == monospaceFont
}
return false
let font = attributes[NSFontAttributeName] as? UIFont
return font == monospaceFont
}
}

4 changes: 0 additions & 4 deletions Aztec/Classes/Formatters/TextListFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ class TextListFormatter: ParagraphAttributeFormatter {
return list.style == listStyle
}

func needsEmptyLinePlaceholder() -> Bool {
return false
}

static func listsOfAnyKindPresent(in attributes: [String: Any]) -> Bool {
let style = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle
return style?.textList != nil
Expand Down
44 changes: 32 additions & 12 deletions Aztec/Classes/TextKit/TextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -215,12 +215,12 @@ open class TextView: UITextView {
// MARK: - Overwritten Properties

/// Overwrites Typing Attributes:
/// This is the (only) valid hook we've found, in order to (selectively) remove the Blockquote/List attributes.
/// This is the (only) valid hook we've found, in order to (selectively) remove the [Blockquote, List, Pre] attributes.
/// For details, see: https://github.com/wordpress-mobile/AztecEditor-iOS/issues/414
///
override open var typingAttributes: [String: Any] {
get {
let updatedAttributes = ensureRemovalOfListAndBlockquoteAttribute(from: super.typingAttributes)
let updatedAttributes = ensureRemovalOfParagraphAttributes(from: super.typingAttributes)
super.typingAttributes = updatedAttributes

return updatedAttributes
Expand Down Expand Up @@ -288,7 +288,7 @@ open class TextView: UITextView {

open override func insertText(_ text: String) {

/// Whenever the user is at the end of the document, while editing a [List, Blockquote], we'll need
/// Whenever the user is at the end of the document, while editing a [List, Blockquote, Pre], we'll need
/// to insert a `\n` character, so that the Layout Manager immediately renders the List's new bullet
/// (or Blockquote's BG).
///
Expand Down Expand Up @@ -576,6 +576,23 @@ open class TextView: UITextView {
toggle(formatter: formatter, atRange: range)
}

/// Adds or removes a Pre style from the specified range.
/// Pre are applied to an entire paragrah regardless of the range.
/// If the range spans multiple paragraphs, the style is applied to all
/// affected paragraphs.
///
/// - Parameters:
/// - range: The NSRange to edit.
///
open func togglePre(range: NSRange) {
ensureInsertionOfNewlineWhenEditingEdgeOfTheDocument()

let formatter = PreFormatter(placeholderAttributes: typingAttributes)
toggle(formatter: formatter, atRange: range)

forceRedrawCursorAfterDelay()
}

/// Adds or removes a blockquote style from the specified range.
/// Blockquotes are applied to an entire paragrah regardless of the range.
/// If the range spans multiple paragraphs, the style is applied to all
Expand Down Expand Up @@ -739,16 +756,17 @@ open class TextView: UITextView {
///
/// - Returns: True if ParagraphAttributes were removed. False otherwise!
///
func ensureRemovalOfParagraphAttributes(insertedText text: String, at range: NSRange) -> Bool {
private func ensureRemovalOfParagraphAttributes(insertedText text: String, at range: NSRange) -> Bool {

guard shouldRemoveParagraphAttributes(insertedText: text, at: range.location) else {
return false
}

let formatters:[AttributeFormatter] = [
let formatters: [AttributeFormatter] = [
TextListFormatter(style: .ordered),
TextListFormatter(style: .unordered),
BlockquoteFormatter()
BlockquoteFormatter(),
PreFormatter(placeholderAttributes: self.defaultAttributes)
]

let atEdgeOfDocument = range.location >= storage.length
Expand All @@ -773,10 +791,10 @@ open class TextView: UITextView {
///
/// A. The selected location is at the very end of the document
/// B. The previous character is a '\n'
/// C. There's a list (OR) blockquote.
/// C. There's a List (OR) Blockquote (OR) Pre.
///
/// This is necessary because when the caret is at EOF, and the previous `\n` character has
/// a [List, Blockquote] styles, that style will remain in the `typingAttributes`. We'll only
/// a [List, Blockquote, Pre] styles, that style will remain in the `typingAttributes`. We'll only
/// allow the style to remain if there are contents in the current line with the textList style
/// (in which case this condition won't ever trigger because we'll either no longer be at EOF,
/// or the previous character won't be `\n`).
Expand All @@ -785,7 +803,7 @@ open class TextView: UITextView {
///
/// - Returns: Updated Typing Attributes.
///
private func ensureRemovalOfListAndBlockquoteAttribute(from typingAttributes: [String: Any]) -> [String: Any] {
private func ensureRemovalOfParagraphAttributes(from typingAttributes: [String: Any]) -> [String: Any] {
guard selectedRange.location == storage.length else {
return typingAttributes
}
Expand All @@ -797,9 +815,10 @@ open class TextView: UITextView {
}

let formatters: [AttributeFormatter] = [
BlockquoteFormatter(),
PreFormatter(placeholderAttributes: self.defaultAttributes),
TextListFormatter(style: .ordered),
TextListFormatter(style: .unordered),
BlockquoteFormatter()
TextListFormatter(style: .unordered)
]

for formatter in formatters where formatter.present(in: typingAttributes) {
Expand All @@ -825,7 +844,7 @@ open class TextView: UITextView {
///
/// A. We're about to insert a new line
/// B. We're at the end of the document
/// C. There's a List (OR) Blockquote active
/// C. There's a List (OR) Blockquote (OR) Pre active
///
/// We're doing this as a workaround, in order to force the LayoutManager render the Bullet (OR)
/// Blockquote's background.
Expand All @@ -841,6 +860,7 @@ open class TextView: UITextView {

let formatters: [AttributeFormatter] = [
BlockquoteFormatter(),
PreFormatter(placeholderAttributes: self.defaultAttributes),
TextListFormatter(style: .ordered),
TextListFormatter(style: .unordered)
]
Expand Down
Loading