aboutsummaryrefslogtreecommitdiff
path: root/Map
diff options
context:
space:
mode:
authorRuben Beltran del Rio <ruben@unlimited.pizza>2021-02-06 00:55:04 +0100
committerRuben Beltran del Rio <ruben@unlimited.pizza>2021-02-06 00:55:04 +0100
commit75a0e4509a70055851b085f3f7293ae1cf48164c (patch)
treeb2933b23235b83cb9f5f2d9fd42c8028650ad1d6 /Map
parent91fd86189477e6c690c6487d51d80bc58c8ecb63 (diff)
Fix performance and undo1.2.0
Diffstat (limited to 'Map')
-rw-r--r--Map/Debouncer.swift2
-rw-r--r--Map/Info.plist2
-rw-r--r--Map/MapParser/MapParser.swift3
-rw-r--r--Map/State/AppState.swift5
-rw-r--r--Map/Views/ContentView.swift16
-rw-r--r--Map/Views/MapDetail.swift67
-rw-r--r--Map/Views/MapRender.swift20
-rw-r--r--Map/Views/MapTextEditor.swift1
-rw-r--r--Map/Views/SlowMapRender.swift90
9 files changed, 158 insertions, 48 deletions
diff --git a/Map/Debouncer.swift b/Map/Debouncer.swift
index f119d8a..cd7960a 100644
--- a/Map/Debouncer.swift
+++ b/Map/Debouncer.swift
@@ -3,7 +3,7 @@ import Foundation
class Debouncer {
// MARK: - Properties
- private let queue = DispatchQueue.main
+ private let queue = DispatchQueue.global(qos: .utility)
private var workItem = DispatchWorkItem(block: {})
private var interval: TimeInterval
diff --git a/Map/Info.plist b/Map/Info.plist
index 903c15d..6ba8cf0 100644
--- a/Map/Info.plist
+++ b/Map/Info.plist
@@ -17,7 +17,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
- <string>1</string>
+ <string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.productivity</string>
<key>LSMinimumSystemVersion</key>
diff --git a/Map/MapParser/MapParser.swift b/Map/MapParser/MapParser.swift
index 3528eb5..cce6251 100644
--- a/Map/MapParser/MapParser.swift
+++ b/Map/MapParser/MapParser.swift
@@ -25,6 +25,9 @@ struct ParsedMap {
let blockers: [Blocker]
let opportunities: [Opportunity]
let stages: [CGFloat]
+
+ static let empty: ParsedMap = ParsedMap(
+ vertices: [], edges: [], blockers: [], opportunities: [], stages: defaultDimensions)
}
struct Vertex {
diff --git a/Map/State/AppState.swift b/Map/State/AppState.swift
index f062051..c261725 100644
--- a/Map/State/AppState.swift
+++ b/Map/State/AppState.swift
@@ -38,7 +38,8 @@ func appStateReducer(state: inout AppState, action: AppAction) {
window.makeKeyAndOrderFront(nil)
let renderView = MapRenderView(
- content: map.content ?? "", evolution: Stage.stages(state.selectedEvolution))
+ content: Binding.constant(map.content ?? ""),
+ evolution: Binding.constant(Stage.stages(state.selectedEvolution)))
let view = NSHostingView(rootView: renderView)
window.contentView = view
@@ -91,7 +92,7 @@ func appStateReducer(state: inout AppState, action: AppAction) {
print("Cancel")
}
}
- case .deleteMap(map: let map):
+ case .deleteMap(let map):
let context = PersistenceController.shared.container.viewContext
context.delete(map)
diff --git a/Map/Views/ContentView.swift b/Map/Views/ContentView.swift
index f439026..534c14c 100644
--- a/Map/Views/ContentView.swift
+++ b/Map/Views/ContentView.swift
@@ -25,7 +25,9 @@ struct ContentView: View {
DefaultMapView()
}
ForEach(maps) { map in
- NavigationLink(destination: MapDetailView(map: map)) {
+ NavigationLink(
+ destination: MapDetailView(map: map, title: map.title ?? "", content: map.content ?? "")
+ ) {
HStack {
Text(map.title ?? "Untitled Map")
Spacer()
@@ -38,10 +40,12 @@ struct ContentView: View {
.cornerRadius(2.0)
}.padding(.leading, 8.0)
}.contextMenu {
- Button(action: { store.send(.deleteMap(map: map))}) {
- Image(systemName: "trash")
- Text("Delete")
- }
+ Button(
+ action: { store.send(.deleteMap(map: map)) },
+ label: {
+ Image(systemName: "trash")
+ Text("Delete")
+ })
}
}
.onDelete(perform: deleteMaps)
@@ -56,7 +60,7 @@ struct ContentView: View {
}
}
}
- DefaultMapView()
+ DefaultMapView()
}
}
diff --git a/Map/Views/MapDetail.swift b/Map/Views/MapDetail.swift
index 6ba92bb..bebe3a1 100644
--- a/Map/Views/MapDetail.swift
+++ b/Map/Views/MapDetail.swift
@@ -5,9 +5,25 @@
// Created by Ruben Beltran del Rio on 2/1/21.
//
+import Combine
import CoreData
import SwiftUI
+class SaveTimer {
+ let currentTimePublisher = Timer.TimerPublisher(interval: 1, runLoop: .main, mode: .default)
+ let cancellable: AnyCancellable?
+
+ init() {
+ self.cancellable = currentTimePublisher.connect() as? AnyCancellable
+ }
+
+ deinit {
+ self.cancellable?.cancel()
+ }
+}
+
+let timer = SaveTimer()
+
struct MapDetailView: View {
@Environment(\.managedObjectContext) private var viewContext
@Environment(\.colorScheme) var colorScheme
@@ -20,23 +36,8 @@ struct MapDetailView: View {
MapColor.colorForScheme(colorScheme)
}
- private var title: Binding<String> {
- Binding(
- get: { map.title ?? "" },
- set: { title in
- map.title = title
- }
- )
- }
-
- private var content: Binding<String> {
- Binding(
- get: { map.content ?? "" },
- set: { content in
- map.content = content
- }
- )
- }
+ @State var title: String
+ @State var content: String
var body: some View {
if map.uuid != nil {
@@ -44,10 +45,7 @@ struct MapDetailView: View {
VStack {
HStack {
TextField(
- "Title", text: title,
- onCommit: {
- try? viewContext.save()
- }
+ "Title", text: $title
).font(.title2).textFieldStyle(PlainTextFieldStyle()).padding(.vertical, 4.0).padding(
.leading, 4.0)
Button(action: saveText) {
@@ -60,25 +58,34 @@ struct MapDetailView: View {
EvolutionPicker()
ZStack(alignment: .topLeading) {
- MapTextEditor(text: content, colorScheme: colorScheme).onChange(of: map.content) { _ in
- try? viewContext.save()
- }
- .background(mapColor.background)
- .foregroundColor(mapColor.foreground)
- .frame(minHeight: 96.0)
+ MapTextEditor(text: $content, colorScheme: colorScheme)
+ .background(mapColor.background)
+ .foregroundColor(mapColor.foreground)
+ .frame(minHeight: 96.0)
}.padding(.top, 8.0).padding(.leading, 8.0).background(mapColor.background).cornerRadius(
5.0)
}.padding(.horizontal, 8.0)
ScrollView([.horizontal, .vertical]) {
- MapRenderView(
- content: content.wrappedValue, evolution: Stage.stages(store.state.selectedEvolution))
+ SlowMapRender(
+ content: content, evolution: Stage.stages(store.state.selectedEvolution),
+ colorScheme: colorScheme)
}.background(mapColor.background)
+ }.onReceive(timer.currentTimePublisher) { _ in
+ saveModel()
+ }.onDisappear {
+ saveModel()
}
} else {
DefaultMapView()
}
}
+ private func saveModel() {
+ map.content = content
+ map.title = title
+ try? viewContext.save()
+ }
+
private func saveText() {
store.send(.exportMapAsText(map: map))
}
@@ -90,7 +97,7 @@ struct MapDetailView: View {
struct MapDetailView_Previews: PreviewProvider {
static var previews: some View {
- MapDetailView(map: Map()).environment(
+ MapDetailView(map: Map(), title: "", content: "").environment(
\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
diff --git a/Map/Views/MapRender.swift b/Map/Views/MapRender.swift
index 7623b61..aa97e22 100644
--- a/Map/Views/MapRender.swift
+++ b/Map/Views/MapRender.swift
@@ -14,8 +14,10 @@ struct MapRenderView: View {
@Environment(\.colorScheme) var colorScheme
- var content: String
- let evolution: Stage
+ @Binding var content: String
+ @Binding var evolution: Stage
+
+ @State var parsedMap: ParsedMap = ParsedMap.empty
let mapSize = CGSize(width: 1300.0, height: 1000.0)
@@ -23,10 +25,6 @@ struct MapRenderView: View {
let vertexSize = CGSize(width: 25.0, height: 25.0)
let padding = CGFloat(30.0)
- var parsedMap: ParsedMap {
- return Map.parse(content: content)
- }
-
var body: some View {
ZStack(alignment: .topLeading) {
@@ -50,13 +48,19 @@ struct MapRenderView: View {
}.frame(
width: mapSize.width,
height: mapSize.height + 2 * padding, alignment: .topLeading
- ).padding(padding)
+ ).onAppear {
+ self.parsedMap = Map.parse(content: content)
+ }.padding(padding).onChange(of: content) { newState in
+ self.parsedMap = Map.parse(content: newState)
+ }
}
}
struct MapRenderView_Previews: PreviewProvider {
static var previews: some View {
- MapRenderView(content: "", evolution: Stage.stages(.general)).environment(
+ MapRenderView(
+ content: Binding.constant(""), evolution: Binding.constant(Stage.stages(.general))
+ ).environment(
\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
diff --git a/Map/Views/MapTextEditor.swift b/Map/Views/MapTextEditor.swift
index e030d64..4fbff82 100644
--- a/Map/Views/MapTextEditor.swift
+++ b/Map/Views/MapTextEditor.swift
@@ -30,6 +30,7 @@ class MapTextEditorController: NSViewController {
scrollView.translatesAutoresizingMaskIntoConstraints = false
+ textView.allowsUndo = true
textView.delegate = self
textView.textStorage?.delegate = self
textView.string = self.text
diff --git a/Map/Views/SlowMapRender.swift b/Map/Views/SlowMapRender.swift
new file mode 100644
index 0000000..b7cd842
--- /dev/null
+++ b/Map/Views/SlowMapRender.swift
@@ -0,0 +1,90 @@
+import Cocoa
+import SwiftUI
+
+class SlowMapRenderController: NSViewController {
+
+ var content: String
+ private var contentBinding: Binding<String> {
+ Binding(
+ get: { self.content },
+ set: { content in
+ self.content = content
+ }
+ )
+ }
+
+ var evolution: Stage
+ private var evolutionBinding: Binding<Stage> {
+ Binding(
+ get: { self.evolution },
+ set: { evolution in
+ self.evolution = evolution
+ }
+ )
+ }
+ private var colorSchemeEnvironment: ObservableColorSchemeEnvironment
+
+ private let debouncer: Debouncer = Debouncer(seconds: 0.1)
+
+ init(content: String, evolution: Stage, colorScheme: ColorScheme) {
+
+ self.content = content
+ self.evolution = evolution
+ self.colorSchemeEnvironment = ObservableColorSchemeEnvironment(colorScheme: colorScheme)
+ super.init(nibName: nil, bundle: nil)
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func loadView() {
+
+ let renderView = MapRenderView(content: self.contentBinding, evolution: self.evolutionBinding)
+ .environmentObject(self.colorSchemeEnvironment)
+ let hostingView = NSHostingView(rootView: renderView)
+
+ self.view = hostingView
+ }
+
+ func update(content: String, evolution: Stage, colorScheme: ColorScheme) {
+ self.debouncer.debounce {
+ DispatchQueue.main.async {
+ print("Updating: START")
+ self.content = content
+ self.evolution = evolution
+ self.colorSchemeEnvironment.colorScheme = colorScheme
+ print("Updating: END")
+ }
+ }
+ }
+
+ private class ObservableColorSchemeEnvironment: ObservableObject {
+ @Published var colorScheme: ColorScheme
+
+ init(colorScheme: ColorScheme) {
+ self.colorScheme = colorScheme
+ }
+ }
+}
+
+struct SlowMapRender: NSViewControllerRepresentable {
+
+ var content: String
+ var evolution: Stage
+ let colorScheme: ColorScheme
+
+ func makeNSViewController(
+ context: NSViewControllerRepresentableContext<SlowMapRender>
+ ) -> SlowMapRenderController {
+ return SlowMapRenderController(content: content, evolution: evolution, colorScheme: colorScheme)
+ }
+
+ func updateNSViewController(
+ _ nsViewController: SlowMapRenderController,
+ context: NSViewControllerRepresentableContext<SlowMapRender>
+ ) {
+ nsViewController.update(
+ content: content, evolution: evolution, colorScheme: context.environment.colorScheme)
+ }
+}