ClashX.Meta/ClashX/ClashWindowController.swift

88 lines
2.8 KiB
Swift

//
// ClashWindowController.swift
// ClashX
//
// Created by yicheng on 2023/7/5.
// Copyright © 2023 west2online. All rights reserved.
//
import AppKit
private class ClashWindowsRecorder {
static let shared = ClashWindowsRecorder()
var windowControllers = [NSWindowController]() {
didSet {
if windowControllers.isEmpty {
NSApp.setActivationPolicy(.accessory)
} else {
if NSApp.activationPolicy() == .accessory {
NSApp.setActivationPolicy(.regular)
}
}
}
}
}
class ClashWindowController<T: NSViewController>: NSWindowController, NSWindowDelegate {
var onWindowClose: (() -> Void)?
private var fromCache = false
private var lastSize: CGSize? {
get {
if let str = UserDefaults.standard.value(forKey: "lastSize.\(T.className())") as? String {
return NSSizeFromString(str) as CGSize
}
return nil
}
set {
if let size = newValue {
UserDefaults.standard.set(NSStringFromSize(size), forKey: "lastSize.\(T.className())")
}
}
}
static func create() -> NSWindowController {
if let wc = ClashWindowsRecorder.shared.windowControllers.first(where: { $0 is Self }) {
(wc as? ClashWindowController)?.fromCache = true
return wc
}
let win = NSWindow()
let wc = ClashWindowController(window: win)
if let X = T.self as? NibLoadable.Type {
wc.contentViewController = (X.createFromNib(in: .main) as! NSViewController)
} else {
wc.contentViewController = T()
}
win.titlebarAppearsTransparent = false
win.styleMask.insert(.closable)
win.styleMask.insert(.resizable)
win.styleMask.insert(.miniaturizable)
if let title = wc.contentViewController?.title {
win.title = title
}
ClashWindowsRecorder.shared.windowControllers.append(wc)
return wc
}
override func showWindow(_ sender: Any?) {
super.showWindow(sender)
NSApp.activate(ignoringOtherApps: true)
if !fromCache, let lastSize = lastSize, lastSize != .zero {
window?.setContentSize(lastSize)
window?.center()
}
window?.makeKeyAndOrderFront(self)
window?.delegate = self
NSApp.activate(ignoringOtherApps: true)
window?.makeKeyAndOrderFront(nil)
}
func windowWillClose(_ notification: Notification) {
ClashWindowsRecorder.shared.windowControllers.removeAll(where: { $0 == self })
onWindowClose?()
if let win = window {
if !win.styleMask.contains(.fullScreen) {
lastSize = win.frame.size
}
}
}
}