[Features] Provides default picker view for @Options with pre-set values
To use, pass in a collection of values to `values` parameter in @Option initializer.
This commit is contained in:
parent
240b74de94
commit
6d95924145
@ -167,6 +167,7 @@
|
|||||||
BFFDF03F23E3C28A00931B96 /* libGambatte.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFDF03D23E3C0F000931B96 /* libGambatte.a */; };
|
BFFDF03F23E3C28A00931B96 /* libGambatte.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFDF03D23E3C0F000931B96 /* libGambatte.a */; };
|
||||||
BFFDF04623E3D3A600931B96 /* libMupen64Plus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFDF04523E3D3A600931B96 /* libMupen64Plus.a */; };
|
BFFDF04623E3D3A600931B96 /* libMupen64Plus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFDF04523E3D3A600931B96 /* libMupen64Plus.a */; };
|
||||||
D517F6BA29E730DA000D14D0 /* SettingsName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9C01929DDFBDD00A8D610 /* SettingsName.swift */; };
|
D517F6BA29E730DA000D14D0 /* SettingsName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9C01929DDFBDD00A8D610 /* SettingsName.swift */; };
|
||||||
|
D517F6BE29E7535F000D14D0 /* Collection+Optionals.swift in Sources */ = {isa = PBXBuildFile; fileRef = D517F6BD29E7535F000D14D0 /* Collection+Optionals.swift */; };
|
||||||
D524F4A1273DE9A100D500B2 /* AltKit in Frameworks */ = {isa = PBXBuildFile; productRef = D524F4A0273DE9A100D500B2 /* AltKit */; };
|
D524F4A1273DE9A100D500B2 /* AltKit in Frameworks */ = {isa = PBXBuildFile; productRef = D524F4A0273DE9A100D500B2 /* AltKit */; };
|
||||||
D524F4A3273DE9C000D500B2 /* ProcessInfo+JIT.swift in Sources */ = {isa = PBXBuildFile; fileRef = D524F4A2273DE9C000D500B2 /* ProcessInfo+JIT.swift */; };
|
D524F4A3273DE9C000D500B2 /* ProcessInfo+JIT.swift in Sources */ = {isa = PBXBuildFile; fileRef = D524F4A2273DE9C000D500B2 /* ProcessInfo+JIT.swift */; };
|
||||||
D524F4A5273DEBB400D500B2 /* ServerManager+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = D524F4A4273DEBB400D500B2 /* ServerManager+Delta.swift */; };
|
D524F4A5273DEBB400D500B2 /* ServerManager+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = D524F4A4273DEBB400D500B2 /* ServerManager+Delta.swift */; };
|
||||||
@ -193,6 +194,7 @@
|
|||||||
D5D7C20229E60F2000663793 /* OptionValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D7C1E629E5F90200663793 /* OptionValue.swift */; };
|
D5D7C20229E60F2000663793 /* OptionValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D7C1E629E5F90200663793 /* OptionValue.swift */; };
|
||||||
D5D7C20329E60F2000663793 /* Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58F39C529E0A473008B4100 /* Option.swift */; };
|
D5D7C20329E60F2000663793 /* Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58F39C529E0A473008B4100 /* Option.swift */; };
|
||||||
D5D7C20429E60F2000663793 /* Feature.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9BFFD29DDECF100A8D610 /* Feature.swift */; };
|
D5D7C20429E60F2000663793 /* Feature.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9BFFD29DDECF100A8D610 /* Feature.swift */; };
|
||||||
|
D5D7C20629E60F6100663793 /* OptionPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D592D6FE29E48FFB008D218A /* OptionPickerView.swift */; };
|
||||||
D5D7C20829E616CF00663793 /* FeatureContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D7C20729E616CF00663793 /* FeatureContainer.swift */; };
|
D5D7C20829E616CF00663793 /* FeatureContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D7C20729E616CF00663793 /* FeatureContainer.swift */; };
|
||||||
D5F82FB82981D3AC00B229AF /* LegacySearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F82FB72981D3AC00B229AF /* LegacySearchBar.swift */; };
|
D5F82FB82981D3AC00B229AF /* LegacySearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F82FB72981D3AC00B229AF /* LegacySearchBar.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
@ -407,6 +409,7 @@
|
|||||||
BFFDF03D23E3C0F000931B96 /* libGambatte.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libGambatte.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
BFFDF03D23E3C0F000931B96 /* libGambatte.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libGambatte.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BFFDF04523E3D3A600931B96 /* libMupen64Plus.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libMupen64Plus.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
BFFDF04523E3D3A600931B96 /* libMupen64Plus.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libMupen64Plus.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
C786AF1D2DDB6223BE2063CC /* Pods-Delta.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Delta.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Delta/Pods-Delta.debug.xcconfig"; sourceTree = "<group>"; };
|
C786AF1D2DDB6223BE2063CC /* Pods-Delta.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Delta.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Delta/Pods-Delta.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
D517F6BD29E7535F000D14D0 /* Collection+Optionals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+Optionals.swift"; sourceTree = "<group>"; };
|
||||||
D524F4A2273DE9C000D500B2 /* ProcessInfo+JIT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+JIT.swift"; sourceTree = "<group>"; };
|
D524F4A2273DE9C000D500B2 /* ProcessInfo+JIT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+JIT.swift"; sourceTree = "<group>"; };
|
||||||
D524F4A4273DEBB400D500B2 /* ServerManager+Delta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ServerManager+Delta.swift"; sourceTree = "<group>"; };
|
D524F4A4273DEBB400D500B2 /* ServerManager+Delta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ServerManager+Delta.swift"; sourceTree = "<group>"; };
|
||||||
D5011C47281B6E8B00A0760B /* CharacterSet+Filename.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CharacterSet+Filename.swift"; sourceTree = "<group>"; };
|
D5011C47281B6E8B00A0760B /* CharacterSet+Filename.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CharacterSet+Filename.swift"; sourceTree = "<group>"; };
|
||||||
@ -425,6 +428,7 @@
|
|||||||
D5864977297756CE0081477E /* CheatBaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatBaseView.swift; sourceTree = "<group>"; };
|
D5864977297756CE0081477E /* CheatBaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatBaseView.swift; sourceTree = "<group>"; };
|
||||||
D58F39C529E0A473008B4100 /* Option.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Option.swift; sourceTree = "<group>"; };
|
D58F39C529E0A473008B4100 /* Option.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Option.swift; sourceTree = "<group>"; };
|
||||||
D58F39C829E0A702008B4100 /* UserDefaults+OptionValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+OptionValues.swift"; sourceTree = "<group>"; };
|
D58F39C829E0A702008B4100 /* UserDefaults+OptionValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+OptionValues.swift"; sourceTree = "<group>"; };
|
||||||
|
D592D6FE29E48FFB008D218A /* OptionPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionPickerView.swift; sourceTree = "<group>"; };
|
||||||
D5A98CE1284EF14B00E023E5 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
D5A98CE1284EF14B00E023E5 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
D5A9BFFD29DDECF100A8D610 /* Feature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Feature.swift; sourceTree = "<group>"; };
|
D5A9BFFD29DDECF100A8D610 /* Feature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Feature.swift; sourceTree = "<group>"; };
|
||||||
D5A9C01929DDFBDD00A8D610 /* SettingsName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsName.swift; sourceTree = "<group>"; };
|
D5A9C01929DDFBDD00A8D610 /* SettingsName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsName.swift; sourceTree = "<group>"; };
|
||||||
@ -1031,6 +1035,7 @@
|
|||||||
children = (
|
children = (
|
||||||
D5A9BFFD29DDECF100A8D610 /* Feature.swift */,
|
D5A9BFFD29DDECF100A8D610 /* Feature.swift */,
|
||||||
D58F39C529E0A473008B4100 /* Option.swift */,
|
D58F39C529E0A473008B4100 /* Option.swift */,
|
||||||
|
D5D7C20529E60F4A00663793 /* Views */,
|
||||||
D517F6BB29E737F5000D14D0 /* Types */,
|
D517F6BB29E737F5000D14D0 /* Types */,
|
||||||
D5D7C1FE29E60EF700663793 /* Protocols */,
|
D5D7C1FE29E60EF700663793 /* Protocols */,
|
||||||
D5D7C1F829E60E8600663793 /* Extensions */,
|
D5D7C1F829E60E8600663793 /* Extensions */,
|
||||||
@ -1042,6 +1047,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D58F39C829E0A702008B4100 /* UserDefaults+OptionValues.swift */,
|
D58F39C829E0A702008B4100 /* UserDefaults+OptionValues.swift */,
|
||||||
|
D517F6BD29E7535F000D14D0 /* Collection+Optionals.swift */,
|
||||||
D54F710329E89DFC009C069A /* NotificationName+Settings.swift */,
|
D54F710329E89DFC009C069A /* NotificationName+Settings.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
@ -1058,6 +1064,14 @@
|
|||||||
path = Protocols;
|
path = Protocols;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D5D7C20529E60F4A00663793 /* Views */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D592D6FE29E48FFB008D218A /* OptionPickerView.swift */,
|
||||||
|
);
|
||||||
|
path = Views;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
FD1E8AE87FA2DB8793F7B937 /* Pods */ = {
|
FD1E8AE87FA2DB8793F7B937 /* Pods */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -1544,7 +1558,9 @@
|
|||||||
D55C468F29E761C000EA6DE9 /* AnyFeature.swift in Sources */,
|
D55C468F29E761C000EA6DE9 /* AnyFeature.swift in Sources */,
|
||||||
D5D7C20129E60F2000663793 /* LocalizedOptionValue.swift in Sources */,
|
D5D7C20129E60F2000663793 /* LocalizedOptionValue.swift in Sources */,
|
||||||
D54F710429E89DFC009C069A /* NotificationName+Settings.swift in Sources */,
|
D54F710429E89DFC009C069A /* NotificationName+Settings.swift in Sources */,
|
||||||
|
D517F6BE29E7535F000D14D0 /* Collection+Optionals.swift in Sources */,
|
||||||
D54F710229E89DCB009C069A /* SettingsUserInfoKey.swift in Sources */,
|
D54F710229E89DCB009C069A /* SettingsUserInfoKey.swift in Sources */,
|
||||||
|
D5D7C20629E60F6100663793 /* OptionPickerView.swift in Sources */,
|
||||||
D5D7C20329E60F2000663793 /* Option.swift in Sources */,
|
D5D7C20329E60F2000663793 /* Option.swift in Sources */,
|
||||||
D5D7C20229E60F2000663793 /* OptionValue.swift in Sources */,
|
D5D7C20229E60F2000663793 /* OptionValue.swift in Sources */,
|
||||||
D5D7C1F929E60EA500663793 /* UserDefaults+OptionValues.swift in Sources */,
|
D5D7C1F929E60EA500663793 /* UserDefaults+OptionValues.swift in Sources */,
|
||||||
|
|||||||
19
DeltaFeatures/Extensions/Collection+Optionals.swift
Normal file
19
DeltaFeatures/Extensions/Collection+Optionals.swift
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// Collection+Optionals.swift
|
||||||
|
// DeltaFeatures
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 4/12/23.
|
||||||
|
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Collection
|
||||||
|
{
|
||||||
|
func appendingNil() -> [Element] where Element: OptionalProtocol, Element.Wrapped: LocalizedOptionValue
|
||||||
|
{
|
||||||
|
var values = Array(self)
|
||||||
|
values.append(Element.none)
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,6 +16,7 @@ public class Option<Value: OptionValue, DetailView: View>: _AnyOption
|
|||||||
public let name: LocalizedStringKey?
|
public let name: LocalizedStringKey?
|
||||||
public let description: LocalizedStringKey?
|
public let description: LocalizedStringKey?
|
||||||
|
|
||||||
|
public let values: [Value]?
|
||||||
public private(set) var detailView: () -> DetailView? = { nil }
|
public private(set) var detailView: () -> DetailView? = { nil }
|
||||||
|
|
||||||
// Assigned to property name.
|
// Assigned to property name.
|
||||||
@ -73,17 +74,24 @@ public class Option<Value: OptionValue, DetailView: View>: _AnyOption
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private init(defaultValue: Value, name: LocalizedStringKey?, description: LocalizedStringKey?)
|
private init(defaultValue: Value, name: LocalizedStringKey?, description: LocalizedStringKey?, values: (some Collection<Value>)?)
|
||||||
{
|
{
|
||||||
self.defaultValue = defaultValue
|
self.defaultValue = defaultValue
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.description = description
|
self.description = description
|
||||||
|
self.values = values.map { Array($0) }
|
||||||
|
|
||||||
self.detailView = { nil }
|
self.detailView = { nil }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private convenience init(defaultValue: Value, name: LocalizedStringKey?, description: LocalizedStringKey?)
|
||||||
|
{
|
||||||
|
self.init(defaultValue: defaultValue, name: name, description: description, values: [Value]?.none)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Hidden" Option (no name or custom SwiftUI view)
|
// "Hidden" Option (no name, pre-set values, or custom SwiftUI view)
|
||||||
public extension Option where DetailView == EmptyView
|
public extension Option where DetailView == EmptyView
|
||||||
{
|
{
|
||||||
// Non-Optional
|
// Non-Optional
|
||||||
@ -102,6 +110,45 @@ public extension Option where DetailView == EmptyView
|
|||||||
convenience init(wrappedValue: Value) where Value: OptionalProtocol
|
convenience init(wrappedValue: Value) where Value: OptionalProtocol
|
||||||
{
|
{
|
||||||
self.init(defaultValue: wrappedValue, name: nil, description: nil)
|
self.init(defaultValue: wrappedValue, name: nil, description: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Picker" Option (User-visible, pre-set options with default picker UI)
|
||||||
|
public extension Option where Value: LocalizedOptionValue, DetailView == OptionPickerView<Value>
|
||||||
|
{
|
||||||
|
// Non-Optional
|
||||||
|
convenience init(wrappedValue: Value, name: LocalizedStringKey, description: LocalizedStringKey? = nil, values: some Collection<Value>)
|
||||||
|
{
|
||||||
|
self.init(defaultValue: wrappedValue, name: name, description: description, values: values)
|
||||||
|
|
||||||
|
self.detailView = { [weak self] () -> DetailView? in
|
||||||
|
guard let self else { return nil }
|
||||||
|
return OptionPickerView(name: name, options: Array(values), selectedValue: self.valueBinding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional, default = nil
|
||||||
|
convenience init(name: LocalizedStringKey, description: LocalizedStringKey? = nil, values: some Collection<Value>) where Value: OptionalProtocol, Value.Wrapped: LocalizedOptionValue
|
||||||
|
{
|
||||||
|
self.init(defaultValue: Value.none, name: name, description: description, values: values)
|
||||||
|
|
||||||
|
self.detailView = { [weak self] () -> DetailView? in
|
||||||
|
guard let self else { return nil }
|
||||||
|
return OptionPickerView(name: name, options: values.appendingNil(), selectedValue: self.valueBinding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional, default = non-nil
|
||||||
|
convenience init(wrappedValue: Value, name: LocalizedStringKey, description: LocalizedStringKey? = nil, values: some Collection<Value>) where Value: OptionalProtocol, Value.Wrapped: LocalizedOptionValue
|
||||||
|
{
|
||||||
|
self.init(defaultValue: wrappedValue, name: name, description: description, values: values)
|
||||||
|
|
||||||
|
self.detailView = { [weak self] () -> DetailView? in
|
||||||
|
guard let self else { return nil }
|
||||||
|
return OptionPickerView(name: name, options: values.appendingNil(), selectedValue: self.valueBinding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// "Custom" Option (User-visible, provides SwiftUI view to configure option)
|
// "Custom" Option (User-visible, provides SwiftUI view to configure option)
|
||||||
public extension Option where Value: LocalizedOptionValue
|
public extension Option where Value: LocalizedOptionValue
|
||||||
|
|||||||
@ -19,6 +19,7 @@ public protocol AnyOption<Value>: AnyObject, Identifiable
|
|||||||
var key: String { get }
|
var key: String { get }
|
||||||
var settingsKey: SettingsName { get }
|
var settingsKey: SettingsName { get }
|
||||||
|
|
||||||
|
var values: [Value]? { get }
|
||||||
var detailView: () -> DetailView? { get }
|
var detailView: () -> DetailView? { get }
|
||||||
|
|
||||||
var value: Value { get set }
|
var value: Value { get set }
|
||||||
|
|||||||
28
DeltaFeatures/Views/OptionPickerView.swift
Normal file
28
DeltaFeatures/Views/OptionPickerView.swift
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// OptionPickerView.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 4/10/23.
|
||||||
|
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
// Type must be public, but not its properties.
|
||||||
|
public struct OptionPickerView<Value: LocalizedOptionValue>: View
|
||||||
|
{
|
||||||
|
var name: LocalizedStringKey
|
||||||
|
var options: [Value]
|
||||||
|
|
||||||
|
@Binding
|
||||||
|
var selectedValue: Value
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
Picker(name, selection: $selectedValue) {
|
||||||
|
ForEach(options, id: \.self) { value in
|
||||||
|
value.localizedDescription
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pickerStyle(.inline)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user