| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- import AppKit
- import SwiftUI
- struct WindowFullScreenObserver: NSViewRepresentable {
- @Binding var isFullScreen: Bool
- func makeCoordinator() -> Coordinator {
- Coordinator(isFullScreen: $isFullScreen)
- }
- func makeNSView(context: Context) -> WindowObservationView {
- let view = WindowObservationView()
- view.onWindowChange = { [weak coordinator = context.coordinator] window in
- coordinator?.observe(window: window)
- }
- return view
- }
- func updateNSView(_ nsView: WindowObservationView, context: Context) {
- context.coordinator.observe(window: nsView.window)
- }
- final class Coordinator {
- @Binding private var isFullScreen: Bool
- private var observers: [NSObjectProtocol] = []
- private weak var observedWindow: NSWindow?
- init(isFullScreen: Binding<Bool>) {
- _isFullScreen = isFullScreen
- }
- func observe(window: NSWindow?) {
- guard window !== observedWindow else {
- if let window {
- updateFullScreenState(for: window)
- }
- return
- }
- removeObservers()
- observedWindow = window
- guard let window else {
- isFullScreen = false
- return
- }
- updateFullScreenState(for: window)
- let center = NotificationCenter.default
- observers = [
- center.addObserver(
- forName: NSWindow.didEnterFullScreenNotification,
- object: window,
- queue: .main
- ) { [weak self] _ in
- self?.updateFullScreenState(for: window)
- },
- center.addObserver(
- forName: NSWindow.didExitFullScreenNotification,
- object: window,
- queue: .main
- ) { [weak self] _ in
- self?.updateFullScreenState(for: window)
- },
- ]
- }
- private func updateFullScreenState(for window: NSWindow) {
- isFullScreen = window.styleMask.contains(.fullScreen)
- }
- private func removeObservers() {
- observers.forEach { NotificationCenter.default.removeObserver($0) }
- observers.removeAll()
- }
- deinit {
- removeObservers()
- }
- }
- }
- final class WindowObservationView: NSView {
- var onWindowChange: ((NSWindow?) -> Void)?
- override func viewDidMoveToWindow() {
- super.viewDidMoveToWindow()
- onWindowChange?(window)
- }
- override func hitTest(_ point: NSPoint) -> NSView? {
- nil
- }
- }
- private struct FullScreenLayoutModifier: ViewModifier {
- @State private var isFullScreen = false
- func body(content: Content) -> some View {
- VStack(spacing: 0) {
- if isFullScreen {
- AppTheme.sidebarBackground
- .frame(height: AppWindow.fullScreenTopInset)
- .frame(maxWidth: .infinity)
- .allowsHitTesting(false)
- }
- content
- .frame(maxWidth: .infinity, maxHeight: .infinity)
- }
- .frame(maxWidth: .infinity, maxHeight: .infinity)
- .background {
- WindowFullScreenObserver(isFullScreen: $isFullScreen)
- }
- }
- }
- extension View {
- func fullScreenLayoutWhenNeeded() -> some View {
- modifier(FullScreenLayoutModifier())
- }
- }
|