Restructuring of model data, item organization. rudimentary search

This commit is contained in:
Casey Fleser 2022-06-07 06:33:18 -05:00
parent 019df42354
commit 4a5876222a
17 changed files with 267 additions and 183 deletions

View file

@ -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;

View file

@ -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)
}
}

View file

@ -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 }

View file

@ -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)
}
}
}
}

View file

@ -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)
}
}
}
}

View file

@ -12,7 +12,7 @@ enum SimError: Error {
case invalidApp
}
struct SimModel {
class SimModel: ObservableObject {
var deviceTypes : [SimDeviceType]
var runtimes : [SimRuntime]

View file

@ -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()

View file

@ -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")
}

View file

@ -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")
}

View file

@ -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")
}

View file

@ -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")
}

View file

@ -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])
}
}

View 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)
}
}

View file

@ -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())
}
}

View 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)
}
}

View file

@ -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)
}
}

View 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)
}
}