fix: 移除有bug的移除当前选择的机制

此实现企图通过捕获对列表空白区域的点击以触发移除列表的当前选中项
This commit is contained in:
Qiu Yuzhou
2026-10-06 00:52:35 +08:00
parent 0ba9b1688b
commit 77c33e526b
2 changed files with 1 additions and 89 deletions
@@ -1,79 +0,0 @@
import SwiftUI
/// List 没有空白点击回调;用原生表格的行命中判断,不拦截行选择、按钮或滚动条。
struct ServerTreeBlankClickObserver: NSViewRepresentable {
let onBlankClick: () -> Void
func makeNSView(context: Context) -> ObserverView {
let view = ObserverView()
view.onBlankClick = onBlankClick
return view
}
func updateNSView(_ view: ObserverView, context: Context) {
view.onBlankClick = onBlankClick
}
static func dismantleNSView(_ view: ObserverView, coordinator: ()) {
view.stopObserving()
}
final class ObserverView: NSView {
var onBlankClick: (() -> Void)?
private var monitor: Any?
override func hitTest(_ point: NSPoint) -> NSView? { nil }
override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
stopObserving()
guard window != nil else { return }
monitor = NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown) { [weak self] event in
self?.handleClick(event)
return event
}
}
func stopObserving() {
if let monitor { NSEvent.removeMonitor(monitor) }
monitor = nil
}
private func handleClick(_ event: NSEvent) {
guard let window, event.window === window,
visibleRect.contains(convert(event.locationInWindow, from: nil))
else { return }
// 背景探针与 List 的原生滚动视图是兄弟;向上寻找共同容器,
// 只接受点击点处的表格,排除滚动条和其他列表。
var container = superview
while let current = container {
if let table = table(at: event.locationInWindow, in: current) {
// 覆盖式滚动条可能落在表格几何范围内,实际命中必须属于表格。
guard let content = window.contentView else { return }
let hitPoint =
content.superview?.convert(event.locationInWindow, from: nil)
?? event.locationInWindow
var hit = content.hitTest(hitPoint)
while let view = hit, view !== table { hit = view.superview }
guard hit === table else { return }
let point = table.convert(event.locationInWindow, from: nil)
if table.row(at: point) == -1 { onBlankClick?() }
return
}
container = current.superview
}
}
private func table(at point: NSPoint, in view: NSView) -> NSTableView? {
if let table = view as? NSTableView,
table.visibleRect.contains(table.convert(point, from: nil))
{
return table
}
for child in view.subviews {
if let table = table(at: point, in: child) { return table }
}
return nil
}
}
}
@@ -37,7 +37,6 @@ struct ServersView: View {
@State private var deleteTarget: NodeID?
@State private var moveTarget: NodeID?
@State private var rootDropHovering = false
@FocusState private var treeFocused: Bool
// 分享 popover 打开时冻结载荷、顶部资料与建议文件名。
@State var shareContext: ShareContext?
@@ -105,19 +104,11 @@ struct ServersView: View {
}
}
.listStyle(.sidebar)
.focusable(interactions: .edit)
.focused($treeFocused)
.onKeyPress(.escape) {
guard treeFocused, selection != nil else { return .ignored }
guard selection != nil else { return .ignored }
selection = nil
return .handled
}
.background {
ServerTreeBlankClickObserver {
selection = nil
treeFocused = true
}
}
.overlay(alignment: .center) {
if workflow.tree.isEmpty {
ContentUnavailableView(