visionOS: native settings menu

This commit is contained in:
Jesús A. Álvarez 2024-02-10 14:33:11 +01:00
parent ed99d1bbfb
commit 632f01f169
5 changed files with 156 additions and 7 deletions

View File

@ -61,6 +61,7 @@
28D3C6152B7681420079E915 /* VisionSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28D3C6142B7681420079E915 /* VisionSupport.swift */; };
28D3C6172B76B8970079E915 /* DefaultSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28D3C6162B76B8970079E915 /* DefaultSceneDelegate.swift */; };
28D3C61B2B7781700079E915 /* KeyboardSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28D3C61A2B7781700079E915 /* KeyboardSceneDelegate.swift */; };
28D3C61D2B7795060079E915 /* SettingsMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28D3C61C2B7795060079E915 /* SettingsMenu.swift */; };
28D5A3FD1CD6868F001A33F6 /* TouchScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 28D5A3FC1CD6868E001A33F6 /* TouchScreen.m */; };
28E3B7DF251D0F13007C273F /* MOUSEMDV.c in Sources */ = {isa = PBXBuildFile; fileRef = 28E3B7CC251D0F12007C273F /* MOUSEMDV.c */; };
28E3B7E0251D0F13007C273F /* MOUSEMDV.c in Sources */ = {isa = PBXBuildFile; fileRef = 28E3B7CC251D0F12007C273F /* MOUSEMDV.c */; };
@ -289,6 +290,7 @@
28D3C6142B7681420079E915 /* VisionSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisionSupport.swift; sourceTree = "<group>"; };
28D3C6162B76B8970079E915 /* DefaultSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultSceneDelegate.swift; sourceTree = "<group>"; };
28D3C61A2B7781700079E915 /* KeyboardSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardSceneDelegate.swift; sourceTree = "<group>"; };
28D3C61C2B7795060079E915 /* SettingsMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsMenu.swift; sourceTree = "<group>"; };
28D5A3FB1CD6868E001A33F6 /* TouchScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TouchScreen.h; sourceTree = "<group>"; };
28D5A3FC1CD6868E001A33F6 /* TouchScreen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TouchScreen.m; sourceTree = "<group>"; };
28E3B7CC251D0F12007C273F /* MOUSEMDV.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = MOUSEMDV.c; sourceTree = "<group>"; };
@ -619,6 +621,7 @@
28F676C61CD15E0B00FC6FA6 /* ViewController.h */,
28F676C71CD15E0B00FC6FA6 /* ViewController.m */,
28D3C6142B7681420079E915 /* VisionSupport.swift */,
28D3C61C2B7795060079E915 /* SettingsMenu.swift */,
28848B601CDE97D600B86C45 /* InsertDiskViewController.h */,
28848B611CDE97D600B86C45 /* InsertDiskViewController.m */,
28848B631CDE97E900B86C45 /* SettingsViewController.h */,
@ -1308,6 +1311,7 @@
28BA89881CE73FBC00A98104 /* MNVMApplication.m in Sources */,
28F6B4CF1CF77099002D76D0 /* compat.m in Sources */,
28F6B4521CF07C48002D76D0 /* UIImage+DiskImageIcon.m in Sources */,
28D3C61D2B7795060079E915 /* SettingsMenu.swift in Sources */,
28BA897F1CE7315400A98104 /* KBKeyboardLayout.m in Sources */,
28D3C61B2B7781700079E915 /* KeyboardSceneDelegate.swift in Sources */,
28848B621CDE97D600B86C45 /* InsertDiskViewController.m in Sources */,

View File

@ -10,7 +10,7 @@
@import CoreGraphics;
@import QuartzCore;
typedef enum : NSInteger {
typedef NS_ENUM(NSInteger, EmulatorSpeed) {
EmulatorSpeedAllOut = -1,
EmulatorSpeed1x = 0,
EmulatorSpeed2x = 1,
@ -18,7 +18,7 @@ typedef enum : NSInteger {
EmulatorSpeed8x = 3,
EmulatorSpeed16x = 4,
EmulatorSpeed32x = 5
} EmulatorSpeed;
};
@protocol Emulator <NSObject>

View File

@ -4,5 +4,6 @@
#import "ViewController.h"
#import "AppDelegate.h"
#import "EmulatorProtocol.h"
#import "KBKeyboardView.h"
#import "KBKeyboardLayout.h"

View File

@ -0,0 +1,148 @@
//
// SettingsMenu.swift
// Mini vMac
//
// Created by Jesús A. Álvarez on 2024-02-10.
// Copyright © 2024 namedfork. All rights reserved.
//
import SwiftUI
struct SettingsMenu: View {
var body: some View {
Menu() {
Section("Settings") {
SpeedMenu()
MachineMenu()
DisplayScalingMenu()
}
} label: {
Image(systemName: "gear")
}.menuOrder(.fixed)
}
}
struct SpeedMenu: View {
@AppStorage("speedValue") var currentSpeed: EmulatorSpeed = .speed1x
private var currentSpeedImage: String {
switch currentSpeed {
case .speed1x:
"tortoise"
case .speedAllOut:
"hare"
case .speed2x:
"2.square"
case .speed4x:
"4.square"
case .speed8x:
"8.square"
case .speed16x:
"16.square"
case .speed32x:
"32.square"
@unknown default:
"hare"
}
}
var body: some View {
Menu("Speed", systemImage:currentSpeedImage) {
SpeedButton(label: "1x", speed: .speed1x)
SpeedButton(label: "2x", speed: .speed2x)
SpeedButton(label: "4x", speed: .speed4x)
SpeedButton(label: "8x", speed: .speed8x)
SpeedButton(label: "16x", speed: .speed16x)
SpeedButton(label: "32x", speed: .speed32x)
SpeedButton(label: "Unlimited", speed: .speedAllOut)
}.menuOrder(.fixed)
}
}
struct SpeedButton: View {
@AppStorage("speedValue") var currentSpeed: EmulatorSpeed = .speed1x
let label: LocalizedStringKey
let speed: EmulatorSpeed
var body: some View {
if currentSpeed == speed {
Button(label, systemImage: "checkmark") {}
} else {
Button {
currentSpeed = speed
} label: {
Text(label)
}
}
}
}
struct MachineMenu: View {
@AppStorage("machine") var currentMachine: String = "Plus4M"
let bundles = AppDelegate.shared.emulatorBundles?.sorted(by: { $0.bundleIdentifier! < $1.bundleIdentifier! }) ?? []
var body: some View {
Menu("Machine", systemImage: "desktopcomputer") {
ForEach(bundles, id: \.bundleIdentifier) { bundle in
MachineButton(bundle: bundle)
}
}.menuOrder(.fixed)
}
}
struct MachineButton: View {
@AppStorage("machine") var currentMachine: String = "Plus4M"
var bundle: Bundle
var body: some View {
if currentMachine == bundle.name {
Label {
Text("\(bundle.displayName ?? bundle.name)\n\(bundle.getInfoString ?? "")")
} icon: {
Image(systemName: "checkmark")
}
} else {
Button {
if !AppDelegate.emulator.anyDiskInserted {
currentMachine = bundle.name
AppDelegate.shared.loadAndStartEmulator()
}
} label: {
Text("\(bundle.displayName ?? bundle.name)\n\(bundle.getInfoString ?? "")")
}
}
}
}
fileprivate extension Bundle {
var name: String { ((self.bundlePath as NSString).lastPathComponent as NSString).deletingPathExtension }
var displayName: String? { self.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String }
var getInfoString: String? { self.object(forInfoDictionaryKey: "CFBundleGetInfoString") as? String }
}
struct DisplayScalingMenu: View {
@AppStorage("screenFilter") var scalingFilter: CALayerContentsFilter = .nearest
static let filters = [
DisplayScalingFilter(filter: .nearest, name: "Nearest"),
DisplayScalingFilter(filter: .linear, name: "Linear"),
DisplayScalingFilter(filter: .trilinear, name: "Trilinear"),
]
var body: some View {
Menu("Display Scaling", systemImage: "rectangle.and.text.magnifyingglass") {
ForEach(DisplayScalingMenu.filters, id: \.filter) { filter in
if scalingFilter == filter.filter {
Label {
Text(filter.name)
} icon: {
Image(systemName: "checkmark")
}
} else {
Button(filter.name) {
scalingFilter = filter.filter
}
}
}
}.menuOrder(.fixed)
}
}
struct DisplayScalingFilter {
let filter: CALayerContentsFilter
let name: String
}

View File

@ -20,11 +20,7 @@ extension ViewController {
VStack {
Spacer(minLength: 80.0)
HStack {
Button(action: {
AppDelegate.shared.showSettings(self)
}, label: {
Image(systemName: "gear")
}).glassBackgroundEffect()
SettingsMenu().glassBackgroundEffect()
Button(action: {
AppDelegate.shared.showInsertDisk(self)