Files
OpenStats/Packages/OpenStatsKit/Sources/OpenStatsUI/AppKit/MainWindowController.swift
T
gentpan c9d722f20d 账号同步、磁盘工具、IP 归属地数据源与纯净度、弹窗与设置调整
- 账号与同步:GitHub / Google / Apple 登录后把菜单栏项目与风格等偏好同步到 getopenstats.com,换一台 Mac 登录即恢复(AccountSync 模块、SyncController、SettingsDocument、账号设置页,openstats:// 回调)
- 磁盘页成为工具页:已用 / 可清除 / 可用分段容量条、空间分析、文件系统检查、本地快照清理(辅助工具协议 v4)、其他磁盘推出
- 公网 IP:归属地数据源可选 CleanIP.io / ipapi.is / DB-IP / ipinfo.io / 本地 GeoLite2,每台 Mac 自己查、本地缓存 7 天;IPv4 / IPv6 双栈各自查询与切换;弹窗显示 ASN、运营方、原生 / 住宅等徽章与纯净度评分色带;接口区块可重置上传 / 下载统计
- 弹窗:右上角按钮改为该指标自己的图标 + 总设置;主窗口页面标题栏的开关点不动已修复;滚动条改为悬浮式,内容不再偏左;网络弹窗卡片被标题行撑宽的问题已修复
- 设置:⌘ 拖动排序说明;每个菜单栏项目单独选风格并带实时预览;网络与磁盘的进程榜单固定 5 行并平滑排序;CPU 持续高负载通知
- 设计系统:分段条、评分色带、标签徽章、胶囊切换、刷新按钮等组件与徽章配色 token
- 更新日志、架构说明、README 与译文同步
2026-09-15 03:37:24 +05:00

101 lines
4.1 KiB
Swift

import AppKit
import SwiftUI
/// 主窗口。打开时应用出现在程序坞与 ⌘Tab 中,关闭后回到仅菜单栏运行
@MainActor
final class MainWindowController: NSObject, NSWindowDelegate {
private let model: AppModel
private var window: NSWindow?
var onVisibilityChange: ((Bool) -> Void)?
init(model: AppModel) {
self.model = model
super.init()
}
var isVisible: Bool { window?.isVisible == true }
/// 登录窗口等系统面板挂靠用
var nsWindow: NSWindow? { window }
func close() {
window?.performClose(nil)
}
func show(tab: PanelTab?) {
if let tab { model.settings.panelTab = tab }
let window = self.window ?? makeWindow()
self.window = window
if !window.isVisible {
window.setFrame(initialFrame(for: window), display: false)
}
NSApp.activate()
window.makeKeyAndOrderFront(nil)
model.isMainWindowVisible = true
onVisibilityChange?(true)
}
private func makeWindow() -> NSWindow {
let width = DS.Size.sidebarWidth + DS.Size.panelWidth
let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: DS.Size.sidebarWidth + DS.Size.windowDefaultContentWidth,
height: DS.Size.panelMinHeight * 2),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered,
defer: false)
window.title = "OpenStats"
WindowChrome.apply(to: window)
window.isReleasedWhenClosed = false
window.delegate = self
window.contentMinSize = NSSize(width: width, height: DS.Size.panelMinHeight * 1.5)
// 默认尺寸调大后换一个保存名,旧的小窗口尺寸不再沿用
window.setFrameAutosaveName("OpenStatsMainWindow.v2")
window.contentView = NSHostingView(rootView: MainWindowView().environment(model))
return window
}
/// 首次打开时按仪表盘内容高度确定窗口大小,之后沿用用户调整过的尺寸
private func initialFrame(for window: NSWindow) -> NSRect {
let visible = (window.screen ?? NSScreen.main)?.visibleFrame ?? .zero
if window.frame.height > DS.Size.panelMinHeight * 2, visible.intersects(window.frame) {
return window.frame
}
let measuring = NSHostingView(rootView: MainWindowView().environment(model).environment(\.isSnapshot, true))
let height = min(measuring.fittingSize.height, visible.height - DS.Space.s12)
// 至少 720pt 高(屏幕放得下时),页面较短也不会开成一个小窗口
let size = NSSize(width: min(window.frame.width, visible.width - DS.Space.s12),
height: min(max(height, DS.Size.windowDefaultHeight), visible.height - DS.Space.s12))
return NSRect(x: visible.midX - size.width / 2, y: visible.midY - size.height / 2,
width: size.width, height: size.height)
}
func windowWillClose(_ notification: Notification) {
model.isMainWindowVisible = false
onVisibilityChange?(false)
}
func windowDidMiniaturize(_ notification: Notification) {
model.isMainWindowVisible = false
}
func windowDidDeminiaturize(_ notification: Notification) {
model.isMainWindowVisible = true
}
}
/// OpenStats 窗口的统一外观:透明标题栏,内容铺满整个窗口,底色与页面一致
@MainActor
enum WindowChrome {
static func apply(to window: NSWindow) {
window.styleMask.insert(.fullSizeContentView)
window.titleVisibility = .hidden
window.titlebarAppearsTransparent = true
window.titlebarSeparatorStyle = .none
window.backgroundColor = .dsBackground
// 空的紧凑工具栏让标题栏与顶栏同高(40pt),红绿灯按钮与页面标题对齐
let toolbar = NSToolbar(identifier: "OpenStats.\(window.title)")
toolbar.showsBaselineSeparator = false
window.toolbar = toolbar
window.toolbarStyle = .unifiedCompact
}
}