mirror of
https://github.com/somegeekintn/SimDirs.git
synced 2026-03-25 08:55:54 +00:00
Added Increase Contrast controls to devices.
This commit is contained in:
parent
61f82f2115
commit
b68c2779fd
10 changed files with 354 additions and 27 deletions
|
|
@ -31,6 +31,8 @@
|
|||
C982F877283D020C00D491F4 /* SimProductFamily.swift in Sources */ = {isa = PBXBuildFile; fileRef = C982F876283D020C00D491F4 /* SimProductFamily.swift */; };
|
||||
C982F879283D042E00D491F4 /* SimModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C982F878283D042E00D491F4 /* SimModel.swift */; };
|
||||
C982F87B283E40C800D491F4 /* SimCtl.swift in Sources */ = {isa = PBXBuildFile; fileRef = C982F87A283E40C800D491F4 /* SimCtl.swift */; };
|
||||
C9BF5232289FE95D00BDDC91 /* DescriptiveToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9BF5231289FE95D00BDDC91 /* DescriptiveToggle.swift */; };
|
||||
C9BF5234289FE99600BDDC91 /* DescriptiveToggleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9BF5233289FE99600BDDC91 /* DescriptiveToggleStyle.swift */; };
|
||||
C9D73C25285C8C0C0044A279 /* SourceItemData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D73C24285C8C0C0044A279 /* SourceItemData.swift */; };
|
||||
C9D73C29285C8C4B0044A279 /* SourceItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D73C28285C8C4B0044A279 /* SourceItem.swift */; };
|
||||
C9DD54C32860936D00D46AB3 /* SourceItemGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9DD54C22860936D00D46AB3 /* SourceItemGroup.swift */; };
|
||||
|
|
@ -72,6 +74,8 @@
|
|||
C982F876283D020C00D491F4 /* SimProductFamily.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimProductFamily.swift; sourceTree = "<group>"; };
|
||||
C982F878283D042E00D491F4 /* SimModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimModel.swift; sourceTree = "<group>"; };
|
||||
C982F87A283E40C800D491F4 /* SimCtl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimCtl.swift; sourceTree = "<group>"; };
|
||||
C9BF5231289FE95D00BDDC91 /* DescriptiveToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptiveToggle.swift; sourceTree = "<group>"; };
|
||||
C9BF5233289FE99600BDDC91 /* DescriptiveToggleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptiveToggleStyle.swift; sourceTree = "<group>"; };
|
||||
C9D73C24285C8C0C0044A279 /* SourceItemData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceItemData.swift; sourceTree = "<group>"; };
|
||||
C9D73C28285C8C4B0044A279 /* SourceItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceItem.swift; sourceTree = "<group>"; };
|
||||
C9DD54C22860936D00D46AB3 /* SourceItemGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceItemGroup.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -159,6 +163,8 @@
|
|||
C9DD54CC2860992200D46AB3 /* Model Views */,
|
||||
C90DCC152896B0370072E403 /* AppearancePicker.swift */,
|
||||
C90DCC132896AAAA0072E403 /* ContentHeader.swift */,
|
||||
C9BF5231289FE95D00BDDC91 /* DescriptiveToggle.swift */,
|
||||
C9BF5233289FE99600BDDC91 /* DescriptiveToggleStyle.swift */,
|
||||
C90BCE472861D70500C2EF35 /* ErrorView.swift */,
|
||||
C927A0DA2846502300533D66 /* PathActions.swift */,
|
||||
C9EE0CD128478FDB00E9B97A /* PathRow.swift */,
|
||||
|
|
@ -281,6 +287,7 @@
|
|||
C927A0DB2846502300533D66 /* PathActions.swift in Sources */,
|
||||
C90BCE442861D3C500C2EF35 /* DeviceContent.swift in Sources */,
|
||||
C90DCC142896AAAA0072E403 /* ContentHeader.swift in Sources */,
|
||||
C9BF5232289FE95D00BDDC91 /* DescriptiveToggle.swift in Sources */,
|
||||
C90BCE502861E9D000C2EF35 /* SourceState.swift in Sources */,
|
||||
C90BCE4A2861DA6700C2EF35 /* RuntimeHeader.swift in Sources */,
|
||||
C9EE0CD42847B79E00E9B97A /* SimApp.swift in Sources */,
|
||||
|
|
@ -295,6 +302,7 @@
|
|||
C982F85B283B9F9000D491F4 /* ContentView.swift in Sources */,
|
||||
C982F875283CEEBB00D491F4 /* SimDevice.swift in Sources */,
|
||||
C9DD54C52860938C00D46AB3 /* SourceItemLink.swift in Sources */,
|
||||
C9BF5234289FE99600BDDC91 /* DescriptiveToggleStyle.swift in Sources */,
|
||||
C982F873283CE9AD00D491F4 /* SimDeviceType.swift in Sources */,
|
||||
C9DD54CB2860948600D46AB3 /* SourceItemLabel.swift in Sources */,
|
||||
C90BCE482861D70500C2EF35 /* ErrorView.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.804",
|
||||
"green" : "0.804",
|
||||
"red" : "0.804"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.310",
|
||||
"green" : "0.310",
|
||||
"red" : "0.310"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.306",
|
||||
"green" : "0.306",
|
||||
"red" : "0.306"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.796",
|
||||
"green" : "0.796",
|
||||
"red" : "0.796"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.902",
|
||||
"green" : "0.902",
|
||||
"red" : "0.902"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.173",
|
||||
"green" : "0.173",
|
||||
"red" : "0.173"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.902",
|
||||
"green" : "0.902",
|
||||
"red" : "0.902"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.294",
|
||||
"green" : "0.294",
|
||||
"red" : "0.294"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
|
@ -105,4 +105,14 @@ struct SimCtl {
|
|||
func setDeviceContentSize(_ device: SimDevice, contentSize: SimDevice.ContentSize) throws {
|
||||
try runAsync(args: ["ui", device.udid, "content_size", contentSize.rawValue])
|
||||
}
|
||||
|
||||
func getDeviceIncreaseContrast(_ device: SimDevice) async throws -> SimDevice.IncreaseContrast {
|
||||
let increaseContrast : String = try await runAsync(args: ["ui", device.udid, "increase_contrast"]).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
return SimDevice.IncreaseContrast(rawValue: increaseContrast) ?? .unknown
|
||||
}
|
||||
|
||||
func setDeviceIncreaseContrast(_ device: SimDevice, increaseContrast: SimDevice.IncreaseContrast) throws {
|
||||
try runAsync(args: ["ui", device.udid, "increase_contrast", increaseContrast.rawValue])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ class SimDevice: ObservableObject, Decodable {
|
|||
@Published var availabilityError : String?
|
||||
@Published var appearance = Appearance.unknown
|
||||
@Published var contentSize = ContentSize.unknown
|
||||
@Published var increaseContrast = IncreaseContrast.unknown
|
||||
var isTransitioning : Bool { state == .booting || state == .shuttingDown }
|
||||
var isBooted : Bool {
|
||||
get { state.showBooted == true }
|
||||
|
|
@ -143,6 +144,13 @@ class SimDevice: ObservableObject, Decodable {
|
|||
await MainActor.run { contentSize = result }
|
||||
}
|
||||
}
|
||||
if increaseContrast == .unknown {
|
||||
Task {
|
||||
let result = try await SimCtl().getDeviceIncreaseContrast(self)
|
||||
|
||||
await MainActor.run { increaseContrast = result }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setAppearance(_ appearance: Appearance) {
|
||||
|
|
@ -164,6 +172,16 @@ class SimDevice: ObservableObject, Decodable {
|
|||
print("Failed to set device content size: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func setIncreaseContrast(_ increaseContrast: IncreaseContrast) {
|
||||
self.increaseContrast = increaseContrast // optimistic
|
||||
|
||||
do {
|
||||
try SimCtl().setDeviceIncreaseContrast(self, increaseContrast: increaseContrast)
|
||||
} catch {
|
||||
print("Failed to set device increase contrast: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SimDevice {
|
||||
|
|
@ -244,11 +262,21 @@ extension SimDevice {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum IncreaseContrast: String {
|
||||
case enabled = "enabled"
|
||||
case disabled = "disabled"
|
||||
case unsupported = "unsupported"
|
||||
case unknown = "unknown"
|
||||
|
||||
var isOn : Bool { self == .enabled }
|
||||
}
|
||||
}
|
||||
|
||||
extension SimDevice: SourceItemData {
|
||||
var title : String { return name }
|
||||
var headerTitle : String { "Device: \(title)" }
|
||||
var isEnabled : Bool { isBooted }
|
||||
var imageDesc : SourceImageDesc { .symbol(systemName: "questionmark.circle", color: isAvailable ? .green : .red) }
|
||||
}
|
||||
|
||||
|
|
|
|||
50
SimDirs/Views/DescriptiveToggle.swift
Normal file
50
SimDirs/Views/DescriptiveToggle.swift
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// DescriptiveToggle.swift
|
||||
// SimDirs
|
||||
//
|
||||
// Created by Casey Fleser on 8/7/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol ToggleDescriptor {
|
||||
var isOn : Bool { get }
|
||||
var titleKey : LocalizedStringKey { get }
|
||||
var text : String { get }
|
||||
var image : Image { get }
|
||||
}
|
||||
|
||||
extension ToggleDescriptor {
|
||||
var circleColor : Color { isOn ? .accentColor : Color("CircleSymbolBkgOff") }
|
||||
}
|
||||
|
||||
struct DescriptiveToggle<T: ToggleDescriptor>: View {
|
||||
@Binding var isOn : Bool
|
||||
var descriptor : T
|
||||
|
||||
init(_ descriptor: T, isOn: Binding<Bool>) {
|
||||
self._isOn = isOn
|
||||
self.descriptor = descriptor
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Toggle(descriptor.titleKey, isOn: _isOn)
|
||||
.toggleStyle(DescriptiveToggleStyle(descriptor))
|
||||
}
|
||||
}
|
||||
|
||||
struct DescriptiveToggle_Previews: PreviewProvider {
|
||||
struct DarkMode: ToggleDescriptor {
|
||||
var isOn : Bool = true
|
||||
var titleKey : LocalizedStringKey { "Dark Mode" }
|
||||
var text : String { isOn ? "On" : "Off" }
|
||||
var image : Image { Image(systemName: "circle.circle") }
|
||||
}
|
||||
|
||||
@State static var toggle = DarkMode()
|
||||
|
||||
static var previews: some View {
|
||||
DescriptiveToggle(DarkMode(), isOn: $toggle.isOn)
|
||||
.disabled(true)
|
||||
}
|
||||
}
|
||||
46
SimDirs/Views/DescriptiveToggleStyle.swift
Normal file
46
SimDirs/Views/DescriptiveToggleStyle.swift
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// DescriptiveToggleStyle.swift
|
||||
// SimDirs
|
||||
//
|
||||
// Created by Casey Fleser on 8/7/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct DescriptiveToggleStyle<T: ToggleDescriptor>: ToggleStyle {
|
||||
var descriptor : T
|
||||
|
||||
init(_ descriptor: T) {
|
||||
self.descriptor = descriptor
|
||||
}
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
Button(action: { configuration.isOn.toggle() }) {
|
||||
VStack(spacing: 0) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.foregroundColor(descriptor.circleColor)
|
||||
descriptor.image
|
||||
.resizable()
|
||||
.foregroundColor(descriptor.isOn ? .white : Color("CircleSymbolOff"))
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.padding(9)
|
||||
}
|
||||
.frame(width: 36, height: 36)
|
||||
.padding(.bottom, 4)
|
||||
|
||||
Group {
|
||||
Text(descriptor.titleKey)
|
||||
.fontWeight(.semibold)
|
||||
Text(descriptor.text)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.font(.system(size: 11))
|
||||
.allowsTightening(true)
|
||||
.minimumScaleFactor(0.5)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,8 +8,9 @@
|
|||
import SwiftUI
|
||||
|
||||
extension SimDevice {
|
||||
public var content : some View { DeviceContent(device: self) }
|
||||
var scheme : ColorScheme? {
|
||||
public var content : some View { DeviceContent(self) }
|
||||
|
||||
var scheme : ColorScheme? {
|
||||
get {
|
||||
switch appearance {
|
||||
case .light: return .light
|
||||
|
|
@ -25,17 +26,36 @@ extension SimDevice {
|
|||
}
|
||||
}
|
||||
}
|
||||
var contentSizeVal : Double {
|
||||
|
||||
var contentSizeVal : Double {
|
||||
get { Double(contentSize.intValue) }
|
||||
set { setContenSize(ContentSize(intValue: Int(newValue))) }
|
||||
}
|
||||
|
||||
var isIncreaseContrast : Bool {
|
||||
get { increaseContrast.isOn }
|
||||
set { setIncreaseContrast(newValue ? .enabled : .disabled) }
|
||||
}
|
||||
}
|
||||
|
||||
extension SimDevice.IncreaseContrast: ToggleDescriptor {
|
||||
var titleKey : LocalizedStringKey { "Increase Contrast" }
|
||||
var text : String { rawValue.capitalized }
|
||||
var image : Image { Image(systemName: "circle.lefthalf.filled") }
|
||||
}
|
||||
|
||||
struct DeviceContent: View {
|
||||
@ObservedObject var device : SimDevice
|
||||
@State var isBooted : Bool
|
||||
|
||||
init(_ device: SimDevice) {
|
||||
self.device = device
|
||||
self.isBooted = device.isBooted
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 3.0) {
|
||||
ContentHeader("Paths")
|
||||
Group {
|
||||
if !device.isAvailable {
|
||||
ErrorView(
|
||||
|
|
@ -43,37 +63,50 @@ struct DeviceContent: View {
|
|||
description: device.availabilityError ?? "Unknown Error")
|
||||
}
|
||||
|
||||
ContentHeader("Paths")
|
||||
PathRow(title: "Data Path", path: device.dataPath)
|
||||
PathRow(title: "Log Path", path: device.logPath)
|
||||
|
||||
ContentHeader("UI")
|
||||
HStack(spacing: 32) {
|
||||
if device.appearance != .unsupported {
|
||||
AppearancePicker(scheme: $device.scheme)
|
||||
}
|
||||
if device.contentSize != .unsupported {
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "textformat.size")
|
||||
.imageScale(.small)
|
||||
Slider(value: $device.contentSizeVal, in: SimDevice.ContentSize.range, step: 1)
|
||||
Image(systemName: "textformat.size")
|
||||
.imageScale(.large)
|
||||
}
|
||||
Text("Content Size")
|
||||
}
|
||||
}
|
||||
}
|
||||
.disabled(!device.isBooted)
|
||||
}
|
||||
.font(.subheadline)
|
||||
.textSelection(.enabled)
|
||||
.lineLimit(1)
|
||||
|
||||
ContentHeader("UI")
|
||||
HStack(spacing: 16) {
|
||||
if device.appearance != .unsupported {
|
||||
AppearancePicker(scheme: $device.scheme)
|
||||
}
|
||||
if device.appearance != .unsupported {
|
||||
DescriptiveToggle(device.increaseContrast, isOn: $device.isIncreaseContrast)
|
||||
}
|
||||
if device.contentSize != .unsupported {
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "textformat.size")
|
||||
.imageScale(.small)
|
||||
Slider(value: $device.contentSizeVal, in: SimDevice.ContentSize.range, step: 1)
|
||||
Image(systemName: "textformat.size")
|
||||
.imageScale(.large)
|
||||
}
|
||||
Text("Content Size")
|
||||
}
|
||||
.opacity(isBooted ? 1.0 : 0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
.environment(\.isEnabled, isBooted)
|
||||
.onAppear {
|
||||
device.discoverUI()
|
||||
}
|
||||
.onChange(of: device.state) { state in
|
||||
let trulyBooted = state == .booted
|
||||
|
||||
if isBooted != trulyBooted {
|
||||
isBooted = trulyBooted
|
||||
|
||||
if isBooted {
|
||||
device.discoverUI()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -82,9 +115,9 @@ struct DeviceContent_Previews: PreviewProvider {
|
|||
|
||||
static var previews: some View {
|
||||
if !devices.isEmpty {
|
||||
DeviceContent(device: devices[0])
|
||||
DeviceContent(devices[0])
|
||||
.preferredColorScheme(.light)
|
||||
DeviceContent(device: devices.randomElement() ?? devices[0])
|
||||
DeviceContent(devices.randomElement() ?? devices[0])
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue