mirror of
https://github.com/somegeekintn/SimDirs.git
synced 2026-04-26 14:47:41 +00:00
Restructuring of model data, item organization. rudimentary search
This commit is contained in:
parent
019df42354
commit
4a5876222a
17 changed files with 267 additions and 183 deletions
|
|
@ -9,11 +9,14 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
C927A0D92846414900533D66 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C927A0D82846414900533D66 /* Helpers.swift */; };
|
||||
C927A0DB2846502300533D66 /* PathActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C927A0DA2846502300533D66 /* PathActions.swift */; };
|
||||
C94C52C52844E7D400E2129E /* SimItemList.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94C52C42844E7D400E2129E /* SimItemList.swift */; };
|
||||
C94C52C72844E80A00E2129E /* SimItemRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94C52C62844E80A00E2129E /* SimItemRow.swift */; };
|
||||
C94C52C92844E99B00E2129E /* SimItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94C52C82844E99B00E2129E /* SimItemContent.swift */; };
|
||||
C94C52CB2844EAAC00E2129E /* RuntimeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94C52CA2844EAAC00E2129E /* RuntimeView.swift */; };
|
||||
C95E5AB6284B6DDE00A2124E /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C95E5AB5284B6DDE00A2124E /* AppView.swift */; };
|
||||
C977973C284F58A900706DFB /* PresentationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C977973B284F58A900706DFB /* PresentationState.swift */; };
|
||||
C977973E284F5AE100706DFB /* SimItemNavLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C977973D284F5AE100706DFB /* SimItemNavLink.swift */; };
|
||||
C9779740284F5CBB00706DFB /* SimItemGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C977973F284F5CBB00706DFB /* SimItemGroup.swift */; };
|
||||
C9779742284F6DE000706DFB /* ToolbarMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9779741284F6DE000706DFB /* ToolbarMenu.swift */; };
|
||||
C982F859283B9F9000D491F4 /* SimDirsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C982F858283B9F9000D491F4 /* SimDirsApp.swift */; };
|
||||
C982F85B283B9F9000D491F4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C982F85A283B9F9000D491F4 /* ContentView.swift */; };
|
||||
C982F85D283B9F9200D491F4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C982F85C283B9F9200D491F4 /* Assets.xcassets */; };
|
||||
|
|
@ -25,7 +28,6 @@
|
|||
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 */; };
|
||||
C982F87E283E57B200D491F4 /* PresentableModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C982F87D283E57B200D491F4 /* PresentableModel.swift */; };
|
||||
C982F880283E57E600D491F4 /* PresentationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C982F87F283E57E600D491F4 /* PresentationItem.swift */; };
|
||||
C982F883283E813F00D491F4 /* DeviceTypeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C982F882283E813F00D491F4 /* DeviceTypeView.swift */; };
|
||||
C9D729F128478AB00064152D /* DeviceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D729F028478AB00064152D /* DeviceView.swift */; };
|
||||
|
|
@ -36,11 +38,14 @@
|
|||
/* Begin PBXFileReference section */
|
||||
C927A0D82846414900533D66 /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
|
||||
C927A0DA2846502300533D66 /* PathActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathActions.swift; sourceTree = "<group>"; };
|
||||
C94C52C42844E7D400E2129E /* SimItemList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimItemList.swift; sourceTree = "<group>"; };
|
||||
C94C52C62844E80A00E2129E /* SimItemRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimItemRow.swift; sourceTree = "<group>"; };
|
||||
C94C52C82844E99B00E2129E /* SimItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimItemContent.swift; sourceTree = "<group>"; };
|
||||
C94C52CA2844EAAC00E2129E /* RuntimeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeView.swift; sourceTree = "<group>"; };
|
||||
C95E5AB5284B6DDE00A2124E /* AppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppView.swift; sourceTree = "<group>"; };
|
||||
C977973B284F58A900706DFB /* PresentationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationState.swift; sourceTree = "<group>"; };
|
||||
C977973D284F5AE100706DFB /* SimItemNavLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimItemNavLink.swift; sourceTree = "<group>"; };
|
||||
C977973F284F5CBB00706DFB /* SimItemGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimItemGroup.swift; sourceTree = "<group>"; };
|
||||
C9779741284F6DE000706DFB /* ToolbarMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarMenu.swift; sourceTree = "<group>"; };
|
||||
C982F855283B9F9000D491F4 /* SimDirs.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimDirs.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C982F858283B9F9000D491F4 /* SimDirsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimDirsApp.swift; sourceTree = "<group>"; };
|
||||
C982F85A283B9F9000D491F4 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -54,7 +59,6 @@
|
|||
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>"; };
|
||||
C982F87D283E57B200D491F4 /* PresentableModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentableModel.swift; sourceTree = "<group>"; };
|
||||
C982F87F283E57E600D491F4 /* PresentationItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationItem.swift; sourceTree = "<group>"; };
|
||||
C982F882283E813F00D491F4 /* DeviceTypeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceTypeView.swift; sourceTree = "<group>"; };
|
||||
C9D729F028478AB00064152D /* DeviceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceView.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -131,8 +135,8 @@
|
|||
C982F87C283E579900D491F4 /* Presentation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C982F87D283E57B200D491F4 /* PresentableModel.swift */,
|
||||
C982F87F283E57E600D491F4 /* PresentationItem.swift */,
|
||||
C977973B284F58A900706DFB /* PresentationState.swift */,
|
||||
);
|
||||
path = Presentation;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -147,8 +151,10 @@
|
|||
C9EE0CD128478FDB00E9B97A /* PathRow.swift */,
|
||||
C94C52CA2844EAAC00E2129E /* RuntimeView.swift */,
|
||||
C94C52C82844E99B00E2129E /* SimItemContent.swift */,
|
||||
C94C52C42844E7D400E2129E /* SimItemList.swift */,
|
||||
C977973F284F5CBB00706DFB /* SimItemGroup.swift */,
|
||||
C977973D284F5AE100706DFB /* SimItemNavLink.swift */,
|
||||
C94C52C62844E80A00E2129E /* SimItemRow.swift */,
|
||||
C9779741284F6DE000706DFB /* ToolbarMenu.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -227,10 +233,14 @@
|
|||
C94C52C92844E99B00E2129E /* SimItemContent.swift in Sources */,
|
||||
C927A0DB2846502300533D66 /* PathActions.swift in Sources */,
|
||||
C9EE0CD42847B79E00E9B97A /* SimApp.swift in Sources */,
|
||||
C9779742284F6DE000706DFB /* ToolbarMenu.swift in Sources */,
|
||||
C9779740284F5CBB00706DFB /* SimItemGroup.swift in Sources */,
|
||||
C95E5AB6284B6DDE00A2124E /* AppView.swift in Sources */,
|
||||
C982F85B283B9F9000D491F4 /* ContentView.swift in Sources */,
|
||||
C982F875283CEEBB00D491F4 /* SimDevice.swift in Sources */,
|
||||
C977973C284F58A900706DFB /* PresentationState.swift in Sources */,
|
||||
C982F873283CE9AD00D491F4 /* SimDeviceType.swift in Sources */,
|
||||
C977973E284F5AE100706DFB /* SimItemNavLink.swift in Sources */,
|
||||
C982F877283D020C00D491F4 /* SimProductFamily.swift in Sources */,
|
||||
C982F859283B9F9000D491F4 /* SimDirsApp.swift in Sources */,
|
||||
C982F871283CE7B800D491F4 /* SimRuntime.swift in Sources */,
|
||||
|
|
@ -238,12 +248,10 @@
|
|||
C94C52C72844E80A00E2129E /* SimItemRow.swift in Sources */,
|
||||
C982F86B283BA22100D491F4 /* SimPlatform.swift in Sources */,
|
||||
C927A0D92846414900533D66 /* Helpers.swift in Sources */,
|
||||
C982F87E283E57B200D491F4 /* PresentableModel.swift in Sources */,
|
||||
C94C52CB2844EAAC00E2129E /* RuntimeView.swift in Sources */,
|
||||
C982F880283E57E600D491F4 /* PresentationItem.swift in Sources */,
|
||||
C982F879283D042E00D491F4 /* SimModel.swift in Sources */,
|
||||
C9D729F128478AB00064152D /* DeviceView.swift in Sources */,
|
||||
C94C52C52844E7D400E2129E /* SimItemList.swift in Sources */,
|
||||
C982F87B283E40C800D491F4 /* SimCtl.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
|||
|
|
@ -8,15 +8,33 @@
|
|||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
let model : SimModel
|
||||
var rootItems : [PresentationItem] { state.presentationItems(from: model) }
|
||||
|
||||
@State private var state = PresentationState(filter: [])
|
||||
|
||||
var body: some View {
|
||||
SimItemList()
|
||||
NavigationView {
|
||||
List {
|
||||
ForEach(rootItems) { item in
|
||||
SimItemGroup(item: item, state: $state)
|
||||
}
|
||||
.padding(.leading, 2.0)
|
||||
}
|
||||
.frame(minWidth: 200)
|
||||
.toolbar {
|
||||
ToolbarItem { ToolbarMenu(state: $state) }
|
||||
}
|
||||
Text("SimDirs")
|
||||
}
|
||||
.searchable(text: $state.searchTerm, placement: .sidebar)
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var simModel = SimModel()
|
||||
|
||||
static var previews: some View {
|
||||
ContentView()
|
||||
.environmentObject(PresentableModel())
|
||||
ContentView(model: simModel)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,13 @@ extension NSWorkspace {
|
|||
}
|
||||
}
|
||||
|
||||
extension OptionSet where Self == Self.Element {
|
||||
mutating func booleanSet(_ value: Bool, options: Self) {
|
||||
if value { update(with: options) }
|
||||
else { subtract(options) }
|
||||
}
|
||||
}
|
||||
|
||||
extension PropertyListSerialization {
|
||||
class func propertyList(from url: URL) -> [String : AnyObject]? {
|
||||
guard let plistData = try? Data(contentsOf: url) else { return nil }
|
||||
|
|
|
|||
|
|
@ -53,26 +53,43 @@ struct PresentationItem: Identifiable {
|
|||
customImage = image
|
||||
}
|
||||
|
||||
func filtered(_ filter: PresentationFilter) -> Self {
|
||||
guard var childItems = self.children, !filter.isEmpty else { return self }
|
||||
var filteredItem = self
|
||||
|
||||
if filter.contains(.withApps) {
|
||||
childItems = childItems.filter { $0.containsType(SimApp.self) }
|
||||
}
|
||||
if filter.contains(.runtimeInstalled) {
|
||||
childItems = childItems.filter {
|
||||
guard let runtime = $0.underlying as? SimRuntime else { return true }
|
||||
|
||||
return runtime.isAvailable
|
||||
}
|
||||
}
|
||||
filteredItem.children = childItems.isEmpty ? nil : childItems.map { $0.filtered(filter)}
|
||||
|
||||
return filteredItem
|
||||
func titlesContain(_ searchTerm: String) -> Bool {
|
||||
return title.contains(searchTerm) || children?.contains(where: { $0.titlesContain(searchTerm)}) ?? false
|
||||
}
|
||||
|
||||
func containsType<T> (_ type: T.Type) -> Bool {
|
||||
|
||||
func containsType<T>(_ type: T.Type) -> Bool {
|
||||
return underlying is T || children?.contains(where: { $0.containsType(type)}) ?? false
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element == PresentationItem {
|
||||
var flatItems : [PresentationItem] { self.flatMap { $0.flattened } }
|
||||
|
||||
func itemsOf<T> (type: T.Type) -> [T] {
|
||||
return flatItems.compactMap { $0.underlying as? T }
|
||||
}
|
||||
|
||||
func validateItems() {
|
||||
let allIDs = flatItems.map { $0.id }
|
||||
var idSet = Set<String>()
|
||||
|
||||
print("Validating \(allIDs.count) items")
|
||||
for id in allIDs {
|
||||
if !idSet.insert(id).inserted {
|
||||
print("Duplicate PresentationItem.id: \(id)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dumpPresentation(level: Int = 0) {
|
||||
let ident = Array<String>(repeating: "\t", count: level).joined()
|
||||
|
||||
for item in self {
|
||||
print("\(ident)\(item.title) [\(item.id)]")
|
||||
|
||||
if let children = item.children {
|
||||
children.dumpPresentation(level: level + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,56 +1,53 @@
|
|||
//
|
||||
// PresentableModel.swift
|
||||
// PresentationState.swift
|
||||
// SimDirs
|
||||
//
|
||||
// Created by Casey Fleser on 5/25/22.
|
||||
// Created by Casey Fleser on 6/7/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct PresentationFilter: OptionSet, CaseIterable {
|
||||
let rawValue: Int
|
||||
|
||||
static let withApps = PresentationFilter(rawValue: 1 << 0)
|
||||
static let runtimeInstalled = PresentationFilter(rawValue: 1 << 1)
|
||||
struct PresentationState {
|
||||
enum Organization: String, CaseIterable, Identifiable {
|
||||
case byDevice = "By Device"
|
||||
case byRuntime = "By Runtime"
|
||||
|
||||
static var allCases : [PresentationFilter] = [.withApps, .runtimeInstalled]
|
||||
}
|
||||
|
||||
class PresentableModel: ObservableObject {
|
||||
enum Style {
|
||||
case byDevice
|
||||
case byRuntime
|
||||
var id: Organization { self }
|
||||
}
|
||||
|
||||
struct Filter: OptionSet, CaseIterable {
|
||||
let rawValue: Int
|
||||
|
||||
static let withApps = Filter(rawValue: 1 << 0)
|
||||
static let runtimeInstalled = Filter(rawValue: 1 << 1)
|
||||
|
||||
static var allCases : [Filter] = [.withApps, .runtimeInstalled]
|
||||
}
|
||||
|
||||
var baseModel = SimModel()
|
||||
var style = Style.byRuntime
|
||||
var items = [PresentationItem]()
|
||||
var flatItems : [PresentationItem] { items.flatMap { $0.flattened } }
|
||||
var organization = Organization.byRuntime
|
||||
var filter = Filter()
|
||||
var searchTerm = ""
|
||||
|
||||
init() {
|
||||
rebuildPresentation()
|
||||
validateItems()
|
||||
// dumpPresentationItems(items)
|
||||
static func testItemsOf<T>(type: T.Type) -> [T] {
|
||||
let flatItems = PresentationState().presentationItems(from: SimModel()).flatItems
|
||||
|
||||
return flatItems.itemsOf(type: type)
|
||||
}
|
||||
|
||||
func filteredItems(filter: PresentationFilter) -> [PresentationItem] {
|
||||
return items.map { $0.filtered(filter) }
|
||||
}
|
||||
|
||||
func rebuildPresentation() {
|
||||
switch style {
|
||||
case .byDevice: items = itemsForDevicePresentation()
|
||||
case .byRuntime: items = itemsForRuntimePresentation()
|
||||
|
||||
func presentationItems(from model: SimModel) -> [PresentationItem] {
|
||||
switch organization {
|
||||
case .byDevice: return itemsForDeviceStyle(from: model)
|
||||
case .byRuntime: return itemsForRuntimeStyle(from: model)
|
||||
}
|
||||
}
|
||||
|
||||
func itemsForDevicePresentation() -> [PresentationItem] {
|
||||
|
||||
func itemsForDeviceStyle(from model: SimModel) -> [PresentationItem] {
|
||||
return SimProductFamily.presentation.map{ family in
|
||||
var familyItem = PresentationItem(family)
|
||||
|
||||
familyItem.children = baseModel.deviceTypes.filter({ $0.supports(productFamily: family) }).map { deviceType in
|
||||
familyItem.children = model.deviceTypes.filter({ $0.supports(productFamily: family) }).map { deviceType in
|
||||
var deviceTypeItem = PresentationItem(deviceType, identifier: deviceType.id)
|
||||
let deviceTypeChildren : [PresentationItem] = baseModel.runtimes.filter({ $0.supports(deviceType: deviceType) }).map { runtime in
|
||||
let deviceTypeChildren : [PresentationItem] = model.runtimes.filter({ $0.supports(deviceType: deviceType) }).map { runtime in
|
||||
var runtimeItem = PresentationItem(runtime, identifier: "\(deviceType.id) - \(runtime.id)")
|
||||
let runtimeItemChildren = runtime.devices.filter({ $0.isDeviceOfType(deviceType) }).map { device -> PresentationItem in
|
||||
var deviceItem = PresentationItem(device, image: family.imageName)
|
||||
|
|
@ -81,13 +78,13 @@ class PresentableModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func itemsForRuntimePresentation() -> [PresentationItem] {
|
||||
func itemsForRuntimeStyle(from model: SimModel) -> [PresentationItem] {
|
||||
return SimPlatform.presentation.map{ platform in
|
||||
var platformItem = PresentationItem(platform)
|
||||
|
||||
platformItem.children = baseModel.runtimes.filter({ $0.supports(platform: platform) }).map { runtime in
|
||||
platformItem.children = model.runtimes.filter({ $0.supports(platform: platform) }).map { runtime in
|
||||
var runtimeItem = PresentationItem(runtime)
|
||||
let runtimeItemChildren : [PresentationItem] = baseModel.deviceTypes.filter({ $0.supports(runtime: runtime) }).map { deviceType in
|
||||
let runtimeItemChildren : [PresentationItem] = model.deviceTypes.filter({ $0.supports(runtime: runtime) }).map { deviceType in
|
||||
var deviceTypeItem = PresentationItem(deviceType, identifier: "\(runtime.id) - \(deviceType.id)")
|
||||
let deviceTypeChildren = runtime.devices.filter({ $0.isDeviceOfType(deviceType) }).map { device -> PresentationItem in
|
||||
var deviceItem = PresentationItem(device, image: deviceType.imageName)
|
||||
|
|
@ -117,33 +114,5 @@ class PresentableModel: ObservableObject {
|
|||
return platformItem
|
||||
}
|
||||
}
|
||||
|
||||
func itemsOf<T> (type: T.Type) -> [T] {
|
||||
return flatItems.compactMap { $0.underlying as? T }
|
||||
}
|
||||
|
||||
func validateItems() {
|
||||
let allIDs = flatItems.map { $0.id }
|
||||
var idSet = Set<String>()
|
||||
|
||||
print("Validating \(allIDs.count) items")
|
||||
for id in allIDs {
|
||||
if !idSet.insert(id).inserted {
|
||||
print("Duplicate PresentationItem.id: \(id)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dumpPresentationItems(_ items: [PresentationItem], level: Int = 0) {
|
||||
let ident = Array(repeating: "\t", count: level).joined()
|
||||
|
||||
for item in items {
|
||||
print("\(ident)\(item.title) [\(item.id)]")
|
||||
|
||||
if let children = item.children {
|
||||
dumpPresentationItems(children, level: level + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -12,7 +12,7 @@ enum SimError: Error {
|
|||
case invalidApp
|
||||
}
|
||||
|
||||
struct SimModel {
|
||||
class SimModel: ObservableObject {
|
||||
var deviceTypes : [SimDeviceType]
|
||||
var runtimes : [SimRuntime]
|
||||
|
||||
|
|
|
|||
|
|
@ -9,12 +9,11 @@ import SwiftUI
|
|||
|
||||
@main
|
||||
struct SimDirsApp: App {
|
||||
@StateObject private var modelData = PresentableModel()
|
||||
@StateObject private var simModel = SimModel()
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
.environmentObject(modelData)
|
||||
ContentView(model: simModel)
|
||||
}
|
||||
.commands {
|
||||
SimCommands()
|
||||
|
|
|
|||
|
|
@ -53,9 +53,9 @@ struct AppView: View {
|
|||
}
|
||||
|
||||
struct AppView_Previews: PreviewProvider {
|
||||
static let apps = PresentableModel().itemsOf(type: SimApp.self)
|
||||
|
||||
static var previews: some View {
|
||||
let apps = PresentationState.testItemsOf(type: SimApp.self)
|
||||
|
||||
if apps.isEmpty {
|
||||
Text("No SimApp present in model data")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ struct DeviceTypeView: View {
|
|||
}
|
||||
|
||||
struct DeviceTypeView_Previews: PreviewProvider {
|
||||
static let deviceTypes = PresentableModel().itemsOf(type: SimDeviceType.self)
|
||||
|
||||
static var previews: some View {
|
||||
let deviceTypes = PresentationState.testItemsOf(type: SimDeviceType.self)
|
||||
|
||||
if deviceTypes.isEmpty {
|
||||
Text("No SimDeviceType present in model data")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ struct DeviceView: View {
|
|||
}
|
||||
|
||||
struct DeviceView_Previews: PreviewProvider {
|
||||
static let devices = PresentableModel().itemsOf(type: SimDevice.self)
|
||||
|
||||
static var previews: some View {
|
||||
let devices = PresentationState.testItemsOf(type: SimDevice.self)
|
||||
|
||||
if devices.isEmpty {
|
||||
Text("No SimDevice present in model data")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,9 +59,9 @@ struct RuntimeView: View {
|
|||
}
|
||||
|
||||
struct RuntimeView_Previews: PreviewProvider {
|
||||
static let runtimes = PresentableModel().itemsOf(type: SimRuntime.self)
|
||||
|
||||
static var previews: some View {
|
||||
let runtimes = PresentationState.testItemsOf(type: SimRuntime.self)
|
||||
|
||||
if runtimes.isEmpty {
|
||||
Text("No SimRuntime present in model data")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ struct SimItemContent: View {
|
|||
}
|
||||
|
||||
struct SimItemContent_Previews: PreviewProvider {
|
||||
static var model = PresentableModel()
|
||||
|
||||
static var previews: some View {
|
||||
SimItemContent(item: model.items[0].children![0])
|
||||
let testItem = PresentationState().presentationItems(from: SimModel())[0]
|
||||
|
||||
SimItemContent(item: testItem.children![0])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
62
SimDirs/Views/SimItemGroup.swift
Normal file
62
SimDirs/Views/SimItemGroup.swift
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// SimItemGroup.swift
|
||||
// SimDirs
|
||||
//
|
||||
// Created by Casey Fleser on 6/7/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SimItemGroup: View {
|
||||
let item : PresentationItem
|
||||
@Binding var state : PresentationState
|
||||
@State private var isExpanded = false
|
||||
|
||||
var children : [PresentationItem]? {
|
||||
guard var items = item.children else { return nil }
|
||||
|
||||
if state.filter.contains(.withApps) {
|
||||
items = items.filter { $0.containsType(SimApp.self) }
|
||||
}
|
||||
if state.filter.contains(.runtimeInstalled) {
|
||||
items = items.filter {
|
||||
guard let runtime = $0.underlying as? SimRuntime else { return true }
|
||||
|
||||
return runtime.isAvailable
|
||||
}
|
||||
}
|
||||
if !state.searchTerm.isEmpty {
|
||||
items = items.filter { $0.titlesContain(state.searchTerm) }
|
||||
}
|
||||
|
||||
return items.isEmpty ? nil : items
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if let childItems = children {
|
||||
DisclosureGroup(
|
||||
isExpanded: $isExpanded,
|
||||
content: {
|
||||
ForEach(childItems) { childItem in
|
||||
SimItemGroup(item: childItem, state: $state)
|
||||
}
|
||||
},
|
||||
label: { SimItemNavLink(item: item) }
|
||||
)
|
||||
}
|
||||
else {
|
||||
SimItemNavLink(item: item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SimItemGroup_Previews: PreviewProvider {
|
||||
static var simModel = SimModel()
|
||||
@State static var state = PresentationState()
|
||||
|
||||
static var previews: some View {
|
||||
let testItem = state.presentationItems(from: simModel)[0]
|
||||
|
||||
SimItemGroup(item: testItem, state: $state)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
//
|
||||
// SimItemList.swift
|
||||
// SimDirs
|
||||
//
|
||||
// Created by Casey Fleser on 5/30/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SimItemList: View {
|
||||
@EnvironmentObject var modelData : PresentableModel
|
||||
@State private var withApps = false
|
||||
@State private var withRuntimes = false
|
||||
|
||||
var filteredItems : [PresentationItem] {
|
||||
var filter = PresentationFilter()
|
||||
|
||||
if withApps { filter.update(with: .withApps) }
|
||||
if withRuntimes { filter.update(with: .runtimeInstalled) }
|
||||
|
||||
return modelData.filteredItems(filter: filter)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
VStack {
|
||||
List {
|
||||
OutlineGroup(filteredItems, children: \.children) { item in
|
||||
NavigationLink {
|
||||
SimItemContent(item: item)
|
||||
} label: {
|
||||
SimItemRow(item: item)
|
||||
}
|
||||
}
|
||||
.padding(.leading, 2.0)
|
||||
}
|
||||
.frame(minWidth: 200)
|
||||
.toolbar {
|
||||
ToolbarItem {
|
||||
Menu {
|
||||
Toggle(isOn: $withApps) {
|
||||
Label("With Apps", systemImage: "app.fill")
|
||||
}
|
||||
Toggle(isOn: $withRuntimes) {
|
||||
Label("Installed Runtimes", systemImage: "cpu.fill")
|
||||
}
|
||||
} label: {
|
||||
Label("Filter", systemImage: "slider.horizontal.3")
|
||||
}
|
||||
}
|
||||
}
|
||||
Text("Search")
|
||||
.padding(.bottom, 4.0)
|
||||
}
|
||||
Text("SimDirs")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SimItemList_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
SimItemList()
|
||||
.environmentObject(PresentableModel())
|
||||
}
|
||||
}
|
||||
27
SimDirs/Views/SimItemNavLink.swift
Normal file
27
SimDirs/Views/SimItemNavLink.swift
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// SimItemNavLink.swift
|
||||
// SimDirs
|
||||
//
|
||||
// Created by Casey Fleser on 6/7/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SimItemNavLink: View {
|
||||
let item : PresentationItem
|
||||
|
||||
var body: some View {
|
||||
NavigationLink {
|
||||
SimItemContent(item: item) } label: {
|
||||
SimItemRow(item: item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SimItemNavLink_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let testItem = PresentationState().presentationItems(from: SimModel())[0]
|
||||
|
||||
SimItemNavLink(item: testItem)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ struct SimItemRow: View {
|
|||
var item : PresentationItem
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Label(title: { Text(item.title) }) {
|
||||
if let icon = item.icon {
|
||||
Image(nsImage: icon)
|
||||
.resizable()
|
||||
|
|
@ -23,17 +23,14 @@ struct SimItemRow: View {
|
|||
.foregroundColor(item.imageColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
Text(item.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SimItemRow_Previews: PreviewProvider {
|
||||
static var model = PresentableModel()
|
||||
|
||||
static var previews: some View {
|
||||
Group {
|
||||
SimItemRow(item: model.items[0])
|
||||
}
|
||||
let testItem = PresentationState().presentationItems(from: SimModel())[0]
|
||||
|
||||
SimItemRow(item: testItem)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
45
SimDirs/Views/ToolbarMenu.swift
Normal file
45
SimDirs/Views/ToolbarMenu.swift
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// ToolbarMenu.swift
|
||||
// SimDirs
|
||||
//
|
||||
// Created by Casey Fleser on 6/7/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ToolbarMenu: View {
|
||||
@Binding var state : PresentationState
|
||||
|
||||
var withApps : Binding<Bool> {
|
||||
Binding(get: { state.filter.contains(.withApps) },
|
||||
set: { state.filter.booleanSet($0, options: .withApps) })
|
||||
}
|
||||
var withRuntimes : Binding<Bool> {
|
||||
Binding(get: { state.filter.contains(.runtimeInstalled) },
|
||||
set: { state.filter.booleanSet($0, options: .runtimeInstalled) })
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Menu {
|
||||
Picker("Organization", selection: $state.organization) {
|
||||
ForEach(PresentationState.Organization.allCases) { style in
|
||||
Text(style.rawValue).tag(style)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.inline)
|
||||
Toggle(isOn: withApps) { Label("With Apps", systemImage: "app.fill") }
|
||||
Toggle(isOn: withRuntimes) { Label("Installed Runtimes", systemImage: "cpu.fill") }
|
||||
} label: {
|
||||
Label("Filter", systemImage: "slider.horizontal.3")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ToolbarMenu_Previews: PreviewProvider {
|
||||
@State static var state = PresentationState(filter: [])
|
||||
|
||||
static var previews: some View {
|
||||
ToolbarMenu(state: $state)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue