Skip to content

Commit

Permalink
Implemented bullet points.
Browse files Browse the repository at this point in the history
  • Loading branch information
will-lumley committed Feb 5, 2020
1 parent 6d0eab4 commit 23b5ff2
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 82 deletions.
8 changes: 4 additions & 4 deletions Example/Pods/Pods.xcodeproj/project.pbxproj

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 22 additions & 37 deletions RichEditor/Classes/RichEditor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class RichEditor: NSView
}

///The marker that will be used for bullet points
fileprivate var bulletPointMarker = NSTextList.MarkerFormat.circle
internal static var bulletPointMarker = "\u{00A0}" //NSTextList.MarkerFormat.circle

/**
Returns the FontStyling object that was derived from the selected text, or the future text if nothing is selected
Expand Down Expand Up @@ -134,31 +134,15 @@ extension RichEditor

public func startBulletPoints()
{
/*
NSAttributedString
NSAttributedStringParagraphStyle
NSTextList
*/
let currentLine = self.textView.currentLine()

//Create a text list (the representation of our bullet points)
let textList = NSTextList(markerFormat: self.bulletPointMarker, options: 0)
textList.startingItemNumber = 1
//Get the string that makes up our current string, and find out where it sits in our TextView
let currentLineStr = currentLine.lineString
let currentLineRange = currentLine.lineRange

//Create a new paragraph style, and apply our bullet points to it
let paragraph = NSMutableParagraphStyle()
paragraph.textLists = [textList]

//Create attributes with our paragraph style (which in turn has the bullet point style within it)
var typingAttributes = self.textView.typingAttributes
typingAttributes[NSAttributedString.Key.paragraphStyle] = paragraph

//Create an attributed string with the attributes already present in our 'future' text
//Put a \n before the bullet point, so that the bullet point is in it's own line
//Put a \t afte the bullet point so there's some space between the bullet point and the text
let attrStr = NSAttributedString(string: "\n\t\(textList.marker(forItemNumber: 1)) ", attributes: typingAttributes)
print("attrStr: \(attrStr)")

self.textView.textStorage?.append(attrStr)
//Get the line in our TextView that our caret is on, and prepend a bulletpoint to it
let bulletPointStr = "\(RichEditor.bulletPointMarker) \(currentLineStr)"
self.textView.replaceCharacters(in: currentLineRange, with: bulletPointStr)
}

/**
Expand Down Expand Up @@ -409,21 +393,22 @@ extension RichEditor: NSTextViewDelegate
if newString == "\n" {
let currentLine = textView.currentLine()

//If the line that we just hit enter on contains a bullet point marker
//TODO: Check for all decimal point markers
guard var currentLineRange = currentLine.1 else { return true }
guard let currentLineStr = currentLine.2 else { return true }
if currentLineStr.contains("") {
currentLineRange.length = currentLineRange.length + 2
//If the line we're currently on is prefixed with a bullet point, append a bullet point to the next line
let currentLineStr = currentLine.lineString
if currentLineStr.hasPrefix(RichEditor.bulletPointMarker) {

let attributedStr = NSMutableAttributedString(attributedString: textView.attributedString())
let currentLineRange = currentLine.lineRange

//Add another bullet point to this list of bullet points
guard let textList = textView.attributedString().textList(at: affectedCharRange) else { return true }

//Get the current line, and replace it with the current line AND a newline with a new bullet point ready to go
let newLine = NSAttributedString(string: "\(currentLineStr)\n\(textList.marker(forItemNumber: 2))", attributes: attributedStr.attributes)
attributedStr.replaceCharacters(in: currentLineRange, with: newLine)
//If our current line is just an empty bullet point line, remove the bullet point and turn it into a regular line
if currentLineStr == RichEditor.bulletPointMarker {
self.textView.replaceCharacters(in: currentLineRange, with: "")
}

//If our current line is a full bullet point line, append a brand spanking new bullet point line below our current line for our user
else {
let bulletPointStr = "\(currentLineStr)\n\(RichEditor.bulletPointMarker)"
self.textView.replaceCharacters(in: currentLineRange, with: bulletPointStr)
}

return false
}
Expand Down
27 changes: 0 additions & 27 deletions RichEditor/Extensions/NSAttributedString+Convenience.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,33 +115,6 @@ extension NSAttributedString
return attachments
}

/**
Finds the NSTextList at a given range
- parameter range: The NSRange that we'll search for the NSTextList in
- returns: An NSTextList, if one exists at the given range
*/
public func textList(at searchRange: NSRange) -> NSTextList?
{
var textList: NSTextList?

self.enumerateAttribute(.paragraphStyle, in: self.string.fullRange, options: .longestEffectiveRangeNotRequired, using: {(value, range, finished) in
if value != nil {
if let paragraphStyle = value as? NSParagraphStyle {

//TODO: Improve search to see if this textlist is the one we're after
if paragraphStyle.textLists.count >= 1 {
textList = paragraphStyle.textLists[0]

print("TextList Range: \(range)")
print("Search Range: \(searchRange)")
}
}
}
})

return textList
}

//MARK: - Attribute Checking
/**
Iterates over every font that exists within this NSAttributedString, and checks if any of the fonts contain the desired NSFontTraitMask
Expand Down
45 changes: 31 additions & 14 deletions RichEditor/Extensions/NSTextView+Convenience.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ extension NSTextView
return self.selectedRange().length > 0
}

public var caretLocation: Int {
return self.selectedRange().location
}

/**
Replaces the current NSString/NSAttributedString that is currently within
the NSTextView and replaces it with the provided HTML string.
Expand Down Expand Up @@ -53,6 +57,7 @@ extension NSTextView
- returns: A boolean value indicative of if the setting of the NSAttributedString
was successful
*/
@discardableResult
public func set(attributedString: NSAttributedString) -> Bool
{
guard let textStorage = self.textStorage else {
Expand Down Expand Up @@ -102,21 +107,18 @@ extension NSTextView

- returns: The line number that the caret is on, the range of our line, and the string that makes up that line of text
*/
func currentLine() -> (Int?, NSRange?, String?)
func currentLine() -> (lineNumber: Int, lineRange: NSRange, lineString: String)
{
//Bail out if the user has selected text
if self.hasSelectedText {
return (nil, nil, nil)
}

//The line number that we're currently iterating on
var lineNumber = 0

//The line number & line of text that we believe the caret to be on
var selectedLineNumber: Int?
var selectedLineRange : NSRange?
var selectedLineOfText: String?

var selectedLineNumber = 0
var selectedLineRange = NSRange(location: 0, length: 0)
var selectedLineOfText = ""

var foundSelectedLine = false

//Iterate over every line in our TextView
self.string.enumerateSubstrings(in: self.string.startIndex..<self.string.endIndex, options: .byLines) {(substring, substringRange, _, _) in
//The range of this current line
Expand All @@ -126,19 +128,34 @@ extension NSTextView
let startOfLine = range.location
let endOfLine = range.location + range.length

let caretLocation = self.selectedRange().location

//If the CaretLocation is between the start of this line, and the end of this line, we can assume that the caret is on this line
if caretLocation >= startOfLine && caretLocation <= endOfLine {
if self.caretLocation >= startOfLine && self.caretLocation <= endOfLine {
//Mark the line number
selectedLineNumber = lineNumber
selectedLineOfText = substring
selectedLineOfText = substring ?? ""
selectedLineRange = range

foundSelectedLine = true
}

lineNumber += 1
}

//If we're not at the starting point, and we didn't find a current line, then we're at the end of our TextView
if self.caretLocation > 0 && !foundSelectedLine {
selectedLineNumber = lineNumber
selectedLineOfText = ""
selectedLineRange = NSRange(location: self.caretLocation, length: 0)
}

return (selectedLineNumber, selectedLineRange, selectedLineOfText)
}

public func append(_ string: String)
{
let textViewText = NSMutableAttributedString(attributedString: self.attributedString())
textViewText.append(NSAttributedString(string: string, attributes: self.typingAttributes))

self.set(attributedString: textViewText)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import Foundation

extension String
{
public var isBulletPoint: Bool {
return self.hasPrefix(RichEditor.bulletPointMarker)
}

/**
Returns an array of strings that is made up of all the "lines" in this string.
- returns: An array of strings that is derived from this string, using a newline as a delimiter
Expand Down

0 comments on commit 23b5ff2

Please sign in to comment.