From aafb2c74a21ab8edf095743d0a6f1b6224262a25 Mon Sep 17 00:00:00 2001 From: dscyrescotti Date: Sun, 7 Jul 2024 18:35:10 +0700 Subject: [PATCH] feat: open memo object over dasboardview instead of new window --- Memola.xcodeproj/project.pbxproj | 4 ++ Memola/App/MemolaApp.swift | 14 +---- .../Dashboard/Dashboard/DashboardView.swift | 51 +++++++++++------- .../Dashboard/Dashboard/MemoManager.swift | 37 +++++++++++++ .../Dashboard/Details/Memos/MemosView.swift | 30 +---------- .../Dashboard/Details/Trash/TrashView.swift | 22 +------- .../Memo/ElementToolbar/ElementToolbar.swift | 12 ++--- Memola/Features/Memo/Memo/MemoView.swift | 2 +- Memola/Features/Memo/PenDock/PenDock.swift | 54 ++++++++++--------- Memola/Features/Memo/Toolbar/Toolbar.swift | 6 ++- 10 files changed, 114 insertions(+), 118 deletions(-) create mode 100644 Memola/Features/Dashboard/Dashboard/MemoManager.swift diff --git a/Memola.xcodeproj/project.pbxproj b/Memola.xcodeproj/project.pbxproj index 89feef7..53aee4e 100644 --- a/Memola.xcodeproj/project.pbxproj +++ b/Memola.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ EC2BEBF82C0F601A005DB0AF /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2BEBF72C0F601A005DB0AF /* Node.swift */; }; EC35655A2BF060D900A4E0BF /* Quad.metal in Sources */ = {isa = PBXBuildFile; fileRef = EC3565592BF060D900A4E0BF /* Quad.metal */; }; EC37FB122C1B2DD90008D976 /* ToolSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC37FB112C1B2DD90008D976 /* ToolSelection.swift */; }; + EC3D67CC2C3AAD5E00359400 /* MemoManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3D67CB2C3AAD5E00359400 /* MemoManager.swift */; }; EC42F7852C25267000E86E96 /* ElementGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC42F7842C25267000E86E96 /* ElementGroup.swift */; }; EC4538892BEBCAE000A86FEC /* Quad.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC4538882BEBCAE000A86FEC /* Quad.swift */; }; EC5050072BF65CED00B4D86E /* PenDropDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC5050062BF65CED00B4D86E /* PenDropDelegate.swift */; }; @@ -151,6 +152,7 @@ EC2BEBF72C0F601A005DB0AF /* Node.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; EC3565592BF060D900A4E0BF /* Quad.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Quad.metal; sourceTree = ""; }; EC37FB112C1B2DD90008D976 /* ToolSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolSelection.swift; sourceTree = ""; }; + EC3D67CB2C3AAD5E00359400 /* MemoManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoManager.swift; sourceTree = ""; }; EC42F7842C25267000E86E96 /* ElementGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementGroup.swift; sourceTree = ""; }; EC4538882BEBCAE000A86FEC /* Quad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Quad.swift; sourceTree = ""; }; EC5050062BF65CED00B4D86E /* PenDropDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenDropDelegate.swift; sourceTree = ""; }; @@ -279,6 +281,7 @@ EC01511C2C305C99008A115E /* Dashboard */ = { isa = PBXGroup; children = ( + EC3D67CB2C3AAD5E00359400 /* MemoManager.swift */, EC01511D2C305CA9008A115E /* DashboardView.swift */, ); path = Dashboard; @@ -1021,6 +1024,7 @@ ECA738A82BE6025900A4542E /* GraphicUniforms.swift in Sources */, EC01511E2C305CA9008A115E /* DashboardView.swift in Sources */, EC8F54AE2C2AF5A4001C7C74 /* LineGridVertex.swift in Sources */, + EC3D67CC2C3AAD5E00359400 /* MemoManager.swift in Sources */, EC5E83902BFDB69C00261D9C /* MovingAverage.swift in Sources */, ECFA15262BEF224900455818 /* StrokeObject.swift in Sources */, ECA738FC2BE61C5200A4542E /* Persistence.swift in Sources */, diff --git a/Memola/App/MemolaApp.swift b/Memola/App/MemolaApp.swift index 93f81ed..d262564 100644 --- a/Memola/App/MemolaApp.swift +++ b/Memola/App/MemolaApp.swift @@ -29,19 +29,7 @@ struct MemolaApp: App { .defaultPosition(.center) .windowResizability(.contentSize) .defaultSize(width: 1200, height: 800) - #endif - WindowGroup(id: "memo-view", for: URL.self) { url in - if let url = url.wrappedValue, let memo = Persistence.loadMemo(of: url) { - MemoView(memo: memo) - #if os(macOS) - .frame(minWidth: 1000, minHeight: 600) - #endif - } - } - #if os(macOS) - .defaultPosition(.center) - .windowResizability(.contentSize) - .defaultSize(width: 1200, height: 800) + .windowToolbarStyle(.unifiedCompact) #endif } } diff --git a/Memola/Features/Dashboard/Dashboard/DashboardView.swift b/Memola/Features/Dashboard/Dashboard/DashboardView.swift index e027341..5dec179 100644 --- a/Memola/Features/Dashboard/Dashboard/DashboardView.swift +++ b/Memola/Features/Dashboard/Dashboard/DashboardView.swift @@ -10,38 +10,54 @@ import SwiftUI struct DashboardView: View { @Environment(\.horizontalSizeClass) var horizontalSizeClass - #if os(iOS) - @State var memo: MemoObject? - #endif + @StateObject var memoManager: MemoManager = .shared + @State var sidebarItem: SidebarItem? = .memos + @Namespace var namespace + var body: some View { + #if os(macOS) NavigationSplitView { Sidebar(sidebarItem: $sidebarItem, horizontalSizeClass: horizontalSizeClass) } detail: { switch sidebarItem { case .memos: - #if os(macOS) MemosView() - #else - MemosView(memo: $memo) - #endif case .trash: - #if os(macOS) TrashView(sidebarItem: $sidebarItem) - #else - TrashView(memo: $memo, sidebarItem: $sidebarItem) - #endif default: - #if os(macOS) MemosView() - #else - MemosView(memo: $memo) - #endif } } - #if os(iOS) - .fullScreenCover(item: $memo) { memo in + .toolbar(memoManager.memoObject == nil ? .visible : .hidden, for: .windowToolbar) + .toolbarBackground(memoManager.memoObject == nil ? .clear : Color(nsColor: .windowBackgroundColor), for: .windowToolbar) + .overlay { + if let memo = memoManager.memoObject { + MemoView(memo: memo) + .onDisappear { + withPersistence(\.viewContext) { context in + try context.saveIfNeeded() + context.refreshAllObjects() + } + } + .transition(.move(edge: .bottom)) + } + } + #else + NavigationSplitView { + Sidebar(sidebarItem: $sidebarItem, horizontalSizeClass: horizontalSizeClass) + } detail: { + switch sidebarItem { + case .memos: + MemosView() + case .trash: + TrashView(sidebarItem: $sidebarItem) + default: + MemosView() + } + } + .fullScreenCover(item: $memoManager.memo) { memo in MemoView(memo: memo) .onDisappear { withPersistence(\.viewContext) { context in @@ -50,7 +66,6 @@ struct DashboardView: View { } } } - #else #endif } } diff --git a/Memola/Features/Dashboard/Dashboard/MemoManager.swift b/Memola/Features/Dashboard/Dashboard/MemoManager.swift new file mode 100644 index 0000000..e2adfdd --- /dev/null +++ b/Memola/Features/Dashboard/Dashboard/MemoManager.swift @@ -0,0 +1,37 @@ +// +// MemoManager.swift +// Memola +// +// Created by Dscyre Scotti on 7/7/24. +// + +import SwiftUI +import Foundation + +class MemoManager: ObservableObject { + static let shared: MemoManager = .init() + + @Published var memoObject: MemoObject? + + private init() { } + + func openMemo(_ memoObject: MemoObject?) { + #if os(macOS) + withAnimation(.easeOut) { + self.memoObject = memoObject + } + #else + self.memoObject = memoObject + #endif + } + + func closeMemo() { + #if os(macOS) + withAnimation(.easeOut) { + self.memoObject = nil + } + #else + self.memoObject = nil + #endif + } +} diff --git a/Memola/Features/Dashboard/Details/Memos/MemosView.swift b/Memola/Features/Dashboard/Details/Memos/MemosView.swift index 28165ff..0edbd98 100644 --- a/Memola/Features/Dashboard/Details/Memos/MemosView.swift +++ b/Memola/Features/Dashboard/Details/Memos/MemosView.swift @@ -8,9 +8,6 @@ import SwiftUI struct MemosView: View { - #if os(macOS) - @Environment(\.openWindow) var openWindow - #endif @Environment(\.horizontalSizeClass) var horizontalSizeClass @FetchRequest var memoObjects: FetchedResults @@ -18,10 +15,6 @@ struct MemosView: View { @State var query: String = "" @State var currentDate: Date = .now - #if os(iOS) - @Binding var memo: MemoObject? - #endif - @AppStorage("memola.memo-objects.memos.sort") var sort: Sort = .recent @AppStorage("memola.memo-objects.memos.filter") var filter: Filter = .none @@ -31,7 +24,6 @@ struct MemosView: View { query.isEmpty ? .memoEmpty : .memoNotFound } - #if os(macOS) init() { let standard = UserDefaults.standard var descriptors: [SortDescriptor] = [] @@ -45,22 +37,6 @@ struct MemosView: View { let predicate = NSCompoundPredicate(type: .and, subpredicates: predicates) _memoObjects = FetchRequest(sortDescriptors: descriptors, predicate: predicate) } - #else - init(memo: Binding) { - _memo = memo - let standard = UserDefaults.standard - var descriptors: [SortDescriptor] = [] - var predicates: [NSPredicate] = [NSPredicate(format: "isTrash = NO")] - let sort = Sort(rawValue: standard.value(forKey: "memola.memo-objects.memos.sort") as? String ?? "") ?? .recent - let filter = Filter(rawValue: standard.value(forKey: "memola.memo-objects.memos.filter") as? String ?? "") ?? .none - if filter == .favorites { - predicates.append(NSPredicate(format: "isFavorite = YES")) - } - descriptors = sort.memoSortDescriptors - let predicate = NSCompoundPredicate(type: .and, subpredicates: predicates) - _memoObjects = FetchRequest(sortDescriptors: descriptors, predicate: predicate) - } - #endif var body: some View { MemoGrid(memoObjects: memoObjects, placeholder: placeholder) { memoObject, cellWidth in @@ -273,11 +249,7 @@ struct MemosView: View { } func openMemo(for memo: MemoObject) { - #if os(macOS) - openWindow(id: "memo-view", value: memo.objectID.uriRepresentation()) - #else - self.memo = memo - #endif + MemoManager.shared.openMemo(memo) } func updatePredicate() { diff --git a/Memola/Features/Dashboard/Details/Trash/TrashView.swift b/Memola/Features/Dashboard/Details/Trash/TrashView.swift index 0d7d628..c484474 100644 --- a/Memola/Features/Dashboard/Details/Trash/TrashView.swift +++ b/Memola/Features/Dashboard/Details/Trash/TrashView.swift @@ -8,9 +8,6 @@ import SwiftUI struct TrashView: View { - #if os(macOS) - @Environment(\.openWindow) var openWindow - #endif @Environment(\.horizontalSizeClass) var horizontalSizeClass @FetchRequest var memoObjects: FetchedResults @@ -19,31 +16,18 @@ struct TrashView: View { @State var restoredMemo: MemoObject? @State var deletedMemo: MemoObject? - #if os(iOS) - @Binding var memo: MemoObject? - #endif @Binding var sidebarItem: SidebarItem? var placeholder: Placeholder.Info { query.isEmpty ? .trashEmpty : .trashNotFound } - #if os(macOS) init(sidebarItem: Binding) { _sidebarItem = sidebarItem let descriptors = [SortDescriptor(\MemoObject.deletedAt, order: .reverse)] let predicate = NSPredicate(format: "isTrash = YES") _memoObjects = FetchRequest(sortDescriptors: descriptors, predicate: predicate) } - #else - init(memo: Binding, sidebarItem: Binding) { - _memo = memo - _sidebarItem = sidebarItem - let descriptors = [SortDescriptor(\MemoObject.deletedAt, order: .reverse)] - let predicate = NSPredicate(format: "isTrash = YES") - _memoObjects = FetchRequest(sortDescriptors: descriptors, predicate: predicate) - } - #endif var body: some View { let restoresMemo = Binding { @@ -161,11 +145,7 @@ struct TrashView: View { restoreMemo(for: memo) self.sidebarItem = .memos if let memo { - #if os(macOS) - openWindow(id: "memo-view", value: memo.objectID.uriRepresentation()) - #else - self.memo = memo - #endif + MemoManager.shared.openMemo(memo) } } diff --git a/Memola/Features/Memo/ElementToolbar/ElementToolbar.swift b/Memola/Features/Memo/ElementToolbar/ElementToolbar.swift index e1c45bd..7e03b12 100644 --- a/Memola/Features/Memo/ElementToolbar/ElementToolbar.swift +++ b/Memola/Features/Memo/ElementToolbar/ElementToolbar.swift @@ -156,11 +156,13 @@ struct ElementToolbar: View { .buttonStyle(.plain) #endif .background { + #if os(iOS) if tool.selection == .photo { Color.accentColor .clipShape(.rect(cornerRadius: 8)) .matchedGeometryEffect(id: "element.toolbar.bg", in: namespace) } + #endif if tool.selection != .photo { Color.clear .matchedGeometryEffect(id: "element.toolbar.photo.options", in: namespace) @@ -172,13 +174,6 @@ struct ElementToolbar: View { .transition(.blurReplace.animation(.easeIn(duration: 0.1))) } } -// .background { -// if tool.selection == .photo { -// RoundedRectangle(cornerRadius: 8) -// .fill(Color.white.tertiary) -// .transition(.move(edge: .leading).combined(with: .opacity).animation(.easeIn(duration: 0.1))) -// } -// } } .background { RoundedRectangle(cornerRadius: 8) @@ -228,6 +223,7 @@ struct ElementToolbar: View { var photoOption: some View { HStack(spacing: 0) { + #if os(iOS) Button { openCamera() } label: { @@ -237,9 +233,7 @@ struct ElementToolbar: View { .clipShape(.rect(cornerRadius: 8)) .contentShape(.rect(cornerRadius: 8)) } - #if os(iOS) .hoverEffect(.lift) - #else .buttonStyle(.plain) #endif PhotosPicker(selection: $photosPickerItem, matching: .images, preferredItemEncoding: .compatible) { diff --git a/Memola/Features/Memo/Memo/MemoView.swift b/Memola/Features/Memo/Memo/MemoView.swift index 9ebb954..4d47e91 100644 --- a/Memola/Features/Memo/Memo/MemoView.swift +++ b/Memola/Features/Memo/Memo/MemoView.swift @@ -15,10 +15,10 @@ struct MemoView: View { @StateObject var canvas: Canvas @StateObject var history: History - @State var memo: MemoObject @State var title: String @FocusState var textFieldState: Bool + let memo: MemoObject let size: CGFloat = 32 init(memo: MemoObject) { diff --git a/Memola/Features/Memo/PenDock/PenDock.swift b/Memola/Features/Memo/PenDock/PenDock.swift index 58a7b0a..e60947b 100644 --- a/Memola/Features/Memo/PenDock/PenDock.swift +++ b/Memola/Features/Memo/PenDock/PenDock.swift @@ -62,35 +62,37 @@ struct PenDock: View { .transition(.move(edge: .trailing).combined(with: .blurReplace)) } } else { - ZStack(alignment: .bottomTrailing) { - if !canvas.locksCanvas { - GeometryReader { proxy in - HStack(alignment: .bottom, spacing: 10) { - newPenButton - .frame(height: height * factor - 18) - compactPenItemList - .fixedSize(horizontal: false, vertical: true) - compactPenPropertyTool + GeometryReader { proxy in + ZStack(alignment: .bottomTrailing) { + if !canvas.locksCanvas { + GeometryReader { proxy in + HStack(alignment: .bottom, spacing: 10) { + newPenButton + .frame(height: height * factor - 18) + compactPenItemList + .fixedSize(horizontal: false, vertical: true) + compactPenPropertyTool + } + .padding(.horizontal, 10) + .clipped() + .background(alignment: .bottom) { + RoundedRectangle(cornerRadius: 8) + .fill(.regularMaterial) + .frame(height: height * factor - 18) + } + .padding(.horizontal, 10) + .padding(.bottom, 20) + .frame(maxWidth: min(proxy.size.height, proxy.size.width), maxHeight: .infinity, alignment: .bottom) + .frame(maxWidth: .infinity) } - .padding(.horizontal, 10) - .clipped() - .background(alignment: .bottom) { - RoundedRectangle(cornerRadius: 8) - .fill(.regularMaterial) - .frame(height: height * factor - 18) - } - .padding(.horizontal, 10) - .padding(.bottom, 20) - .frame(maxWidth: min(proxy.size.height, proxy.size.width), maxHeight: .infinity, alignment: .bottom) - .frame(maxWidth: .infinity) + .transition(.move(edge: .bottom).combined(with: .blurReplace)) } - .transition(.move(edge: .bottom).combined(with: .blurReplace)) + lockButton + .frame(maxWidth: .infinity, alignment: .bottomTrailing) + .padding(10) + .offset(y: canvas.locksCanvas || proxy.size.width > proxy.size.height ? 0 : -(height * factor - size + 30)) + .transition(.move(edge: .trailing).combined(with: .blurReplace)) } - lockButton - .frame(maxWidth: .infinity, alignment: .bottomTrailing) - .padding(10) - .offset(y: canvas.locksCanvas ? 0 : -(height * factor - size + 30)) - .transition(.move(edge: .trailing).combined(with: .blurReplace)) } } #endif diff --git a/Memola/Features/Memo/Toolbar/Toolbar.swift b/Memola/Features/Memo/Toolbar/Toolbar.swift index 05dc21c..24629c2 100644 --- a/Memola/Features/Memo/Toolbar/Toolbar.swift +++ b/Memola/Features/Memo/Toolbar/Toolbar.swift @@ -17,11 +17,11 @@ struct Toolbar: View { @ObservedObject var history: History @State var title: String - @State var memo: MemoObject @FocusState var textFieldState: Bool let size: CGFloat + let memo: MemoObject init(size: CGFloat, memo: MemoObject, tool: Tool, canvas: Canvas, history: History) { self.size = size @@ -190,7 +190,11 @@ struct Toolbar: View { func closeMemo() { canvas.save(for: memo) { + #if os(macOS) + MemoManager.shared.closeMemo() + #else dismiss() + #endif } } }