Skip to content

Commit b6e55d9

Browse files
committed
Hide nearby cursor suggestion when the line of code is out of frame
1 parent 4f0fd60 commit b6e55d9

3 files changed

Lines changed: 40 additions & 12 deletions

File tree

Core/Sources/SuggestionWidget/SuggestionPanelView.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@ import SwiftUI
44
@MainActor
55
final class SuggestionPanelDisplayController: ObservableObject {
66
@Published var alignTopToAnchor = false
7+
@Published var isPanelOutOfFrame: Bool = false
78
@Published var isPanelDisplayed: Bool = false
89

910
init(
1011
alignTopToAnchor: Bool = false,
12+
isPanelOutOfFrame: Bool = false,
1113
isPanelDisplayed: Bool = false
1214
) {
1315
self.alignTopToAnchor = alignTopToAnchor
1416
self.isPanelDisplayed = isPanelDisplayed
17+
self.isPanelOutOfFrame = isPanelOutOfFrame
1518
}
1619
}
1720

@@ -45,7 +48,9 @@ struct SuggestionPanelView: View {
4548
}
4649
.frame(maxWidth: .infinity, maxHeight: Style.inlineSuggestionMaxHeight)
4750
.fixedSize(horizontal: false, vertical: true)
48-
.allowsHitTesting(displayController.isPanelDisplayed)
51+
.allowsHitTesting(
52+
displayController.isPanelDisplayed && !displayController.isPanelOutOfFrame
53+
)
4954
}
5055
}
5156
.frame(maxWidth: .infinity)
@@ -59,6 +64,7 @@ struct SuggestionPanelView: View {
5964
.preferredColorScheme(viewModel.colorScheme)
6065
.opacity({
6166
guard displayController.isPanelDisplayed else { return 0 }
67+
guard !displayController.isPanelOutOfFrame else { return 0 }
6268
guard viewModel.content != nil else { return 0 }
6369
return 1
6470
}())
@@ -72,6 +78,12 @@ struct SuggestionPanelView: View {
7278
.easeInOut(duration: 0.2),
7379
value: displayController.isPanelDisplayed
7480
)
81+
.animation(
82+
featureFlag: \.animationBCrashSuggestion,
83+
.easeInOut(duration: 0.2),
84+
value: displayController.isPanelOutOfFrame
85+
)
7586
.frame(maxWidth: Style.inlineSuggestionMinWidth, maxHeight: Style.inlineSuggestionMaxHeight)
7687
}
7788
}
89+

Core/Sources/SuggestionWidget/SuggestionWidgetController.swift

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -537,15 +537,18 @@ extension SuggestionWidgetController {
537537
sharedPanelDisplayController.alignTopToAnchor = widgetLocation.defaultPanelLocation
538538
.alignPanelTop
539539

540-
let suggestionPanelLocation = widgetLocation.suggestionPanelLocation ?? widgetLocation
541-
.defaultPanelLocation
542-
suggestionWindow.setFrame(
543-
suggestionPanelLocation.frame,
544-
display: false,
545-
animate: animated
546-
)
547-
suggestionPanelDisplayController.alignTopToAnchor = suggestionPanelLocation
548-
.alignPanelTop
540+
if let suggestionPanelLocation = widgetLocation.suggestionPanelLocation {
541+
suggestionWindow.setFrame(
542+
suggestionPanelLocation.frame,
543+
display: false,
544+
animate: animated
545+
)
546+
suggestionPanelDisplayController.isPanelOutOfFrame = false
547+
suggestionPanelDisplayController.alignTopToAnchor = suggestionPanelLocation
548+
.alignPanelTop
549+
} else {
550+
suggestionPanelDisplayController.isPanelOutOfFrame = true
551+
}
549552

550553
if detachChat {
551554
if chatWindow.alphaValue == 0 {

Core/Sources/SuggestionWidget/WidgetPositionStrategy.swift

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,11 @@ enum UpdateLocationStrategy {
224224
guard let selectionFrame = UpdateLocationStrategy
225225
.getSelectionFirstLineFrame(editor: editor) else { return nil }
226226

227+
// hide it when the line of code is outside of the editor visible rect
228+
if selectionFrame.maxY < editorFrame.minY || selectionFrame.minY > editorFrame.maxY {
229+
return nil
230+
}
231+
227232
let proposedY = mainScreen.frame.height - selectionFrame.maxY
228233
let proposedX = selectionFrame.maxX - 40
229234
let maxY = max(
@@ -235,7 +240,11 @@ enum UpdateLocationStrategy {
235240
activeScreen.frame.maxY - 4
236241
)
237242

238-
let alignPanelTopToAnchor = y - Style.inlineSuggestionMaxHeight >= activeScreen.frame.minY
243+
// align panel to top == place under the selection frame.
244+
// we initially try to place it at the bottom side, but if there is no enough space
245+
// we move it to the top of the selection frame.
246+
let alignPanelTopToAnchor = y - Style.inlineSuggestionMaxHeight
247+
>= activeScreen.frame.minY
239248

240249
let caseIgnoreCompletionPanel = {
241250
(alignPanelTopToAnchor: Bool) -> WidgetLocation.PanelLocation? in
@@ -246,7 +255,7 @@ enum UpdateLocationStrategy {
246255
return activeScreen.frame.maxX - Style.inlineSuggestionMinWidth
247256
}()
248257
if alignPanelTopToAnchor {
249-
// case: present below selection
258+
// case: present under selection
250259
return .init(
251260
frame: .init(
252261
x: x,
@@ -257,6 +266,7 @@ enum UpdateLocationStrategy {
257266
alignPanelTop: alignPanelTopToAnchor
258267
)
259268
} else {
269+
// case: present above selection
260270
return .init(
261271
frame: .init(
262272
x: x,
@@ -275,8 +285,10 @@ enum UpdateLocationStrategy {
275285

276286
switch (completionPanelBelowCursor, alignPanelTopToAnchor) {
277287
case (true, false), (false, true):
288+
// case: different position, place the suggestion as it should be
278289
return caseIgnoreCompletionPanel(alignPanelTopToAnchor)
279290
case (true, true), (false, false):
291+
// case: same position, place the suggestion next to the completion panel
280292
let y = completionPanelBelowCursor
281293
? y - Style.inlineSuggestionMaxHeight
282294
: y + selectionFrame.height - Style.widgetPadding
@@ -303,6 +315,7 @@ enum UpdateLocationStrategy {
303315
alignPanelTop: alignPanelTopToAnchor
304316
)
305317
}
318+
// case: no enough horizontal space, place the suggestion on the other side
306319
return caseIgnoreCompletionPanel(!alignPanelTopToAnchor)
307320
}
308321
}

0 commit comments

Comments
 (0)