Skip to content

Commit 412b0a2

Browse files
Fix Gutter Position On Small Documents (#312)
### Description Fixes an issue where on small documents the text view / scroll view would get messed up when placing the gutter view relative to the top padding. This makes the y position relative to the text view's y position. ### Related Issues * N/A ### Checklist - [x] I read and understood the [contributing guide](https://github.com/CodeEditApp/CodeEdit/blob/main/CONTRIBUTING.md) as well as the [code of conduct](https://github.com/CodeEditApp/CodeEdit/blob/main/CODE_OF_CONDUCT.md) - [x] The issues this PR addresses are related to each other - [x] My changes generate no new warnings - [x] My code builds and runs on my machine - [x] My changes are all related to the related issue above - [x] I documented my code ### Screenshots Before, would only appear on small files (1-3 lines): ![Screenshot 2025-04-22 at 10 40 16 AM](https://github.com/user-attachments/assets/fbade1e5-200d-4e46-8b6a-bcd6145ec7b7) After: ![Screenshot 2025-04-22 at 10 36 08 AM](https://github.com/user-attachments/assets/46963668-af4c-42f8-a83f-01c7404589bb)
1 parent 8985e21 commit 412b0a2

File tree

3 files changed

+33
-11
lines changed

3 files changed

+33
-11
lines changed

Sources/CodeEditSourceEditor/Controller/TextViewController+LoadView.swift

+7-6
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ extension TextViewController {
2323
delegate: self
2424
)
2525
gutterView.updateWidthIfNeeded()
26-
scrollView.addFloatingSubview(
27-
gutterView,
28-
for: .horizontal
29-
)
26+
scrollView.addFloatingSubview(gutterView, for: .horizontal)
3027

3128
minimapView = MinimapView(textView: textView, theme: theme)
3229
scrollView.addFloatingSubview(minimapView, for: .vertical)
@@ -89,7 +86,7 @@ extension TextViewController {
8986
minimapView.bottomAnchor.constraint(equalTo: scrollView.contentView.bottomAnchor),
9087
minimapXConstraint,
9188
maxWidthConstraint,
92-
relativeWidthConstraint,
89+
relativeWidthConstraint
9390
])
9491
}
9592

@@ -124,7 +121,11 @@ extension TextViewController {
124121
queue: .main
125122
) { [weak self] _ in
126123
self?.gutterView.frame.size.height = (self?.textView.frame.height ?? 0) + 10
124+
self?.gutterView.frame.origin.y = (self?.textView.frame.origin.y ?? 0.0)
125+
- (self?.scrollView.contentInsets.top ?? 0)
126+
127127
self?.gutterView.needsDisplay = true
128+
self?.scrollView.needsLayout = true
128129
}
129130

130131
NotificationCenter.default.addObserver(
@@ -146,7 +147,7 @@ extension TextViewController {
146147

147148
// Reset content insets and gutter position when appearance changes
148149
self.styleScrollView()
149-
self.gutterView.frame.origin.y = -self.scrollView.contentInsets.top
150+
self.gutterView.frame.origin.y = self.textView.frame.origin.y - self.scrollView.contentInsets.top
150151
}
151152
}
152153
.store(in: &cancellables)

Sources/CodeEditSourceEditor/Controller/TextViewController+StyleViews.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ extension TextViewController {
102102

103103
findViewController?.topPadding = contentInsets?.top
104104

105-
gutterView.frame.origin.y = -scrollView.contentInsets.top
105+
gutterView.frame.origin.y = textView.frame.origin.y - scrollView.contentInsets.top
106106

107107
// Update scrollview tiling
108108
scrollView.reflectScrolledClipView(scrollView.contentView)

Sources/CodeEditSourceEditor/Gutter/GutterView.swift

+25-4
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ public class GutterView: NSView {
4545
var textColor: NSColor = .secondaryLabelColor
4646

4747
@Invalidating(.display)
48-
var font: NSFont = .systemFont(ofSize: 13)
48+
var font: NSFont = .systemFont(ofSize: 13) {
49+
didSet {
50+
updateFontLineHeight()
51+
}
52+
}
4953

5054
@Invalidating(.display)
5155
var edgeInsets: EdgeInsets = EdgeInsets(leading: 20, trailing: 12)
@@ -74,6 +78,19 @@ public class GutterView: NSView {
7478
/// The maximum number of digits found for a line number.
7579
private var maxLineLength: Int = 0
7680

81+
private var fontLineHeight = 1.0
82+
83+
private func updateFontLineHeight() {
84+
let string = NSAttributedString(string: "0", attributes: [.font: font])
85+
let typesetter = CTTypesetterCreateWithAttributedString(string)
86+
let ctLine = CTTypesetterCreateLine(typesetter, CFRangeMake(0, 1))
87+
var ascent: CGFloat = 0
88+
var descent: CGFloat = 0
89+
var leading: CGFloat = 0
90+
CTLineGetTypographicBounds(ctLine, &ascent, &descent, &leading)
91+
fontLineHeight = (ascent + descent + leading)
92+
}
93+
7794
override public var isFlipped: Bool {
7895
true
7996
}
@@ -181,7 +198,7 @@ public class GutterView: NSView {
181198
y: line.yPos,
182199
width: width,
183200
height: line.height
184-
)
201+
).pixelAligned
185202
)
186203
}
187204

@@ -217,12 +234,16 @@ public class GutterView: NSView {
217234
let fragment: LineFragment? = linePosition.data.lineFragments.first?.data
218235
var ascent: CGFloat = 0
219236
let lineNumberWidth = CTLineGetTypographicBounds(ctLine, &ascent, nil, nil)
237+
let fontHeightDifference = ((fragment?.height ?? 0) - fontLineHeight) / 4
220238

221-
let yPos = linePosition.yPos + ascent + (fragment?.heightDifference ?? 0)/2
239+
let yPos = linePosition.yPos + ascent + (fragment?.heightDifference ?? 0)/2 + fontHeightDifference
222240
// Leading padding + (width - linewidth)
223241
let xPos = edgeInsets.leading + (maxWidth - lineNumberWidth)
224242

225-
context.textPosition = CGPoint(x: xPos, y: yPos).pixelAligned
243+
ContextSetHiddenSmoothingStyle(context, 16)
244+
245+
context.textPosition = CGPoint(x: xPos, y: yPos)
246+
226247
CTLineDraw(ctLine, context)
227248
}
228249
context.restoreGState()

0 commit comments

Comments
 (0)