This PR introduces fullscreen mode for happiNESs. However, you need to be on macOS 15.1 or later in order to be able to do this, and so it was critical that the emulator continued to run in windowed mode without regression, which it does. Some crucial points to make:
Getting this to work was really tricky. There may be other clearer ways but in order to be able to capture when the window entered or exited fullscreen mode, and then be able to set compute the proper scaling accordingly, I needed to add the onReceive() modifier on the topmost view in the window, and capture events from a static NotificationCenter.Publisher configured to send those events. Inside the closure for the modifier, the computer's window height is captured, and the new scale computed from it.
The closure above also captures the original scaling factor from when it was originally in windowed mode, and restores it upon exiting fullscreen mode.
It was extremely difficult getting the ContentView centered within the window in fullscreen mode. Using default Spacers within an HStack weren't sufficient; the minLengths had to be large enough for it to work but I didn't want to hardcode any one magic number. And so, I chose to compute the half width of the main screen and use that as the minLength parameter for each Spacer and that worked.
The app window remains unresizable in windowed mode.
There are now keyboard bindings to the scaling actions, namely, ⌘-1, ⌘-2, or ⌘-3.
Scaling is disabled when in fullscreen mode for obvious reasons; this is done by inspecting the state of the isFullscreen var which is set and reset in the onReceive() closure described above.
This PR introduces fullscreen mode for happiNESs. However, you need to be on macOS 15.1 or later in order to be able to do this, and so it was critical that the emulator continued to run in windowed mode without regression, which it does. Some crucial points to make:
onReceive()
modifier on the topmost view in the window, and capture events from a staticNotificationCenter.Publisher
configured to send those events. Inside the closure for the modifier, the computer's window height is captured, and the new scale computed from it.ContentView
centered within the window in fullscreen mode. Using defaultSpacer
s within anHStack
weren't sufficient; theminLength
s had to be large enough for it to work but I didn't want to hardcode any one magic number. And so, I chose to compute the half width of the main screen and use that as theminLength
parameter for eachSpacer
and that worked.isFullscreen
var which is set and reset in theonReceive()
closure described above.