Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions Sources/Tic/AppModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,21 @@ final class AppModel {
searchWindow = panel
}
guard let window = searchWindow else { return }
window.center() // centered on screen every time it's called
centerOnActiveDisplay(window) // centered on screen every time it's called
window.level = .floating // and always on top when called
window.makeKeyAndOrderFront(nil)
NSApp.activate()
}

/// Dead-centre of the display the pointer is on. `NSWindow.center()` isn't that: it sits above
/// centre, and on whichever display the reused panel was last shown.
private func centerOnActiveDisplay(_ window: NSWindow) {
let pointer = NSEvent.mouseLocation
guard let area = (NSScreen.screens.first { $0.frame.contains(pointer) } ?? NSScreen.main)?.visibleFrame
else { return }
window.setFrameOrigin(NSPoint(x: area.midX - window.frame.width / 2, y: area.midY - window.frame.height / 2))
}

/// Closes the Lists palette (Escape / pick a list / its close button).
func dismissSearch() {
searchWindow?.orderOut(nil)
Expand Down Expand Up @@ -158,7 +167,7 @@ final class AppModel {
mcpWindow = panel
}
guard let window = mcpWindow else { return }
window.center()
centerOnActiveDisplay(window)
window.level = .floating
window.makeKeyAndOrderFront(nil)
NSApp.activate()
Expand Down
16 changes: 13 additions & 3 deletions Sources/Tic/Views/ListsSearchView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ struct ListsSearchView: View {
@State private var model = AppModel.shared
@State private var search = ""
@State private var selectedID: Note.ID?
@State private var scrollRequest: Note.ID?
@State private var pointerAtKeyMove: NSPoint?
@FocusState private var searchFocused: Bool

private var results: [Note] {
Expand All @@ -27,6 +29,7 @@ struct ListsSearchView: View {
}
.frame(width: 700, height: 440)
.background(.regularMaterial)
.ignoresSafeArea() // else the hidden title bar leaves a blank strip on top
.task {
searchFocused = true
selectedID = results.first?.id
Expand Down Expand Up @@ -87,17 +90,22 @@ struct ListsSearchView: View {
onDelete: { model.delete(note) }
)
.id(note.id)
.onHover { if $0 { selectedID = note.id } }
// A row scrolled under a resting pointer isn't a hover: that would
// snatch the selection back from the arrow keys.
.onHover { if $0, NSEvent.mouseLocation != pointerAtKeyMove { selectedID = note.id } }
.onTapGesture { selectedID = note.id; openSelected() }
}
}
}
.padding(.horizontal, 8)
.padding(.vertical, 6)
}
.onChange(of: selectedID) { _, id in
// Only the arrow keys scroll. Hover also selects, and scrolling for that moved the
// list under the pointer, which hovered a new row, which scrolled again…
.onChange(of: scrollRequest) { _, id in
guard let id else { return }
withAnimation(.easeOut(duration: 0.12)) { proxy.scrollTo(id, anchor: .center) }
withAnimation(.easeOut(duration: 0.12)) { proxy.scrollTo(id) }
scrollRequest = nil
}
}
.frame(maxHeight: .infinity)
Expand Down Expand Up @@ -138,6 +146,8 @@ struct ListsSearchView: View {
let current = selectedID.flatMap { ids.firstIndex(of: $0) } ?? -1
let next = min(max(current + delta, 0), ids.count - 1)
selectedID = ids[next]
scrollRequest = ids[next]
pointerAtKeyMove = NSEvent.mouseLocation
}

private func openSelected() {
Expand Down
1 change: 1 addition & 0 deletions Sources/Tic/Views/MCPSetupView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct MCPSetupView: View {
}
.frame(width: 680, height: 460)
.background(.regularMaterial)
.ignoresSafeArea() // else the hidden title bar leaves a blank strip on top
.onExitCommand { model.dismissMCPSetup() }
}

Expand Down
Loading