From 38a1956c7fda254462f20f94563f3fa9e8a3ebe2 Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Wed, 6 May 2026 16:21:25 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=82=AC=E6=B5=AE?= =?UTF-8?q?=E7=AA=97=E9=A1=B6=E9=83=A8=E5=BC=82=E5=B8=B8=E7=9F=A9=E5=BD=A2?= =?UTF-8?q?=E5=B9=B6=E5=AE=8C=E5=96=84=E8=83=8C=E6=9D=BF=E8=A1=8C=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/FloatingMonitor/App/AppDelegate.swift | 1 + .../FloatingMonitor/App/FloatingWindow.swift | 8 +- .../App/FloatingWindowController.swift | 112 +++++++++++++++++- Sources/FloatingMonitor/Configuration.swift | 12 ++ Sources/FloatingMonitor/ContentView.swift | 1 + .../FloatingMonitor/Views/SettingsView.swift | 20 ++++ 6 files changed, 148 insertions(+), 6 deletions(-) diff --git a/Sources/FloatingMonitor/App/AppDelegate.swift b/Sources/FloatingMonitor/App/AppDelegate.swift index 0e2f4e5..70c4848 100644 --- a/Sources/FloatingMonitor/App/AppDelegate.swift +++ b/Sources/FloatingMonitor/App/AppDelegate.swift @@ -6,6 +6,7 @@ extension Notification.Name { static let showSettingsPanel = Notification.Name("FloatingMonitor.ShowSettings") static let resizeMonitorWindow = Notification.Name("FloatingMonitor.ResizeWindow") static let refreshIntervalChanged = Notification.Name("FloatingMonitor.RefreshIntervalChanged") + static let backdropEnabledChanged = Notification.Name("FloatingMonitor.BackdropEnabledChanged") } // MARK: - AppDelegate diff --git a/Sources/FloatingMonitor/App/FloatingWindow.swift b/Sources/FloatingMonitor/App/FloatingWindow.swift index 85f57a0..7e6a035 100644 --- a/Sources/FloatingMonitor/App/FloatingWindow.swift +++ b/Sources/FloatingMonitor/App/FloatingWindow.swift @@ -21,7 +21,11 @@ final class FloatingWindow: NSWindow { self.hasShadow = true self.isMovableByWindowBackground = true self.minSize = NSSize(width: 240, height: 200) - self.titlebarAppearsTransparent = true - self.titleVisibility = .hidden + + // 替换默认 contentView 为透明视图,防止系统默认灰色背景露出 + let view = NSView() + view.wantsLayer = true + view.layer?.backgroundColor = NSColor.clear.cgColor + self.contentView = view } } diff --git a/Sources/FloatingMonitor/App/FloatingWindowController.swift b/Sources/FloatingMonitor/App/FloatingWindowController.swift index 863355a..1935921 100644 --- a/Sources/FloatingMonitor/App/FloatingWindowController.swift +++ b/Sources/FloatingMonitor/App/FloatingWindowController.swift @@ -6,14 +6,86 @@ final class FloatingWindowController: NSWindowController { private var hostingView: NSHostingView? private var visualEffect: NSVisualEffectView! + private let debugLogPath = NSHomeDirectory() + "/Desktop/fm_debug.log" + convenience init() { - let window = FloatingWindow() + let window = FloatingWindow( + contentRect: NSRect(x: 0, y: 0, width: 250, height: 300), + styleMask: [.borderless, .resizable], + backing: .buffered, + defer: false + ) window.isReleasedWhenClosed = false self.init(window: window) setupVisualEffect() + observeBackdropToggle() + clearDebugLog() } - // MARK: - 毛玻璃(只做合成锚点,alpha 始终=1) + // MARK: - 调试日志 + + private func clearDebugLog() { + try? "".write(toFile: debugLogPath, atomically: true, encoding: .utf8) + } + + private func log(_ msg: String) { + let ts = ISO8601DateFormatter().string(from: Date()) + let line = "[\(ts)] \(msg)\n" + if let data = line.data(using: .utf8) { + if let handle = FileHandle(forWritingAtPath: debugLogPath) { + handle.seekToEndOfFile() + handle.write(data) + handle.closeFile() + } else { + try? data.write(to: URL(fileURLWithPath: debugLogPath)) + } + } + } + + func dumpGeometry(_ tag: String) { + guard let window = window else { return } + log("===== \(tag) =====") + log("window.frame = \(window.frame)") + log("window.styleMask = \(window.styleMask.rawValue)") + log("window.isOpaque = \(window.isOpaque)") + log("window.hasShadow = \(window.hasShadow)") + log("window.level = \(window.level.rawValue)") + log("window.backgroundColor= \(String(describing: window.backgroundColor))") + log("window.isVisible = \(window.isVisible)") + log("window.titlebarAppearsTransparent = \(window.titlebarAppearsTransparent)") + + guard let cv = window.contentView else { return } + log("--- contentView ---") + log("contentView.frame = \(cv.frame)") + log("contentView.bounds = \(cv.bounds)") + log("contentView.subviews.count = \(cv.subviews.count)") + + for (i, sv) in cv.subviews.enumerated() { + log(" subview[\(i)]: \(type(of: sv))") + log(" frame = \(sv.frame)") + log(" bounds = \(sv.bounds)") + log(" isHidden = \(sv.isHidden)") + log(" alphaValue = \(sv.alphaValue)") + log(" wantsLayer = \(sv.wantsLayer)") + if let layer = sv.layer { + log(" layer.frame = \(layer.frame)") + log(" layer.bounds = \(layer.bounds)") + log(" layer.cornerRadius= \(layer.cornerRadius)") + log(" layer.masksToBounds=\(layer.masksToBounds)") + log(" layer.isHidden = \(layer.isHidden)") + log(" layer.opacity = \(layer.opacity)") + log(" layer.sublayers?.count = \(layer.sublayers?.count ?? 0)") + if let subs = layer.sublayers { + for (j, sl) in subs.enumerated() { + log(" sublayer[\(j)]: \(type(of: sl)) frame=\(sl.frame) bounds=\(sl.bounds) hidden=\(sl.isHidden)") + } + } + } + } + log("") + } + + // MARK: - 毛玻璃背板 private func setupVisualEffect() { guard let window = window else { return } @@ -23,7 +95,7 @@ final class FloatingWindowController: NSWindowController { ve.material = .hudWindow ve.blendingMode = .behindWindow ve.state = .active - ve.alphaValue = 1.0 // 始终不透明,维持窗口合成通道 + ve.alphaValue = 1.0 ve.translatesAutoresizingMaskIntoConstraints = false ve.layer?.cornerRadius = 16 @@ -43,6 +115,35 @@ final class FloatingWindowController: NSWindowController { ve.topAnchor.constraint(equalTo: contentView.topAnchor), ve.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), ]) + + applyBackdropEnabled(Configuration.shared.backdropEnabled) + } + + // MARK: - 背板开关 + + private func observeBackdropToggle() { + NotificationCenter.default.addObserver( + forName: .backdropEnabledChanged, object: nil, queue: .main + ) { [weak self] note in + guard let enabled = note.object as? Bool else { return } + self?.applyBackdropEnabled(enabled) + } + } + + private func applyBackdropEnabled(_ enabled: Bool) { + guard let ve = visualEffect else { return } + if enabled { + ve.blendingMode = .behindWindow + ve.alphaValue = 1.0 + ve.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.35).cgColor + } else { + ve.blendingMode = .withinWindow + ve.alphaValue = 0.0 + ve.layer?.backgroundColor = NSColor.clear.cgColor + } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in + self?.dumpGeometry("backdropEnabled=\(enabled)") + } } // MARK: - SwiftUI 嵌入 @@ -52,7 +153,7 @@ final class FloatingWindowController: NSWindowController { let hv = NSHostingView(rootView: rootView) hv.wantsLayer = true - hv.layer?.cornerRadius = 16 + hv.layer?.cornerRadius = Configuration.shared.backdropEnabled ? 16 : 0 hv.layer?.masksToBounds = true hv.translatesAutoresizingMaskIntoConstraints = false @@ -88,6 +189,9 @@ final class FloatingWindowController: NSWindowController { } window.makeKeyAndOrderFront(nil) NSApp.activate(ignoringOtherApps: false) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in + self?.dumpGeometry("show") + } } func hide() { diff --git a/Sources/FloatingMonitor/Configuration.swift b/Sources/FloatingMonitor/Configuration.swift index a6435cd..316c03d 100644 --- a/Sources/FloatingMonitor/Configuration.swift +++ b/Sources/FloatingMonitor/Configuration.swift @@ -110,6 +110,16 @@ final class Configuration: ObservableObject { didSet { save("windowHeight", windowHeight) } } + @Published var backdropEnabled: Bool { + didSet { + save("backdropEnabled", backdropEnabled) + NotificationCenter.default.post(name: .backdropEnabledChanged, object: backdropEnabled) + } + } + @Published var invertTextColor: Bool { + didSet { save("invertTextColor", invertTextColor) } + } + @Published var gaugeAnimationEnabled: Bool { didSet { save("gaugeAnimationEnabled", gaugeAnimationEnabled) } } @@ -192,6 +202,8 @@ final class Configuration: ObservableObject { fontSize = defaults.object(forKey: "fontSize") as? Double ?? 12 windowWidth = defaults.object(forKey: "windowWidth") as? Double ?? 250 windowHeight = defaults.object(forKey: "windowHeight") as? Double ?? 300 + backdropEnabled = defaults.object(forKey: "backdropEnabled") as? Bool ?? true + invertTextColor = defaults.object(forKey: "invertTextColor") as? Bool ?? false gaugeAnimationEnabled = defaults.object(forKey: "gaugeAnimationEnabled") as? Bool ?? true textAnimationEnabled = defaults.object(forKey: "textAnimationEnabled") as? Bool ?? true diff --git a/Sources/FloatingMonitor/ContentView.swift b/Sources/FloatingMonitor/ContentView.swift index a8f7c51..db2dcf7 100644 --- a/Sources/FloatingMonitor/ContentView.swift +++ b/Sources/FloatingMonitor/ContentView.swift @@ -33,6 +33,7 @@ struct ContentView: View { } .frame(minWidth: 240, idealWidth: 280, maxWidth: 400, minHeight: 200, idealHeight: 480, maxHeight: 700) + .environment(\.colorScheme, config.invertTextColor ? .dark : .light) .onAppear { monitor.interval = config.refreshInterval monitor.topN = config.processCount diff --git a/Sources/FloatingMonitor/Views/SettingsView.swift b/Sources/FloatingMonitor/Views/SettingsView.swift index 3a09ca8..223d1f2 100644 --- a/Sources/FloatingMonitor/Views/SettingsView.swift +++ b/Sources/FloatingMonitor/Views/SettingsView.swift @@ -269,6 +269,26 @@ struct SettingsView: View { .frame(width: 38, alignment: .trailing) } + HStack { + Text("毛玻璃背板") + .font(.system(size: 12)) + Spacer() + Toggle("", isOn: $config.backdropEnabled) + .toggleStyle(.switch) + .scaleEffect(0.7) + .frame(width: 38) + } + + HStack { + Text("反转文字颜色") + .font(.system(size: 12)) + Spacer() + Toggle("", isOn: $config.invertTextColor) + .toggleStyle(.switch) + .scaleEffect(0.7) + .frame(width: 38) + } + HStack { Text("图形动画") .font(.system(size: 12))