Skip to content

Commit 9099e93

Browse files
committed
Normalize lineHeightMultiple handling via stLineHeightMultiple helper method
Introduced a stLineHeightMultiple computed property on NSParagraphStyle to consistently treat zero or unset lineHeightMultiple values as 1.0. Updated rendering and layout logic across AppKit and UIKit platforms to use this normalization, reducing repetitive conditional checks and improving baseline alignment calculations. Updated README example lineHeight setting to 120%. Fixes #85
1 parent ca2675e commit 9099e93

File tree

9 files changed

+55
-33
lines changed

9 files changed

+55
-33
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## [2.0.5] - 2025-04-19
4+
5+
### Changed
6+
- Normalize lineHeightMultiple handling via stLineHeightMultiple helper method
7+
- Improve scroll view styling and visibility in STCompletionViewController
8+
- Make STCompletionViewController layout resizable and set default window size
9+
- Add GitHub issue template configuration to disable blank issues
10+
- Add Makefile with targets for changelog generation and help message
11+
- Update changelog for versions 2.0.3 and 2.0.4, adjust git-cliff config and add mise setup
12+
313
## [2.0.4] - 2025-03-30
414

515
### Changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ The text view can be customized in a variety of ways.
132132

133133
```swift
134134
let paragraph = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
135-
// Set the line-height to 110%
136-
paragraph.lineHeightMultiple = 1.1
135+
// Set the line-height to 120%
136+
paragraph.lineHeightMultiple = 1.2
137137
paragraph.defaultTabInterval = 28
138138

139139
// Default Paragraph style

Sources/STTextViewAppKit/STTextLayoutFragment.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,8 @@ final class STTextLayoutFragment: NSTextLayoutFragment {
6666
paragraphStyle = self.defaultParagraphStyle
6767
}
6868

69-
if !paragraphStyle.lineHeightMultiple.isAlmostZero() {
70-
let offset = -(lineFragment.typographicBounds.height * (paragraphStyle.lineHeightMultiple - 1.0) / 2)
71-
lineFragment.draw(at: point.moved(dx: lineFragment.typographicBounds.origin.x, dy: lineFragment.typographicBounds.origin.y + offset), in: context)
72-
} else {
73-
lineFragment.draw(at: lineFragment.typographicBounds.origin, in: context)
74-
}
69+
let offset = -(lineFragment.typographicBounds.height * (paragraphStyle.stLineHeightMultiple - 1.0) / 2)
70+
lineFragment.draw(at: point.moved(dx: lineFragment.typographicBounds.origin.x, dy: lineFragment.typographicBounds.origin.y + offset), in: context)
7571
}
7672

7773
#if USE_FONT_SMOOTHING_STYLE

Sources/STTextViewAppKit/STTextView+Gutter.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ extension STTextView {
8585
// Calculations in sync with position used by STTextLayoutFragment
8686
let ctNumberLine = CTLineCreateWithAttributedString(NSAttributedString(string: "\(lineNumber)", attributes: lineTextAttributes))
8787
let baselineParagraphStyle = typingAttributes[.paragraphStyle] as? NSParagraphStyle ?? defaultParagraphStyle
88-
let baselineOffset = -(ctNumberLine.typographicHeight() * (baselineParagraphStyle.lineHeightMultiple - 1.0) / 2)
88+
let baselineOffset = -(ctNumberLine.typographicHeight() * (baselineParagraphStyle.stLineHeightMultiple - 1.0) / 2)
8989

9090
var effectiveLineTextAttributes = lineTextAttributes
9191
if gutterView.highlightSelectedLine/*, isLineSelected*/, !selectedLineTextAttributes.isEmpty {
@@ -168,8 +168,8 @@ extension STTextView {
168168
if !textLineFragment.isExtraLineFragment {
169169
locationForFirstCharacter = textLineFragment.locationForCharacter(at: 0)
170170

171-
if let paragraphStyle = textLineFragment.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle, !paragraphStyle.lineHeightMultiple.isAlmostZero() {
172-
baselineYOffset = -(textLineFragment.typographicBounds.height * (paragraphStyle.lineHeightMultiple - 1.0) / 2)
171+
if let paragraphStyle = textLineFragment.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle {
172+
baselineYOffset = -(textLineFragment.typographicBounds.height * (paragraphStyle.stLineHeightMultiple - 1.0) / 2)
173173
}
174174

175175
lineFragmentFrame = CGRect(
@@ -188,8 +188,8 @@ extension STTextView {
188188
let prevTextLineFragment = layoutFragment.textLineFragments[layoutFragment.textLineFragments.count - 2]
189189
locationForFirstCharacter = prevTextLineFragment.locationForCharacter(at: 0)
190190

191-
if let paragraphStyle = prevTextLineFragment.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle, !paragraphStyle.lineHeightMultiple.isAlmostZero() {
192-
baselineYOffset = -(prevTextLineFragment.typographicBounds.height * (paragraphStyle.lineHeightMultiple - 1.0) / 2)
191+
if let paragraphStyle = prevTextLineFragment.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle {
192+
baselineYOffset = -(prevTextLineFragment.typographicBounds.height * (paragraphStyle.stLineHeightMultiple - 1.0) / 2)
193193
}
194194

195195
lineFragmentFrame = CGRect(
@@ -206,8 +206,8 @@ extension STTextView {
206206
} else {
207207
locationForFirstCharacter = textLineFragment.locationForCharacter(at: 0)
208208

209-
if let paragraphStyle = textLineFragment.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle, !paragraphStyle.lineHeightMultiple.isAlmostZero() {
210-
baselineYOffset = -(textLineFragment.typographicBounds.height * (paragraphStyle.lineHeightMultiple - 1.0) / 2)
209+
if let paragraphStyle = textLineFragment.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle {
210+
baselineYOffset = -(textLineFragment.typographicBounds.height * (paragraphStyle.stLineHeightMultiple - 1.0) / 2)
211211
}
212212

213213
lineFragmentFrame = CGRect(

Sources/STTextViewAppKit/STTextView.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,7 @@ import AVFoundation
229229
internal var typingLineHeight: CGFloat {
230230
let font = typingAttributes[.font] as? NSFont ?? _defaultTypingAttributes[.font] as! NSFont
231231
let paragraphStyle = typingAttributes[.paragraphStyle] as? NSParagraphStyle ?? self._defaultTypingAttributes[.paragraphStyle] as! NSParagraphStyle
232-
let lineHeightMultiple = paragraphStyle.lineHeightMultiple.isAlmostZero() ? 1.0 : paragraphStyle.lineHeightMultiple
233-
return calculateDefaultLineHeight(for: font) * lineHeightMultiple
232+
return calculateDefaultLineHeight(for: font) * paragraphStyle.stLineHeightMultiple
234233
}
235234

236235
/// The characters of the receiver’s text.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Created by Marcin Krzyzanowski
2+
// https://github.com/krzyzanowskim/STTextView/blob/main/LICENSE.md
3+
4+
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
5+
import AppKit
6+
#endif
7+
#if canImport(UIKit)
8+
import UIKit
9+
#endif
10+
11+
package extension NSParagraphStyle {
12+
13+
/// Normalized value for default paragraph style
14+
/// NSParagraphStyle.lineHeightMultiple means "default" multiple, that is 1.0
15+
var stLineHeightMultiple: CGFloat {
16+
if lineHeightMultiple.isAlmostZero() {
17+
return 1.0
18+
} else {
19+
return lineHeightMultiple
20+
}
21+
}
22+
}

Sources/STTextViewUIKit/STTextLayoutFragment.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,8 @@ final class STTextLayoutFragment: NSTextLayoutFragment {
6767
paragraphStyle = self.defaultParagraphStyle
6868
}
6969

70-
if !paragraphStyle.lineHeightMultiple.isAlmostZero() {
71-
let offset = -(lineFragment.typographicBounds.height * (paragraphStyle.lineHeightMultiple - 1.0) / 2)
72-
lineFragment.draw(at: point.moved(dx: lineFragment.typographicBounds.origin.x, dy: lineFragment.typographicBounds.origin.y + offset), in: context)
73-
} else {
74-
lineFragment.draw(at: lineFragment.typographicBounds.origin, in: context)
75-
}
70+
let offset = -(lineFragment.typographicBounds.height * (paragraphStyle.stLineHeightMultiple - 1.0) / 2)
71+
lineFragment.draw(at: point.moved(dx: lineFragment.typographicBounds.origin.x, dy: lineFragment.typographicBounds.origin.y + offset), in: context)
7672
}
7773

7874
#if USE_FONT_SMOOTHING_STYLE

Sources/STTextViewUIKit/STTextView+Gutter.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ extension STTextView {
7272
// Calculations in sync with position used by STTextLayoutFragment
7373
let ctNumberLine = CTLineCreateWithAttributedString(NSAttributedString(string: "\(lineNumber)", attributes: typingAttributes))
7474
let baselineParagraphStyle = typingAttributes[.paragraphStyle] as? NSParagraphStyle ?? defaultParagraphStyle
75-
let baselineOffset = -(ctNumberLine.typographicHeight() * (baselineParagraphStyle.lineHeightMultiple - 1.0) / 2)
75+
let baselineOffset = -(ctNumberLine.typographicHeight() * (baselineParagraphStyle.stLineHeightMultiple - 1.0) / 2)
7676

7777
var effectiveLineTextAttributes = lineTextAttributes
7878
if gutterView.highlightSelectedLine, !selectedLineTextAttributes.isEmpty {
@@ -155,8 +155,8 @@ extension STTextView {
155155
if !textLineFragment.isExtraLineFragment {
156156
locationForFirstCharacter = textLineFragment.locationForCharacter(at: 0)
157157

158-
if let paragraphStyle = textLineFragment.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle, !paragraphStyle.lineHeightMultiple.isAlmostZero() {
159-
baselineYOffset = -(textLineFragment.typographicBounds.height * (paragraphStyle.lineHeightMultiple - 1.0) / 2)
158+
if let paragraphStyle = textLineFragment.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle {
159+
baselineYOffset = -(textLineFragment.typographicBounds.height * (paragraphStyle.stLineHeightMultiple - 1.0) / 2)
160160
}
161161

162162
lineFragmentFrame = CGRect(
@@ -175,8 +175,8 @@ extension STTextView {
175175
let prevTextLineFragment = layoutFragment.textLineFragments[layoutFragment.textLineFragments.count - 2]
176176
locationForFirstCharacter = prevTextLineFragment.locationForCharacter(at: 0)
177177

178-
if let paragraphStyle = prevTextLineFragment.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle, !paragraphStyle.lineHeightMultiple.isAlmostZero() {
179-
baselineYOffset = -(prevTextLineFragment.typographicBounds.height * (paragraphStyle.lineHeightMultiple - 1.0) / 2)
178+
if let paragraphStyle = prevTextLineFragment.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle {
179+
baselineYOffset = -(prevTextLineFragment.typographicBounds.height * (paragraphStyle.stLineHeightMultiple - 1.0) / 2)
180180
}
181181

182182
lineFragmentFrame = CGRect(
@@ -193,8 +193,8 @@ extension STTextView {
193193
} else {
194194
locationForFirstCharacter = textLineFragment.locationForCharacter(at: 0)
195195

196-
if let paragraphStyle = textLineFragment.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle, !paragraphStyle.lineHeightMultiple.isAlmostZero() {
197-
baselineYOffset = -(textLineFragment.typographicBounds.height * (paragraphStyle.lineHeightMultiple - 1.0) / 2)
196+
if let paragraphStyle = textLineFragment.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle {
197+
baselineYOffset = -(textLineFragment.typographicBounds.height * (paragraphStyle.stLineHeightMultiple - 1.0) / 2)
198198
}
199199

200200
lineFragmentFrame = CGRect(
@@ -235,7 +235,7 @@ extension STTextView {
235235
width: max(lineFragmentFrame.intersection(gutterView.containerView.frame).width, gutterView.containerView.frame.width),
236236
height: lineFragmentFrame.size.height
237237
)
238-
)
238+
).pixelAligned
239239

240240
gutterView.containerView.addSubview(numberCell)
241241
requiredWidthFitText = max(requiredWidthFitText, numberCell.intrinsicContentSize.width)

Sources/STTextViewUIKit/STTextView.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,7 @@ import STTextViewCommon
418418
internal var typingLineHeight: CGFloat {
419419
let font = typingAttributes[.font] as? UIFont ?? _defaultTypingAttributes[.font] as! UIFont
420420
let paragraphStyle = typingAttributes[.paragraphStyle] as? NSParagraphStyle ?? _defaultTypingAttributes[.paragraphStyle] as! NSParagraphStyle
421-
let lineHeightMultiple = paragraphStyle.lineHeightMultiple.isAlmostZero() ? 1.0 : paragraphStyle.lineHeightMultiple
422-
return calculateDefaultLineHeight(for: font) * lineHeightMultiple
421+
return calculateDefaultLineHeight(for: font) * paragraphStyle.stLineHeightMultiple
423422
}
424423

425424
private let editableTextInteraction = UITextInteraction(for: .editable)

0 commit comments

Comments
 (0)