Skip to content

Commit b787330

Browse files
authored
Merge pull request #100 from yusufozgul/feature/editorOverScroll
Editor Overscroll
2 parents ab09954 + e34050a commit b787330

File tree

6 files changed

+73
-5
lines changed

6 files changed

+73
-5
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ struct ContentView: View {
4444
@State var font = NSFont.monospacedSystemFont(ofSize: 11, weight: .regular)
4545
@State var tabWidth = 4
4646
@State var lineHeight = 1.2
47+
@State var editorOverscroll = 0.3
4748

4849
var body: some View {
4950
CodeEditTextView(
@@ -52,7 +53,8 @@ struct ContentView: View {
5253
theme: $theme,
5354
font: $font,
5455
tabWidth: $tabWidth,
55-
lineHeight: $lineHeight
56+
lineHeight: $lineHeight,
57+
editorOverscroll: $editorOverscroll
5658
)
5759
}
5860
}

Sources/CodeEditTextView/CodeEditTextView.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ public struct CodeEditTextView: NSViewControllerRepresentable {
2020
/// - font: The default font
2121
/// - tabWidth: The tab width
2222
/// - lineHeight: The line height multiplier (e.g. `1.2`)
23+
/// - editorOverscroll: The percentage for overscroll, between 0-1 (default: `0.0`)
2324
public init(
2425
_ text: Binding<String>,
2526
language: CodeLanguage,
2627
theme: Binding<EditorTheme>,
2728
font: Binding<NSFont>,
2829
tabWidth: Binding<Int>,
2930
lineHeight: Binding<Double>,
31+
editorOverscroll: Binding<Double> = .constant(0.0),
3032
cursorPosition: Published<(Int, Int)>.Publisher? = nil
3133
) {
3234
self._text = text
@@ -35,6 +37,7 @@ public struct CodeEditTextView: NSViewControllerRepresentable {
3537
self._font = font
3638
self._tabWidth = tabWidth
3739
self._lineHeight = lineHeight
40+
self._editorOverscroll = editorOverscroll
3841
self.cursorPosition = cursorPosition
3942
}
4043

@@ -44,6 +47,7 @@ public struct CodeEditTextView: NSViewControllerRepresentable {
4447
@Binding private var font: NSFont
4548
@Binding private var tabWidth: Int
4649
@Binding private var lineHeight: Double
50+
@Binding private var editorOverscroll: Double
4751
private var cursorPosition: Published<(Int, Int)>.Publisher?
4852

4953
public typealias NSViewControllerType = STTextViewController
@@ -55,7 +59,8 @@ public struct CodeEditTextView: NSViewControllerRepresentable {
5559
font: font,
5660
theme: theme,
5761
tabWidth: tabWidth,
58-
cursorPosition: cursorPosition
62+
cursorPosition: cursorPosition,
63+
editorOverscroll: editorOverscroll
5964
)
6065
controller.lineHeightMultiple = lineHeight
6166
return controller
@@ -67,6 +72,7 @@ public struct CodeEditTextView: NSViewControllerRepresentable {
6772
controller.theme = theme
6873
controller.tabWidth = tabWidth
6974
controller.lineHeightMultiple = lineHeight
75+
controller.editorOverscroll = editorOverscroll
7076
controller.reloadUI()
7177
return
7278
}

Sources/CodeEditTextView/Documentation.docc/CodeEditTextView.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ struct ContentView: View {
1212
@State var font = NSFont.monospacedSystemFont(ofSize: 11, weight: .regular)
1313
@State var tabWidth = 4
1414
@State var lineHeight = 1.2
15+
@State var editorOverscroll = 0.3
1516

1617
var body: some View {
1718
CodeEditTextView(
@@ -20,7 +21,8 @@ struct ContentView: View {
2021
theme: $theme,
2122
font: $font,
2223
tabWidth: $tabWidth,
23-
lineHeight: $lineHeight
24+
lineHeight: $lineHeight,
25+
editorOverscroll: $editorOverscroll
2426
)
2527
}
2628
}

Sources/CodeEditTextView/Documentation.docc/STTextViewController.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@
1010
- ``font``
1111
- ``tabWidth``
1212
- ``lineHeightMultiple``
13+
- ``editorOverscroll``

Sources/CodeEditTextView/STTextViewController.swift

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt
4242
/// The font to use in the `textView`
4343
public var font: NSFont
4444

45+
/// The editorOverscroll to use for the textView over scroll
46+
public var editorOverscroll: Double
47+
4548
// MARK: - Highlighting
4649

4750
internal var highlighter: Highlighter?
@@ -55,14 +58,16 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt
5558
font: NSFont,
5659
theme: EditorTheme,
5760
tabWidth: Int,
58-
cursorPosition: Published<(Int, Int)>.Publisher? = nil
61+
cursorPosition: Published<(Int, Int)>.Publisher? = nil,
62+
editorOverscroll: Double
5963
) {
6064
self.text = text
6165
self.language = language
6266
self.font = font
6367
self.theme = theme
6468
self.tabWidth = tabWidth
6569
self.cursorPosition = cursorPosition
70+
self.editorOverscroll = editorOverscroll
6671
super.init(nibName: nil, bundle: nil)
6772
}
6873

@@ -152,6 +157,13 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt
152157

153158
public override func viewDidLoad() {
154159
super.viewDidLoad()
160+
161+
NotificationCenter.default.addObserver(forName: NSWindow.didResizeNotification,
162+
object: nil,
163+
queue: .main) { [weak self] _ in
164+
guard let self = self else { return }
165+
(self.view as? NSScrollView)?.contentView.contentInsets.bottom = self.bottomContentInsets
166+
}
155167
}
156168

157169
public override func viewDidAppear() {
@@ -169,6 +181,18 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt
169181
return paragraph
170182
}
171183

184+
/// ScrollView's bottom inset using as editor overscroll
185+
private var bottomContentInsets: CGFloat {
186+
let height = view.frame.height
187+
var inset = editorOverscroll * height
188+
189+
if height - inset < lineHeight {
190+
inset = height - lineHeight
191+
}
192+
193+
return max(inset, .zero)
194+
}
195+
172196
/// Reloads the UI to apply changes to ``STTextViewController/font``, ``STTextViewController/theme``, ...
173197
internal func reloadUI() {
174198
textView?.font = font
@@ -182,6 +206,8 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt
182206
rulerView?.separatorColor = theme.invisibles
183207
rulerView?.baselineOffset = baselineOffset
184208

209+
(view as? NSScrollView)?.contentView.contentInsets.bottom = bottomContentInsets
210+
185211
setStandardAttributes()
186212
}
187213

Tests/CodeEditTextViewTests/STTextViewControllerTests.swift

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ final class STTextViewControllerTests: XCTestCase {
3232
language: .default,
3333
font: .monospacedSystemFont(ofSize: 11, weight: .medium),
3434
theme: theme,
35-
tabWidth: 4
35+
tabWidth: 4,
36+
editorOverscroll: 0.5
3637
)
3738
}
3839

@@ -60,4 +61,34 @@ final class STTextViewControllerTests: XCTestCase {
6061
XCTAssertEqual(color4, NSColor.textColor)
6162
}
6263

64+
func test_editorOverScroll() throws {
65+
let scrollView = try XCTUnwrap(controller.view as? NSScrollView)
66+
scrollView.frame = .init(x: .zero,
67+
y: .zero,
68+
width: 100,
69+
height: 100)
70+
71+
// editorOverscroll: 0
72+
XCTAssertEqual(scrollView.contentView.contentInsets.bottom, 0)
73+
74+
controller.editorOverscroll = 0.5
75+
controller.reloadUI()
76+
77+
// editorOverscroll: 0.5
78+
XCTAssertEqual(scrollView.contentView.contentInsets.bottom, 50.0)
79+
80+
controller.editorOverscroll = 1.0
81+
controller.reloadUI()
82+
83+
// editorOverscroll: 1.0
84+
XCTAssertEqual(scrollView.contentView.contentInsets.bottom, 87.0)
85+
}
86+
87+
func test_editorOverScroll_ZeroCondition() throws {
88+
let scrollView = try XCTUnwrap(controller.view as? NSScrollView)
89+
scrollView.frame = .zero
90+
91+
// editorOverscroll: 0
92+
XCTAssertEqual(scrollView.contentView.contentInsets.bottom, 0)
93+
}
6394
}

0 commit comments

Comments
 (0)