Last active
May 3, 2023 04:59
-
-
Save gwl/848eb9e3deb481f9a9d4cce17985a17d to your computer and use it in GitHub Desktop.
ContentView for SaverDebugger app
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // ContentView.swift | |
| // SaverDebugger | |
| // | |
| // Created by Gary W. Longsine on 4/9/23. | |
| // | |
| import SwiftUI | |
| import ScreenSaver | |
| import os | |
| struct windowSize { | |
| // changes let to static - read comments | |
| var minWidth : CGFloat = 640 | |
| var minHeight : CGFloat = 400 | |
| var maxWidth : CGFloat = 5120 | |
| var maxHeight : CGFloat = 2880 | |
| } | |
| struct ContentView: View { | |
| @State private var currentScreenSaverView: ScreenSaverView? = nil | |
| @State private var selectedSaver: Int = 2 | |
| @State private var selectedWindowSize: Int = 0 | |
| // @State private var selectedWindowSize = "MacBook" | |
| @State private var currentSaver: AnyView? = nil | |
| @State private var timer: Timer? = nil | |
| @State private var recordingManager = RecordingManager() | |
| let logger = Logger(subsystem: "com.illumineX.SaverDebugger", category: "SaverDebugger") | |
| let saverOptions = ["iX Circles", "iX Game of Life", "iX Hyperspace", "iX Rings", "iX Stars"] | |
| // window sizes for testing | |
| let windowSizes = ["Record Video": CGSize(width: 640, height: 400), | |
| "MacBook": CGSize(width: 1280, height: 800), | |
| "MacBook 16:10 (640x400)": CGSize(width: 640, height: 400), | |
| "MacBook (Retina, 12-inch, 226 ppi)": CGSize(width: 2304 / 2, height: 1440 / 2), | |
| "MacBook Pro": CGSize(width: 1440, height: 900), | |
| "MacBook Pro (17-inch)": CGSize(width: 1920, height: 1200), | |
| "MacBook Pro (Retina, 13.3-inch, 113 ppi)": CGSize(width: 1280, height: 800), | |
| "MacBook Pro (Retina, 13.3-inch, 227 ppi)": CGSize(width: 2560 / 2, height: 1600 / 2), | |
| "MacBook Pro (Retina, 15.4-inch, 220 ppi)": CGSize(width: 2880 / 2, height: 1800 / 2), | |
| "MacBook Air (13.3-inch, 127 ppi)": CGSize(width: 1440, height: 900), | |
| "MacBook Air (11.6-inch, 135 ppi)": CGSize(width: 1366, height: 768), | |
| "iMac (21.5-inch, 102 ppi)": CGSize(width: 1920, height: 1080), | |
| "iMac (Retina, 21.5-inch, 219 ppi)": CGSize(width: 2650 / 2, height: 1440 / 2), | |
| "iMac (Retina 5K, 27-inch, 218 ppi)": CGSize(width: 5120 / 2, height: 2650 / 2), | |
| "Apple Studio display 5k (27-inch, 218 ppi)": CGSize(width: 5120 / 2, height: 2880 / 2), | |
| "Apple Thunderbolt display (27-inch, 109 ppi)": CGSize(width: 2560, height: 1440)] | |
| var body: some View { | |
| VStack { | |
| VStack { | |
| controlsSection(selectedSaver: $selectedSaver, selectedWindowSize: $selectedWindowSize, saverOptions: saverOptions, windowSizes: windowSizes) | |
| } | |
| currentSaver | |
| // .frame(maxWidth: .infinity, maxHeight: .infinity) | |
| .frame(minWidth: windowSize().minWidth, minHeight: windowSize().minHeight) | |
| .frame(maxWidth: windowSize().maxWidth, maxHeight: windowSize().maxHeight) | |
| .border(Color.purple, width: 3) | |
| } | |
| .onAppear { | |
| loadScreensaver() | |
| //updateWindowSize() | |
| if let screenSaverView = currentScreenSaverView { | |
| recordingManager.startRecording(for: currentScreenSaverView!) | |
| } | |
| logger.info("SaverDebugger: loadSaver() called by onAppear") | |
| } | |
| .onChange(of: selectedSaver) { _ in | |
| loadScreensaver() | |
| // updateWindowSize() | |
| logger.info("SaverDebugger: loadSaver() called by onChange selectedSaver") | |
| } | |
| .onChange(of: selectedWindowSize) { _ in | |
| //loadScreensaver() | |
| updateWindowSize() | |
| logger.info("SaverDebugger: loadSaver() called by onChange selectedWindowSize") | |
| } | |
| } | |
| func loadScreensaver() { | |
| timer?.invalidate() | |
| let sortedSizes = windowSizes.values.sorted { | |
| $0.width * $0.height < $1.width * $1.height | |
| } | |
| let screenSize = windowSizes[Array(windowSizes.keys.sorted())[selectedWindowSize]]! | |
| let defaultWidth = 1280 | |
| let defaultHeight = 800 | |
| switch selectedSaver { | |
| case 0: | |
| let view = IXCirclesView(frame: NSRect(origin: .zero, size: screenSize )) | |
| // currentSaver = AnyView(view) // Use this instead for a SwiftUI screenaver view? | |
| currentSaver = AnyView(NSViewWrapper(view)) // for an AppKit NSView screensaver view | |
| currentScreenSaverView = view | |
| //logger.info("SaverDebugger: IXCirclesView selected") | |
| case 1: | |
| let view = IXGameOfLifeView(frame: NSRect(origin: .zero, size: screenSize )) | |
| currentSaver = AnyView(NSViewWrapper(view)) | |
| currentScreenSaverView = view | |
| //logger.info("SaverDebugger: IXGameOfLifeView selected") | |
| case 2: | |
| let view = IXHyperspaceView(frame: NSRect(origin: .zero, size: screenSize )) | |
| currentSaver = AnyView(NSViewWrapper(view)) | |
| currentScreenSaverView = view | |
| //logger.info("SaverDebugger: IXHyperspaceView selected") | |
| case 3: | |
| let view = IXRingsView(frame: NSRect(origin: .zero, size: screenSize )) | |
| currentSaver = AnyView(NSViewWrapper(view)) | |
| currentScreenSaverView = view | |
| //logger.info("SaverDebugger: IXHyperspaceView selected") | |
| case 4: | |
| let view = IXStarsSaverView(frame: NSRect(origin: .zero, size: screenSize )) | |
| currentSaver = AnyView(NSViewWrapper(view)) | |
| currentScreenSaverView = view | |
| //logger.info("SaverDebugger: IXHyperspaceView selected") | |
| default: | |
| break | |
| } | |
| if let screensaverView = currentScreenSaverView { | |
| timer = Timer.scheduledTimer(withTimeInterval: screensaverView.animationTimeInterval, repeats: true) { _ in | |
| screensaverView.animateOneFrame() | |
| } | |
| } | |
| if let window = currentWindow() { | |
| let contentRect = NSRect(origin: .zero, size: screenSize ) | |
| let frameRect = window.frameRect(forContentRect: contentRect) | |
| let newOrigin = CGPoint(x: window.frame.origin.x, y: window.frame.origin.y + window.frame.size.height - frameRect.size.height) | |
| window.setFrame(NSRect(origin: newOrigin, size: frameRect.size), display: true, animate: true) | |
| // Update the window's contentView | |
| let contentView = NSHostingView(rootView: self.body) | |
| contentView.frame = contentRect | |
| window.contentView = contentView | |
| } | |
| } | |
| // a function to provide the controls view | |
| func controlsSection(selectedSaver: Binding<Int>, selectedWindowSize: Binding<Int>, saverOptions: [String], windowSizes: [String: CGSize]) -> some View { | |
| HStack { | |
| Picker("Screensaver", selection: selectedSaver) { | |
| ForEach(0..<saverOptions.count, id: \.self) { | |
| Text(saverOptions[$0]).tag($0) | |
| } | |
| } | |
| .onChange(of: selectedSaver.wrappedValue) { newValue in | |
| updateWindowSize() | |
| logger.info("SaverDebugger: \(saverOptions[newValue]) selected by Picker") | |
| } | |
| .padding() | |
| Picker("Window Size", selection: $selectedWindowSize) { | |
| ForEach(windowSizes.keys.sorted(), id: \.self) { key in | |
| Text(key).tag(windowSizes.keys.sorted().firstIndex(of: key)!) | |
| } | |
| } | |
| } | |
| } | |
| // two functions and one extention to support window resizing | |
| // a helper function to get the current window | |
| func currentWindow() -> NSWindow? { | |
| logger.info("SaverDebugger: currentWindow()") | |
| return NSApplication.shared.windows.first { window in | |
| return window.contentView?.superview is NSHostingView<ContentView> | |
| } | |
| } | |
| // two functions to support window resizing | |
| private func updateWindowSize() { | |
| guard let window = NSApp.windows.first, | |
| // let selectedSize = windowSizes[selectedSize] | |
| let newSize = windowSizes[Array(windowSizes.keys.sorted())[selectedWindowSize]] | |
| else { return } | |
| let newWindowWidth = newSize.width | |
| let newWindowHeight = newSize.height + 44 + window.titleBarHeight | |
| let currentFrame = window.frame | |
| // Calculate the new frame origin | |
| let newX = currentFrame.origin.x | |
| let newY = currentFrame.origin.y + currentFrame.size.height - newWindowHeight | |
| let newFrame = CGRect(x: newX, y: newY, width: newWindowWidth, height: newWindowHeight) | |
| window.setFrame(newFrame, display: true, animate: true) | |
| } | |
| private func controlsSectionHeight() -> CGFloat { | |
| return 44.0 | |
| } | |
| } | |
| // an extension to support window resizing | |
| extension NSWindow { | |
| var titleBarHeight: CGFloat { | |
| return contentLayoutRect.height - contentRect(forFrameRect: frame).height | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment