Skip to content

Commit 0a76020

Browse files
committed
Implement dynamic indent for lists.
1 parent 14e27b8 commit 0a76020

4 files changed

Lines changed: 74 additions & 63 deletions

File tree

Aztec/Classes/Constants/Metrics.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ public enum Metrics {
88

99
public static var defaultIndentation = CGFloat(12)
1010
public static var maxIndentation = CGFloat(200)
11-
public static var listTextIndentation = CGFloat(36)
12-
public static var tabStepInterval = 8
11+
public static var listTextIndentation = CGFloat(16)
12+
public static var listTextCharIndentation = CGFloat(8)
13+
public static var tabStepInterval = 4
1314
public static var tabStepCount = 12
1415
public static var paragraphSpacing = CGFloat(6)
1516
}

Aztec/Classes/TextKit/LayoutManager.swift

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ private extension LayoutManager {
6767

6868
enumerateLineFragments(forGlyphRange: blockquoteGlyphRange) { (rect, usedRect, textContainer, glyphRange, stop) in
6969

70-
let startIndent = paragraphStyle.blockquoteIndent
70+
let startIndent = paragraphStyle.indentToFirst(Blockquote.self) - Metrics.listTextIndentation
7171

7272
let lineRange = self.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil)
7373
let lineCharacters = textStorage.attributedSubstring(from: lineRange).string
@@ -78,13 +78,12 @@ private extension LayoutManager {
7878

7979
let nestDepth = paragraphStyle.blockquoteNestDepth
8080
for index in 0...nestDepth {
81-
let indent = startIndent + CGFloat(index) * Metrics.listTextIndentation
81+
let indent = paragraphStyle.indent(to: index, of: Blockquote.self) - Metrics.listTextIndentation
8282

83-
let nestRect = self.blockquoteRect(origin: origin, lineRect: rect, blockquoteIndent: indent, lineEndsParagraph: lineEndsParagraph)
83+
let nestRect = self.blockquoteRect(origin: origin, lineRect: rect, blockquoteIndent: indent, lineEndsParagraph: lineEndsParagraph)
8484

85-
self.drawBlockquoteBorder(in: nestRect.integral, with: context, at: index)
85+
self.drawBlockquoteBorder(in: nestRect.integral, with: context, at: index)
8686
}
87-
8887

8988
}
9089
}
@@ -100,7 +99,7 @@ private extension LayoutManager {
10099
return
101100
}
102101

103-
let extraIndent = paragraphStyle.blockquoteIndent
102+
let extraIndent = paragraphStyle.indentToLast(Blockquote.self)
104103
let extraRect = blockquoteRect(origin: origin, lineRect: extraLineFragmentRect, blockquoteIndent: extraIndent, lineEndsParagraph: false)
105104

106105
drawBlockquoteBackground(in: extraRect.integral, with: context)
@@ -121,11 +120,7 @@ private extension LayoutManager {
121120
private func blockquoteRect(origin: CGPoint, lineRect: CGRect, blockquoteIndent: CGFloat, lineEndsParagraph: Bool) -> CGRect {
122121
var blockquoteRect = lineRect.offsetBy(dx: origin.x, dy: origin.y)
123122

124-
guard blockquoteIndent != 0 else {
125-
return blockquoteRect
126-
}
127-
128-
let paddingWidth = Metrics.listTextIndentation * 0.5 + blockquoteIndent
123+
let paddingWidth = blockquoteIndent
129124
blockquoteRect.origin.x += paddingWidth
130125
blockquoteRect.size.width -= paddingWidth
131126

@@ -284,16 +279,16 @@ private extension LayoutManager {
284279
/// - location: Text Location that should get the marker rendered.
285280
///
286281
private func drawItem(_ markerText: String, in rect: CGRect, styled paragraphAttributes: [NSAttributedString.Key: Any], at location: Int) {
287-
guard let style = paragraphAttributes[.paragraphStyle] as? NSParagraphStyle else {
282+
guard let style = paragraphAttributes[.paragraphStyle] as? ParagraphStyle else {
288283
return
289284
}
290285
let markerAttributes = markerAttributesBasedOnParagraph(attributes: paragraphAttributes)
291286
let markerAttributedText = NSAttributedString(string: markerText, attributes: markerAttributes)
292287

293288
var yOffset = CGFloat(0)
294289
var xOffset = CGFloat(0)
295-
let indentWidth = style.firstLineHeadIndent
296-
let markerWidth = markerAttributedText.size().width
290+
let indentWidth = style.indentToLast(TextList.self)
291+
let markerWidth = markerAttributedText.size().width * 1.5
297292

298293
if location > 0 {
299294
yOffset += style.paragraphSpacingBefore
@@ -315,10 +310,7 @@ private extension LayoutManager {
315310
///
316311
private func markerAttributesBasedOnParagraph(attributes: [NSAttributedString.Key: Any]) -> [NSAttributedString.Key: Any] {
317312
var resultAttributes = attributes
318-
var indent: CGFloat = 0
319-
if let style = attributes[.paragraphStyle] as? ParagraphStyle {
320-
indent = style.listIndent + Metrics.listTextIndentation - (Metrics.listTextIndentation / 4)
321-
}
313+
let indent: CGFloat = CGFloat(Metrics.tabStepInterval)
322314

323315
resultAttributes[.paragraphStyle] = markerParagraphStyle(indent: indent)
324316
resultAttributes.removeValue(forKey: .underlineStyle)
@@ -336,10 +328,11 @@ private extension LayoutManager {
336328
/// Returns the Marker Paragraph Attributes
337329
///
338330
private func markerParagraphStyle(indent: CGFloat) -> NSParagraphStyle {
339-
let tabStop = NSTextTab(textAlignment: .right, location: indent, options: [:])
331+
340332
let paragraphStyle = NSMutableParagraphStyle()
341-
342-
paragraphStyle.tabStops = [tabStop]
333+
paragraphStyle.alignment = .right
334+
paragraphStyle.tailIndent = -indent
335+
paragraphStyle.lineBreakMode = .byClipping
343336

344337
return paragraphStyle
345338
}

Aztec/Classes/TextKit/ParagraphProperty/TextList.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ open class TextList: ParagraphProperty {
1616

1717
func markerText(forItemNumber number: Int) -> String {
1818
switch self {
19-
case .ordered: return "\t\(number).\t"
20-
case .unordered: return "\t\u{2022}"
19+
case .ordered: return "\(number)."
20+
case .unordered: return "\u{2022}"
2121
}
2222
}
2323
}

Aztec/Classes/TextKit/ParagraphStyle.swift

Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ open class ParagraphStyle: NSMutableParagraphStyle, CustomReflectable {
140140

141141
open override var headIndent: CGFloat {
142142
get {
143-
let extra: CGFloat = (CGFloat(lists.count + blockquotes.count) * Metrics.listTextIndentation)
143+
let extra: CGFloat = (CGFloat(blockquotes.count) * Metrics.listTextIndentation) + listIndent
144144

145145
return baseHeadIndent + extra
146146
}
@@ -152,7 +152,8 @@ open class ParagraphStyle: NSMutableParagraphStyle, CustomReflectable {
152152

153153
open override var firstLineHeadIndent: CGFloat {
154154
get {
155-
let extra: CGFloat = (CGFloat(lists.count + blockquotes.count) * Metrics.listTextIndentation)
155+
156+
let extra: CGFloat = (CGFloat(blockquotes.count) * Metrics.listTextIndentation) + listIndent
156157

157158
return baseFirstLineHeadIndent + extra
158159
}
@@ -175,57 +176,73 @@ open class ParagraphStyle: NSMutableParagraphStyle, CustomReflectable {
175176
super.tailIndent = newValue
176177
}
177178
}
178-
179-
/// The level of indent to start the blockquote of the paragraph. Includes list indentation.
180-
/// Handles whether blockquote is outside or insdie of a list.
181-
public var blockquoteIndent: CGFloat {
182-
let listAndBlockquotes = properties.filter({ property in
183-
return property is Blockquote || property is TextList
184-
})
185-
if listAndBlockquotes.first is Blockquote {
179+
180+
/// Calculates the indentation of the paragraph up, for up to a certain depth of nesting of the type provided
181+
/// - Parameters:
182+
/// - depth: the depth up to check
183+
/// - type: the type to check
184+
public func indent<T : ParagraphProperty>(to depth: Int, of type: T.Type) -> CGFloat {
185+
var position = -1
186+
var currentDepth = -1
187+
for property in properties {
188+
position += 1
189+
if property is T {
190+
currentDepth += 1
191+
}
192+
if depth == currentDepth {
193+
break
194+
}
195+
}
196+
if position == -1 || currentDepth == -1 {
186197
return 0
187198
}
188-
var depth = 0
189-
for position in (0..<listAndBlockquotes.count).reversed() {
190-
if listAndBlockquotes[position] is Blockquote {
191-
depth = position
192-
break
199+
return indent(through: position)
200+
}
201+
202+
/// Calculates the indentation of the paragraph up to the fist of property of the type
203+
/// - Parameter type: the type to check
204+
public func indentToFirst<T : ParagraphProperty>(_ type: T.Type) -> CGFloat {
205+
let depth = properties.firstIndex(where: {$0 is T}) ?? 0
206+
return indent(through: depth)
207+
}
208+
209+
/// Calculates the indentation of the paragraph up the last property of the type specified
210+
/// - Parameter type: the paragraph property type to check
211+
public func indentToLast<T : ParagraphProperty>(_ type: T.Type) -> CGFloat {
212+
let depth = properties.lastIndex(where: {$0 is T}) ?? 0
213+
return indent(through: depth)
214+
}
215+
216+
/// Calculates the level of indent up to a certain depth
217+
private func indent(through depth: Int) -> CGFloat {
218+
let totalIndent = properties.prefix(through: depth).reduce(CGFloat(0)) { (total, property) in
219+
if let list = property as? TextList {
220+
return total + indent(for: list)
221+
} else if property is Blockquote {
222+
return total + Metrics.listTextIndentation
193223
}
224+
return total
194225
}
195-
return CGFloat(depth) * Metrics.listTextIndentation
226+
return totalIndent
196227
}
197228

198229
/// The level of depth for the nested blockquote of the paragraph. Excludes list indentation.
199230
///
200231
public var blockquoteNestDepth: Int {
201-
let listAndBlockquotes = properties.filter({ property in
202-
return property is Blockquote
203-
})
204-
var depth = 0
205-
for position in (0..<listAndBlockquotes.count).reversed() {
206-
if listAndBlockquotes[position] is Blockquote {
207-
depth = position
208-
break
209-
}
210-
}
211-
return depth
232+
return max(0, blockquotes.count - 1)
212233
}
213234

235+
private func indent(for list: TextList) -> CGFloat {
236+
let markerSize = CGFloat(list.style.markerText(forItemNumber: list.start ?? 1).count)
237+
return Metrics.listTextIndentation + (markerSize * Metrics.listTextCharIndentation)
238+
}
214239
/// The amount of indent for the list of the paragraph if any.
215240
///
216241
public var listIndent: CGFloat {
217-
let listAndBlockquotes = properties.filter({ property in
218-
return property is Blockquote || property is TextList
219-
})
220-
var depth = 0
221-
for position in (0..<listAndBlockquotes.count).reversed() {
222-
if listAndBlockquotes[position] is TextList {
223-
depth = position
224-
break
225-
}
242+
let listIndent: CGFloat = lists.reduce(0) { (total, list) in
243+
return total + indent(for: list)
226244
}
227-
228-
return CGFloat(depth) * Metrics.listTextIndentation
245+
return listIndent
229246
}
230247

231248
open var baseHeadIndent: CGFloat = 0

0 commit comments

Comments
 (0)