@@ -19,21 +19,22 @@ extension FoldingRibbonView {
1919
2020 override func draw( _ dirtyRect: NSRect ) {
2121 guard let context = NSGraphicsContext . current? . cgContext,
22- let layoutManager = model? . controller? . textView. layoutManager else {
22+ let layoutManager = model? . controller? . textView. layoutManager,
23+ // Find the visible lines in the rect AppKit is asking us to draw.
24+ let rangeStart = layoutManager. textLineForPosition ( dirtyRect. minY) ,
25+ let rangeEnd = layoutManager. textLineForPosition ( dirtyRect. maxY) else {
2326 return
2427 }
2528
2629 context. saveGState ( )
2730 context. clip ( to: dirtyRect)
2831
29- // Find the visible lines in the rect AppKit is asking us to draw.
30- guard let rangeStart = layoutManager. textLineForPosition ( dirtyRect. minY) ,
31- let rangeEnd = layoutManager. textLineForPosition ( dirtyRect. maxY) else {
32- return
33- }
34- let textRange = rangeStart. range. location..< rangeEnd. range. upperBound
35- let folds = getDrawingFolds ( forTextRange: textRange, layoutManager: layoutManager)
32+ let folds = getDrawingFolds (
33+ forTextRange: rangeStart. range. location..< rangeEnd. range. upperBound,
34+ layoutManager: layoutManager
35+ )
3636 let foldCaps = FoldCapInfo ( folds)
37+
3738 for fold in folds. filter ( { !$0. fold. isCollapsed } ) {
3839 drawFoldMarker (
3940 fold,
@@ -101,14 +102,15 @@ extension FoldingRibbonView {
101102 }
102103 }
103104
105+ // MARK: - Draw Fold Marker
106+
104107 /// Draw a single fold marker for a fold.
105108 ///
106109 /// Ensure the correct fill color is set on the drawing context before calling.
107110 ///
108111 /// - Parameters:
109112 /// - foldInfo: The fold to draw.
110- /// - markerContext: The context in which the fold is being drawn, including the depth and if a line is
111- /// being hovered.
113+ /// - foldCaps:
112114 /// - context: The drawing context to use.
113115 /// - layoutManager: A layout manager used to retrieve position information for lines.
114116 private func drawFoldMarker(
@@ -119,21 +121,27 @@ extension FoldingRibbonView {
119121 ) {
120122 let minYPosition = foldInfo. startLine. yPos
121123 let maxYPosition = foldInfo. endLine. yPos + foldInfo. endLine. height
124+ let foldRect = NSRect ( x: 0 , y: minYPosition + 1 , width: 7 , height: maxYPosition - minYPosition - 2 )
122125
123126 if foldInfo. fold. isCollapsed {
124- drawCollapsedFold ( minYPosition: minYPosition, maxYPosition: maxYPosition, in: context)
125- } else if let hoveringFold, hoveringFold. isHoveringEqual ( foldInfo. fold) {
126- drawHoveredFold (
127+ drawCollapsedFold (
128+ foldInfo: foldInfo,
127129 minYPosition: minYPosition,
128130 maxYPosition: maxYPosition,
129131 in: context
130132 )
133+ } else if hoveringFold? . isHoveringEqual ( foldInfo. fold) == true {
134+ drawHoveredFold (
135+ foldInfo: foldInfo,
136+ foldCaps: foldCaps,
137+ foldRect: foldRect,
138+ in: context
139+ )
131140 } else {
132141 drawNestedFold (
133142 foldInfo: foldInfo,
134143 foldCaps: foldCaps,
135- minYPosition: minYPosition,
136- maxYPosition: maxYPosition,
144+ foldRect: foldCaps. adjustFoldRect ( using: foldInfo, rect: foldRect) ,
137145 in: context
138146 )
139147 }
@@ -142,13 +150,14 @@ extension FoldingRibbonView {
142150 // MARK: - Collapsed Fold
143151
144152 private func drawCollapsedFold(
153+ foldInfo: DrawingFoldInfo ,
145154 minYPosition: CGFloat ,
146155 maxYPosition: CGFloat ,
147156 in context: CGContext
148157 ) {
149158 context. saveGState ( )
150159
151- let fillRect = CGRect ( x: 0 , y: minYPosition, width: Self . width, height: maxYPosition - minYPosition)
160+ let fillRect = CGRect ( x: 0 , y: minYPosition + 1.0 , width: Self . width, height: maxYPosition - minYPosition - 2.0 )
152161
153162 let height = 5.0
154163 let minX = 2.0
@@ -162,6 +171,11 @@ extension FoldingRibbonView {
162171 chevron. addLine ( to: CGPoint ( x: maxX, y: centerY) )
163172 chevron. addLine ( to: CGPoint ( x: minX, y: maxY) )
164173
174+ if let hoveringFoldMask, hoveringFoldMask. intersects ( CGPath ( rect: fillRect, transform: . none) ) {
175+ context. addPath ( hoveringFoldMask)
176+ context. clip ( )
177+ }
178+
165179 context. setStrokeColor ( foldedIndicatorChevronColor)
166180 context. setLineCap ( . round)
167181 context. setLineJoin ( . round)
@@ -178,22 +192,35 @@ extension FoldingRibbonView {
178192 // MARK: - Hovered Fold
179193
180194 private func drawHoveredFold(
181- minYPosition: CGFloat ,
182- maxYPosition: CGFloat ,
195+ foldInfo: DrawingFoldInfo ,
196+ foldCaps: FoldCapInfo ,
197+ foldRect: NSRect ,
183198 in context: CGContext
184199 ) {
185200 context. saveGState ( )
186- let plainRect = NSRect ( x: - 2 , y: minYPosition, width: 11.0 , height: maxYPosition - minYPosition)
187- let roundedRect = NSBezierPath ( roundedRect: plainRect, xRadius: 11.0 / 2 , yRadius: 11.0 / 2 )
201+ let plainRect = foldRect. transform ( x: - 2.0 , y: - 1.0 , width: 4.0 , height: 2.0 )
202+ let roundedRect = NSBezierPath (
203+ roundedRect: plainRect,
204+ xRadius: plainRect. width / 2 ,
205+ yRadius: plainRect. width / 2
206+ )
188207
189208 context. setFillColor ( hoverFillColor. copy ( alpha: hoverAnimationProgress) ?? hoverFillColor)
190209 context. setStrokeColor ( hoverBorderColor. copy ( alpha: hoverAnimationProgress) ?? hoverBorderColor)
191210 context. addPath ( roundedRect. cgPathFallback)
192211 context. drawPath ( using: . fillStroke)
193212
194- // Add the little arrows
195- drawChevron ( in: context, yPosition: minYPosition + 8 , pointingUp: false )
196- drawChevron ( in: context, yPosition: maxYPosition - 8 , pointingUp: true )
213+ // Add the little arrows if we're not hovering right on a collapsed guy
214+ if foldCaps. hoveredFoldShouldDrawTopChevron ( foldInfo) {
215+ drawChevron ( in: context, yPosition: plainRect. minY + 8 , pointingUp: false )
216+ }
217+ if foldCaps. hoveredFoldShouldDrawBottomChevron ( foldInfo) {
218+ drawChevron ( in: context, yPosition: plainRect. maxY - 8 , pointingUp: true )
219+ }
220+
221+ let plainMaskRect = foldRect. transform ( y: 1.0 , height: - 2.0 )
222+ let roundedMaskRect = NSBezierPath ( roundedRect: plainMaskRect, xRadius: Self . width / 2 , yRadius: Self . width / 2 )
223+ hoveringFoldMask = roundedMaskRect. cgPathFallback
197224
198225 context. restoreGState ( )
199226 }
@@ -207,7 +234,11 @@ extension FoldingRibbonView {
207234 let minX = center - ( chevronSize. width / 2 )
208235 let maxX = center + ( chevronSize. width / 2 )
209236
210- let startY = pointingUp ? yPosition + chevronSize. height : yPosition - chevronSize. height
237+ let startY = if pointingUp {
238+ yPosition + chevronSize. height
239+ } else {
240+ yPosition - chevronSize. height
241+ }
211242
212243 context. setStrokeColor ( NSColor . secondaryLabelColor. withAlphaComponent ( hoverAnimationProgress) . cgColor)
213244 context. setLineCap ( . round)
@@ -228,21 +259,15 @@ extension FoldingRibbonView {
228259 private func drawNestedFold(
229260 foldInfo: DrawingFoldInfo ,
230261 foldCaps: FoldCapInfo ,
231- minYPosition: CGFloat ,
232- maxYPosition: CGFloat ,
262+ foldRect: NSRect ,
233263 in context: CGContext
234264 ) {
235265 context. saveGState ( )
236- let plainRect = foldCaps. adjustFoldRect (
237- using: foldInfo,
238- rect: NSRect ( x: 0 , y: minYPosition + 1 , width: 7 , height: maxYPosition - minYPosition - 2 )
239- )
240- let radius = plainRect. width / 2.0
241266 let roundedRect = NSBezierPath (
242- roundingRect: plainRect ,
267+ roundingRect: foldRect ,
243268 capTop: foldCaps. foldNeedsTopCap ( foldInfo) ,
244269 capBottom: foldCaps. foldNeedsBottomCap ( foldInfo) ,
245- cornerRadius: radius
270+ cornerRadius: foldRect . width / 2.0
246271 )
247272
248273 context. setFillColor ( markerColor)
@@ -254,8 +279,8 @@ extension FoldingRibbonView {
254279 drawOutline (
255280 foldInfo: foldInfo,
256281 foldCaps: foldCaps,
282+ foldRect: foldRect,
257283 originalPath: roundedRect. cgPathFallback,
258- yPosition: minYPosition... maxYPosition,
259284 in: context
260285 )
261286 }
@@ -277,42 +302,26 @@ extension FoldingRibbonView {
277302 private func drawOutline(
278303 foldInfo: DrawingFoldInfo ,
279304 foldCaps: FoldCapInfo ,
305+ foldRect: NSRect ,
280306 originalPath: CGPath ,
281- yPosition: ClosedRange < CGFloat > ,
282307 in context: CGContext
283308 ) {
284309 context. saveGState ( )
285310
286- let plainRect = foldCaps. adjustFoldRect (
287- using: foldInfo,
288- rect: NSRect (
289- x: - 0.5 ,
290- y: yPosition. lowerBound,
291- width: frame. width + 1.0 ,
292- height: yPosition. upperBound - yPosition. lowerBound
293- )
294- )
295- let radius = plainRect. width / 2.0
311+ let plainRect = foldRect. transform ( x: - 1.0 , y: - 1.0 , width: 2.0 , height: 2.0 )
296312 let roundedRect = NSBezierPath (
297313 roundingRect: plainRect,
298314 capTop: foldCaps. foldNeedsTopCap ( foldInfo) ,
299315 capBottom: foldCaps. foldNeedsBottomCap ( foldInfo) ,
300- cornerRadius: radius
316+ cornerRadius: plainRect . width / 2.0
301317 )
302- roundedRect. transform ( using: . init( translationByX: - 0.5 , byY: 0.0 ) )
318+ roundedRect. transform ( using: . init( translationByX: - 1.0 , byY: 0.0 ) )
303319
304320 let combined = CGMutablePath ( )
305321 combined. addPath ( roundedRect. cgPathFallback)
306322 combined. addPath ( originalPath)
307323
308- context. clip (
309- to: CGRect (
310- x: 0 ,
311- y: yPosition. lowerBound,
312- width: 7 ,
313- height: yPosition. upperBound - yPosition. lowerBound
314- )
315- )
324+ context. clip ( to: foldRect. transform ( y: - 1.0 , height: 2.0 ) )
316325 context. addPath ( combined)
317326 context. setFillColor ( markerBorderColor)
318327 context. drawPath ( using: . eoFill)
0 commit comments