diff --git a/Memola.xcodeproj/project.pbxproj b/Memola.xcodeproj/project.pbxproj index 9b61856..86c305c 100644 --- a/Memola.xcodeproj/project.pbxproj +++ b/Memola.xcodeproj/project.pbxproj @@ -26,12 +26,13 @@ EC2002D72C4160EF002EBD5F /* EditCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2002D62C4160EF002EBD5F /* EditCommands.swift */; }; EC2002D92C4161ED002EBD5F /* ViewCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2002D82C4161ED002EBD5F /* ViewCommands.swift */; }; EC2002DD2C4163E8002EBD5F /* AppCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2002DC2C4163E8002EBD5F /* AppCommands.swift */; }; - EC2002E12C416470002EBD5F /* Shortcut.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2002E02C416470002EBD5F /* Shortcut.swift */; }; - EC2002E52C416551002EBD5F /* Shortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2002E42C416551002EBD5F /* Shortcuts.swift */; }; - EC2002E92C4167C5002EBD5F /* ShortcutKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2002E82C4167C5002EBD5F /* ShortcutKey.swift */; }; EC2002ED2C417B68002EBD5F /* AppScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2002EC2C417B68002EBD5F /* AppScene.swift */; }; EC2002F02C417BF1002EBD5F /* ActiveSceneKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2002EF2C417BF1002EBD5F /* ActiveSceneKey.swift */; }; EC2106AD2C10C2A700FBE27C /* AnyStroke.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2106AC2C10C2A700FBE27C /* AnyStroke.swift */; }; + EC2117632C47FA30005B32A1 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2117622C47FA30005B32A1 /* SettingsView.swift */; }; + EC2117662C4802C0005B32A1 /* AppWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2117652C4802C0005B32A1 /* AppWindow.swift */; }; + EC2117692C480D4D005B32A1 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2117682C480D4D005B32A1 /* AboutView.swift */; }; + EC21176B2C480EE6005B32A1 /* Bundle++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC21176A2C480EE6005B32A1 /* Bundle++.swift */; }; EC2BEBF42C0F5FF7005DB0AF /* RTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2BEBF32C0F5FF7005DB0AF /* RTree.swift */; }; EC2BEBF62C0F600D005DB0AF /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2BEBF52C0F600D005DB0AF /* Box.swift */; }; EC2BEBF82C0F601A005DB0AF /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2BEBF72C0F601A005DB0AF /* Node.swift */; }; @@ -163,12 +164,13 @@ EC2002D62C4160EF002EBD5F /* EditCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCommands.swift; sourceTree = ""; }; EC2002D82C4161ED002EBD5F /* ViewCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewCommands.swift; sourceTree = ""; }; EC2002DC2C4163E8002EBD5F /* AppCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCommands.swift; sourceTree = ""; }; - EC2002E02C416470002EBD5F /* Shortcut.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shortcut.swift; sourceTree = ""; }; - EC2002E42C416551002EBD5F /* Shortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shortcuts.swift; sourceTree = ""; }; - EC2002E82C4167C5002EBD5F /* ShortcutKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutKey.swift; sourceTree = ""; }; EC2002EC2C417B68002EBD5F /* AppScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScene.swift; sourceTree = ""; }; EC2002EF2C417BF1002EBD5F /* ActiveSceneKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSceneKey.swift; sourceTree = ""; }; EC2106AC2C10C2A700FBE27C /* AnyStroke.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyStroke.swift; sourceTree = ""; }; + EC2117622C47FA30005B32A1 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; + EC2117652C4802C0005B32A1 /* AppWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppWindow.swift; sourceTree = ""; }; + EC2117682C480D4D005B32A1 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; + EC21176A2C480EE6005B32A1 /* Bundle++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle++.swift"; sourceTree = ""; }; EC2BEBF32C0F5FF7005DB0AF /* RTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RTree.swift; sourceTree = ""; }; EC2BEBF52C0F600D005DB0AF /* Box.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = ""; }; EC2BEBF72C0F601A005DB0AF /* Node.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; @@ -403,33 +405,6 @@ path = Commands; sourceTree = ""; }; - EC2002DE2C41645A002EBD5F /* Shortcut */ = { - isa = PBXGroup; - children = ( - EC2002D32C416002002EBD5F /* Commands */, - EC2002DF2C416466002EBD5F /* Core */, - EC2002E72C4167B1002EBD5F /* EnvironmentValues */, - ); - path = Shortcut; - sourceTree = ""; - }; - EC2002DF2C416466002EBD5F /* Core */ = { - isa = PBXGroup; - children = ( - EC2002E02C416470002EBD5F /* Shortcut.swift */, - EC2002E42C416551002EBD5F /* Shortcuts.swift */, - ); - path = Core; - sourceTree = ""; - }; - EC2002E72C4167B1002EBD5F /* EnvironmentValues */ = { - isa = PBXGroup; - children = ( - EC2002E82C4167C5002EBD5F /* ShortcutKey.swift */, - ); - path = EnvironmentValues; - sourceTree = ""; - }; EC2002EE2C417BBF002EBD5F /* AppScene */ = { isa = PBXGroup; children = ( @@ -439,6 +414,39 @@ path = AppScene; sourceTree = ""; }; + EC2117602C47FA14005B32A1 /* Settings */ = { + isa = PBXGroup; + children = ( + EC2117672C480D45005B32A1 /* About */, + EC2117612C47FA29005B32A1 /* Settings */, + ); + path = Settings; + sourceTree = ""; + }; + EC2117612C47FA29005B32A1 /* Settings */ = { + isa = PBXGroup; + children = ( + EC2117622C47FA30005B32A1 /* SettingsView.swift */, + ); + path = Settings; + sourceTree = ""; + }; + EC2117642C4802B4005B32A1 /* AppWindow */ = { + isa = PBXGroup; + children = ( + EC2117652C4802C0005B32A1 /* AppWindow.swift */, + ); + path = AppWindow; + sourceTree = ""; + }; + EC2117672C480D45005B32A1 /* About */ = { + isa = PBXGroup; + children = ( + EC2117682C480D4D005B32A1 /* AboutView.swift */, + ); + path = About; + sourceTree = ""; + }; EC2BEBF22C0F5FE1005DB0AF /* RTree */ = { isa = PBXGroup; children = ( @@ -546,7 +554,7 @@ isa = PBXGroup; children = ( ECA738762BE5EE4E00A4542E /* App */, - EC2002DE2C41645A002EBD5F /* Shortcut */, + EC2002D32C416002002EBD5F /* Commands */, ECA7387E2BE5FE4200A4542E /* Canvas */, EC50500A2BF6672000B4D86E /* Components */, EC5050102BF670EE00B4D86E /* Config */, @@ -607,6 +615,7 @@ children = ( EC01511A2C305ABB008A115E /* Dashboard */, ECA7387B2BE5EF3500A4542E /* Memo */, + EC2117602C47FA14005B32A1 /* Settings */, ); path = Features; sourceTree = ""; @@ -981,6 +990,7 @@ ECF7B2CC2C39169C004D2C57 /* simd_float4x4++.swift */, ECF7B2CD2C39169C004D2C57 /* Image++.swift */, ECF7B2CE2C39169C004D2C57 /* View++.swift */, + EC21176A2C480EE6005B32A1 /* Bundle++.swift */, ); path = Extensions; sourceTree = ""; @@ -996,6 +1006,7 @@ ECF7B2E52C391DFA004D2C57 /* Utilies */ = { isa = PBXGroup; children = ( + EC2117642C4802B4005B32A1 /* AppWindow */, EC6E3BDD2C43D5A500DD20F3 /* SidebarVisibility */, EC2002EE2C417BBF002EBD5F /* AppScene */, ECF7B2CF2C39169C004D2C57 /* Extensions */, @@ -1120,7 +1131,6 @@ EC01511E2C305CA9008A115E /* DashboardView.swift in Sources */, EC8F54AE2C2AF5A4001C7C74 /* LineGridVertex.swift in Sources */, EC2002F02C417BF1002EBD5F /* ActiveSceneKey.swift in Sources */, - EC2002E12C416470002EBD5F /* Shortcut.swift in Sources */, EC5E83902BFDB69C00261D9C /* MovingAverage.swift in Sources */, ECFA15262BEF224900455818 /* StrokeObject.swift in Sources */, ECA738FC2BE61C5200A4542E /* Persistence.swift in Sources */, @@ -1130,10 +1140,10 @@ ECFA15222BEF21F500455818 /* CanvasObject.swift in Sources */, ECA738AA2BE6026D00A4542E /* Uniforms.swift in Sources */, ECF7B2D12C39169C004D2C57 /* CGAffineTransform++.swift in Sources */, - EC2002E52C416551002EBD5F /* Shortcuts.swift in Sources */, EC2002DD2C4163E8002EBD5F /* AppCommands.swift in Sources */, EC1815082C2D980B00541369 /* Sort.swift in Sources */, EC6E3BD92C43C6C000DD20F3 /* OnDismissSearchViewModifier.swift in Sources */, + EC2117662C4802C0005B32A1 /* AppWindow.swift in Sources */, ECA7387D2BE5EF4B00A4542E /* MemoView.swift in Sources */, ECDDD40D2C366B3B00DF9D5E /* PreviewRenderPass.swift in Sources */, ECF7B2E12C39169C004D2C57 /* View++.swift in Sources */, @@ -1151,6 +1161,7 @@ EC2BEBF62C0F600D005DB0AF /* Box.swift in Sources */, ECA738832BE5FEFE00A4542E /* RenderPass.swift in Sources */, ECEC01A82BEE11BA006DA24C /* QuadShape.swift in Sources */, + EC2117692C480D4D005B32A1 /* AboutView.swift in Sources */, ECF7B2DC2C39169C004D2C57 /* MTLTexture++.swift in Sources */, ECA738862BE5FF2500A4542E /* Canvas.swift in Sources */, ECF7B2DE2C39169C004D2C57 /* NSManagedObjectContext++.swift in Sources */, @@ -1159,6 +1170,7 @@ EC8C9DCE2C39882500A8F3C4 /* NSSyncScrollView.swift in Sources */, ECF7B2D72C39169C004D2C57 /* Color++.swift in Sources */, EC01512C2C306BEF008A115E /* MemoCard.swift in Sources */, + EC2117632C47FA30005B32A1 /* SettingsView.swift in Sources */, ECA738D42BE60F9100A4542E /* StrokeGenerator.swift in Sources */, ECF7B2D52C39169C004D2C57 /* CGSize++.swift in Sources */, ECA739082BE623F300A4542E /* PenDock.swift in Sources */, @@ -1191,7 +1203,6 @@ ECA7388F2BE600DA00A4542E /* Grid.metal in Sources */, EC2BEBF42C0F5FF7005DB0AF /* RTree.swift in Sources */, ECA738C92BE60EF700A4542E /* GraphicContext.swift in Sources */, - EC2002E92C4167C5002EBD5F /* ShortcutKey.swift in Sources */, EC0D14212BF79C73009BFE5F /* ToolObject.swift in Sources */, ECDAC07B2C318DBC0000ED77 /* ElementToolbar.swift in Sources */, EC50500D2BF6674400B4D86E /* OnDragViewModifier.swift in Sources */, @@ -1213,6 +1224,7 @@ EC5050072BF65CED00B4D86E /* PenDropDelegate.swift in Sources */, ECF7B2E42C39174D004D2C57 /* Platform.swift in Sources */, ECA738C12BE60E5300A4542E /* PenStyle.swift in Sources */, + EC21176B2C480EE6005B32A1 /* Bundle++.swift in Sources */, EC2002D72C4160EF002EBD5F /* EditCommands.swift in Sources */, ECF7B2DF2C39169C004D2C57 /* simd_float4x4++.swift in Sources */, ECF7B2D02C39169C004D2C57 /* Array++.swift in Sources */, @@ -1384,7 +1396,7 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.0; - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.0.0; MTLLINKER_FLAGS = ""; MTL_COMPILER_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.example.Memola; @@ -1422,7 +1434,7 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.0; - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.0.0; MTLLINKER_FLAGS = ""; MTL_COMPILER_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.example.Memola; diff --git a/Memola/App/Application.swift b/Memola/App/Application.swift index 8f473e9..23e2572 100644 --- a/Memola/App/Application.swift +++ b/Memola/App/Application.swift @@ -11,6 +11,8 @@ import SwiftUI final class Application: NSObject, ObservableObject { @Published var memoObject: MemoObject? @Published private(set) var sidebarVisibility: SidebarVisibility = .shown + + lazy var newMemoPublisher = PassthroughSubject() } extension Application { @@ -54,6 +56,17 @@ extension Application: NSApplicationDelegate { NSWindow.allowsAutomaticWindowTabbing = false UserDefaults.standard.register(defaults: ["NSQuitAlwaysKeepsWindows": false]) } + + func openWindow(for appWindow: AppWindow) { + let window = NSApplication.shared.windows.first { window in + window.identifier?.rawValue.contains(appWindow.id) == true + } + if window == nil, let url = appWindow.url { + NSWorkspace.shared.open(url) + } else { + window?.makeKeyAndOrderFront(nil) + } + } } #else extension Application: UIApplicationDelegate { diff --git a/Memola/App/MemolaApp.swift b/Memola/App/MemolaApp.swift index 5889ecd..7319024 100644 --- a/Memola/App/MemolaApp.swift +++ b/Memola/App/MemolaApp.swift @@ -16,7 +16,7 @@ struct MemolaApp: App { #endif var body: some Scene { - WindowGroup { + WindowGroup(id: AppWindow.dashboard.id) { DashboardView() .persistence(\.viewContext) .onReceive(NotificationCenter.default.publisher(for: Platform.Application.willTerminateNotification)) { _ in @@ -36,13 +36,38 @@ struct MemolaApp: App { .defaultPosition(.center) .windowResizability(.contentSize) .defaultSize(width: 1200, height: 800) - .windowToolbarStyle(.unifiedCompact) + .windowToolbarStyle(.unified) + .handlesExternalEvents(matching: [AppWindow.dashboard.id]) #endif .commands { - AppCommands() - FileCommands() + #if os(macOS) + AppCommands(application: application) + #endif + FileCommands(application: application) EditCommands() ViewCommands(application: application) } + #if os(macOS) + WindowGroup(id: AppWindow.settings.id) { + SettingsView() + .onReceive(NotificationCenter.default.publisher(for: Platform.Application.willTerminateNotification)) { _ in + withPersistenceSync(\.viewContext) { context in + try context.saveIfNeeded() + } + withPersistenceSync(\.backgroundContext) { context in + try context.saveIfNeeded() + } + } + #if os(macOS) + .frame(minWidth: 700, minHeight: 500) + #endif + .environmentObject(application) + } + .defaultPosition(.center) + .windowResizability(.contentSize) + .defaultSize(width: 800, height: 400) + .windowToolbarStyle(.unifiedCompact) + .handlesExternalEvents(matching: [AppWindow.settings.id]) + #endif } } diff --git a/Memola/Canvas/History/History.swift b/Memola/Canvas/History/History.swift index 9cbf6c8..83dc934 100644 --- a/Memola/Canvas/History/History.swift +++ b/Memola/Canvas/History/History.swift @@ -100,7 +100,9 @@ final class History: ObservableObject { } } } - redoStack.removeAll() + DispatchQueue.main.async { [weak self] in + self?.redoStack.removeAll() + } withPersistence(\.viewContext) { [weak memo] context in memo?.updatedAt = .now try context.saveIfNeeded() diff --git a/Memola/Shortcut/Commands/AppCommands.swift b/Memola/Commands/AppCommands.swift similarity index 63% rename from Memola/Shortcut/Commands/AppCommands.swift rename to Memola/Commands/AppCommands.swift index 16c39fd..99bec35 100644 --- a/Memola/Shortcut/Commands/AppCommands.swift +++ b/Memola/Commands/AppCommands.swift @@ -5,13 +5,20 @@ // Created by Dscyre Scotti on 7/12/24. // +#if os(macOS) import SwiftUI struct AppCommands: Commands { + @ObservedObject private var application: Application + + init(application: Application) { + self.application = application + } + var body: some Commands { CommandGroup(replacing: .appSettings) { Button { - + application.openWindow(for: .settings) } label: { Text("Services...") } @@ -19,3 +26,4 @@ struct AppCommands: Commands { } } } +#endif diff --git a/Memola/Shortcut/Commands/EditCommands.swift b/Memola/Commands/EditCommands.swift similarity index 100% rename from Memola/Shortcut/Commands/EditCommands.swift rename to Memola/Commands/EditCommands.swift diff --git a/Memola/Commands/FileCommands.swift b/Memola/Commands/FileCommands.swift new file mode 100644 index 0000000..51533e3 --- /dev/null +++ b/Memola/Commands/FileCommands.swift @@ -0,0 +1,41 @@ +// +// FileCommands.swift +// Memola +// +// Created by Dscyre Scotti on 7/12/24. +// + +import SwiftUI + +struct FileCommands: Commands { + @FocusedValue(\.activeSceneKey) private var appScene + + @ObservedObject private var application: Application + + init(application: Application) { + self.application = application + } + + var body: some Commands { + CommandGroup(replacing: .newItem) { + #if os(macOS) + if appScene == nil { + Button { + application.openWindow(for: .dashboard) + } label: { + Text("Open Dashboard") + } + .keyboardShortcut("m", modifiers: [.command]) + } + #endif + if appScene == .memos { + Button { + application.newMemoPublisher.send() + } label: { + Text("New Memo") + } + .keyboardShortcut("n", modifiers: [.command]) + } + } + } +} diff --git a/Memola/Shortcut/Commands/ViewCommands.swift b/Memola/Commands/ViewCommands.swift similarity index 100% rename from Memola/Shortcut/Commands/ViewCommands.swift rename to Memola/Commands/ViewCommands.swift diff --git a/Memola/Config/Info.plist b/Memola/Config/Info.plist index 5c10a78..d71b4c0 100644 --- a/Memola/Config/Info.plist +++ b/Memola/Config/Info.plist @@ -2,30 +2,39 @@ - NSCameraUsageDescription - Memola requires access to the camera to capture photos. - CFBundleShortVersionString - $(MARKETING_VERSION) - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - + NSHumanReadableCopyright + Copyright © 2024 Memola. All rights reserved. CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) - UILaunchScreen - - UILaunchScreen - - - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleExecutable $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + memola + + + + CFBundleVersion + $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS + NSCameraUsageDescription + Memola requires access to the camera to capture photos. UIApplicationSceneManifest UIApplicationSupportsMultipleScenes @@ -35,19 +44,23 @@ UIApplicationSupportsIndirectInputEvents - CFBundleVersion - $(CURRENT_PROJECT_VERSION) + UILaunchScreen + + UILaunchScreen + + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UISupportedInterfaceOrientations~iphone UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - CFBundleInfoDictionaryVersion - 6.0 - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleName - $(PRODUCT_NAME) diff --git a/Memola/Features/Dashboard/Dashboard/DashboardView.swift b/Memola/Features/Dashboard/Dashboard/DashboardView.swift index 48cc22c..4e1cfa1 100644 --- a/Memola/Features/Dashboard/Dashboard/DashboardView.swift +++ b/Memola/Features/Dashboard/Dashboard/DashboardView.swift @@ -52,7 +52,7 @@ struct DashboardView: View { } .animation(.easeIn, value: application.memoObject) .toolbar(application.memoObject == nil ? .visible : .hidden, for: .windowToolbar) - .toolbarBackground(application.memoObject == nil ? .clear : Color(nsColor: .windowBackgroundColor), for: .windowToolbar) + .toolbarBackground(Color(nsColor: .unemphasizedSelectedContentBackgroundColor), for: .windowToolbar) .onChange(of: columnVisibility) { oldValue, newValue in application.changeSidebarVisibility(newValue == .all ? .shown : .hidden) } diff --git a/Memola/Features/Dashboard/Details/Memos/MemosView.swift b/Memola/Features/Dashboard/Details/Memos/MemosView.swift index d40aa0e..95935d8 100644 --- a/Memola/Features/Dashboard/Details/Memos/MemosView.swift +++ b/Memola/Features/Dashboard/Details/Memos/MemosView.swift @@ -8,7 +8,6 @@ import SwiftUI struct MemosView: View { - @Environment(\.shortcut) private var shortcut @Environment(\.horizontalSizeClass) private var horizontalSizeClass @EnvironmentObject private var application: Application @@ -159,8 +158,10 @@ struct MemosView: View { .onReceive(timer) { date in currentDate = date } - .onReceive(shortcut.publisher) { shortcut in - handleShortcut(for: shortcut) + .onReceive(application.newMemoPublisher) { shortcut in + if application.memoObject == nil { + createMemo(title: "Untitled") + } } } @@ -289,15 +290,4 @@ struct MemosView: View { try context.saveIfNeeded() } } - - private func handleShortcut(for shortcut: Shortcuts) { - switch shortcut { - case .newMemo: - if application.memoObject == nil { - createMemo(title: "Untitled") - } - default: - break - } - } } diff --git a/Memola/Features/Dashboard/Details/Trash/TrashView.swift b/Memola/Features/Dashboard/Details/Trash/TrashView.swift index a3f9f8d..aa567bc 100644 --- a/Memola/Features/Dashboard/Details/Trash/TrashView.swift +++ b/Memola/Features/Dashboard/Details/Trash/TrashView.swift @@ -8,7 +8,6 @@ import SwiftUI struct TrashView: View { - @Environment(\.shortcut) private var shortcut @Environment(\.horizontalSizeClass) private var horizontalSizeClass @EnvironmentObject private var application: Application diff --git a/Memola/Features/Dashboard/Sidebar/Sidebar.swift b/Memola/Features/Dashboard/Sidebar/Sidebar.swift index 3d1794f..860bd49 100644 --- a/Memola/Features/Dashboard/Sidebar/Sidebar.swift +++ b/Memola/Features/Dashboard/Sidebar/Sidebar.swift @@ -9,35 +9,93 @@ import SwiftUI struct Sidebar: View { private let sidebarItems: [SidebarItem] = [.memos, .trash] - @Binding private var sidebarItem: SidebarItem? - private let horizontalSizeClass: UserInterfaceSizeClass? + @Binding private var sidebarItem: SidebarItem? + + @State private var presentsSettings: Bool = false + + #if os(macOS) + @EnvironmentObject private var application: Application + #endif + init(sidebarItem: Binding, horizontalSizeClass: UserInterfaceSizeClass?) { self._sidebarItem = sidebarItem self.horizontalSizeClass = horizontalSizeClass } var body: some View { + #if os(macOS) + regularList + #else + Group { + if horizontalSizeClass == .compact { + compactList + } else { + regularList + } + } + .sheet(isPresented: $presentsSettings) { + SettingsView() + } + #endif + } + + private var regularList: some View { + VStack(spacing: 10) { + list + Divider() + settingsButton + .buttonStyle(.unselected) + .padding(.horizontal, 10) + } + #if os(macOS) + .padding(.bottom, 10) + .background(Color(color: .windowBackgroundColor)) + #else + .background(Color(color: .secondarySystemBackground)) + #endif + .navigationSplitViewColumnWidth(min: 250, ideal: 250, max: 250) + } + + #if os(iOS) + private var compactList: some View { + list + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + settingsButton + } + } + } + #endif + + private var list: some View { List(selection: $sidebarItem) { ForEach(sidebarItems) { item in - if horizontalSizeClass == .compact { - Button { - sidebarItem = item - } label: { - Label(item.title, systemImage: item.icon) - .foregroundColor(.primary) + Group { + if horizontalSizeClass == .compact { + Button { + sidebarItem = item + } label: { + Label(item.title, systemImage: item.icon) + .foregroundColor(.primary) + } + } else { + Button { + sidebarItem = item + } label: { + Label(item.title, systemImage: item.icon) + .foregroundColor(.primary) + } + .buttonStyle(sidebarItem == item ? .selected : .unselected) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) } - } else { - Button { - sidebarItem = item - } label: { - Label(item.title, systemImage: item.icon) - .foregroundColor(.primary) - } - .buttonStyle(sidebarItem == item ? .selected : .unselected) - .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) } + #if os(macOS) + .padding(.top, item == .memos ? 20 : 0) + #else + .padding(.top, horizontalSizeClass == .regular ? (item == .memos ? 20 : 0) : 0) + #endif } } .listStyle(.sidebar) @@ -48,11 +106,22 @@ struct Sidebar: View { #else .background(Color(color: .secondarySystemBackground)) #endif - .navigationSplitViewColumnWidth(min: 250, ideal: 250, max: 250) #if os(iOS) .navigationBarTitleDisplayMode(horizontalSizeClass == .compact ? .automatic : .inline) #endif } + + private var settingsButton: some View { + Button { + #if os(macOS) + application.openWindow(for: .settings) + #else + presentsSettings.toggle() + #endif + } label: { + Label("Settings", systemImage: "gearshape.fill") + } + } } extension Sidebar { diff --git a/Memola/Features/Settings/About/AboutView.swift b/Memola/Features/Settings/About/AboutView.swift new file mode 100644 index 0000000..24682f0 --- /dev/null +++ b/Memola/Features/Settings/About/AboutView.swift @@ -0,0 +1,37 @@ +// +// AboutView.swift +// Memola +// +// Created by Dscyre Scotti on 7/17/24. +// + +import SwiftUI + +struct AboutView: View { + var body: some View { + List { + Section { + HStack { + Text("App Version") + Spacer() + Text("v\(Bundle.main.appVersion) (\(Bundle.main.appBuild))") + } + #if os(macOS) + .listRowSeparator(.hidden) + #endif + } + Section("COPYRIGHT") { + Text(Bundle.main.copyright) + .font(.callout) + #if os(macOS) + .listRowSeparator(.hidden) + #endif + } + } + .navigationTitle("About") + #if os(iOS) + .listStyle(.insetGrouped) + .navigationBarTitleDisplayMode(.inline) + #endif + } +} diff --git a/Memola/Features/Settings/Settings/SettingsView.swift b/Memola/Features/Settings/Settings/SettingsView.swift new file mode 100644 index 0000000..626d699 --- /dev/null +++ b/Memola/Features/Settings/Settings/SettingsView.swift @@ -0,0 +1,53 @@ +// +// SettingsView.swift +// Memola +// +// Created by Dscyre Scotti on 7/17/24. +// + +import SwiftUI + +struct SettingsView: View { + @Environment(\.dismiss) private var dismiss + + var body: some View { + NavigationStack { + List { + NavigationLink { + AboutView() + } label: { + Label("About", systemImage: "info.circle.fill") + .foregroundStyle(.primary) + } + } + .navigationTitle("Settings") + #if os(iOS) + .navigationBarTitleDisplayMode(.large) + #endif + .toolbar { + #if os(iOS) + ToolbarItem(placement: .topBarTrailing) { + Button { + dismiss() + } label: { + Text("Close") + } + } + #endif + } + } + .focusedSceneValue(\.activeSceneKey, .settings) + .interactiveDismissDisabled() + #if os(macOS) + .onAppear { + DispatchQueue.main.async { + NSApplication.shared.windows.forEach { window in + guard window.identifier?.rawValue.contains(AppWindow.settings.id) == true else { return } + window.standardWindowButton(.zoomButton)?.isEnabled = false + } + } + } + #endif + } +} + diff --git a/Memola/Shortcut/Commands/FileCommands.swift b/Memola/Shortcut/Commands/FileCommands.swift deleted file mode 100644 index 3a602ba..0000000 --- a/Memola/Shortcut/Commands/FileCommands.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// FileCommands.swift -// Memola -// -// Created by Dscyre Scotti on 7/12/24. -// - -import SwiftUI - -struct FileCommands: Commands { - @Environment(\.shortcut) private var shortcut - @FocusedValue(\.activeSceneKey) private var appScene - - var body: some Commands { - CommandGroup(replacing: .newItem) { - if appScene == .memos { - Button { - shortcut.trigger(.newMemo) - } label: { - Text("New Memo") - } - .keyboardShortcut("n", modifiers: [.command]) - } - } - } -} diff --git a/Memola/Shortcut/Core/Shortcut.swift b/Memola/Shortcut/Core/Shortcut.swift deleted file mode 100644 index 24c00ef..0000000 --- a/Memola/Shortcut/Core/Shortcut.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// Shortcut.swift -// Memola -// -// Created by Dscyre Scotti on 7/12/24. -// - -import Combine -import Foundation - -final class Shortcut { - static let shared: Shortcut = .init() - - private let _publisher = PassthroughSubject() - - lazy var publisher: AnyPublisher = { - _publisher.eraseToAnyPublisher() - }() - - private init() { } - - func trigger(_ shortcut: Shortcuts) { - _publisher.send(shortcut) - } -} diff --git a/Memola/Shortcut/Core/Shortcuts.swift b/Memola/Shortcut/Core/Shortcuts.swift deleted file mode 100644 index 438600e..0000000 --- a/Memola/Shortcut/Core/Shortcuts.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// Shortcuts.swift -// Memola -// -// Created by Dscyre Scotti on 7/12/24. -// - -import Foundation - -enum Shortcuts { - // MARK: - Memos - case newMemo - case findMemo -} diff --git a/Memola/Shortcut/EnvironmentValues/ShortcutKey.swift b/Memola/Shortcut/EnvironmentValues/ShortcutKey.swift deleted file mode 100644 index 4d1f1ab..0000000 --- a/Memola/Shortcut/EnvironmentValues/ShortcutKey.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// ShortcutKey.swift -// Memola -// -// Created by Dscyre Scotti on 7/12/24. -// - -import SwiftUI - -private struct ShortcutKey: EnvironmentKey { - static var defaultValue: Shortcut = .shared -} - -extension EnvironmentValues { - var shortcut: Shortcut { - get { self[ShortcutKey.self] } - } -} diff --git a/Memola/Utilies/AppScene/AppScene.swift b/Memola/Utilies/AppScene/AppScene.swift index 0c71200..7ea16fb 100644 --- a/Memola/Utilies/AppScene/AppScene.swift +++ b/Memola/Utilies/AppScene/AppScene.swift @@ -11,4 +11,5 @@ enum AppScene { case memos case trash case memo + case settings } diff --git a/Memola/Utilies/AppWindow/AppWindow.swift b/Memola/Utilies/AppWindow/AppWindow.swift new file mode 100644 index 0000000..6aeecc4 --- /dev/null +++ b/Memola/Utilies/AppWindow/AppWindow.swift @@ -0,0 +1,19 @@ +// +// AppWindow.swift +// Memola +// +// Created by Dscyre Scotti on 7/17/24. +// + +import Foundation + +enum AppWindow: String, Identifiable { + var id: String { rawValue } + + case dashboard + case settings + + var url: URL? { + URL(string: "memola:\(id)") + } +} diff --git a/Memola/Utilies/Extensions/Bundle++.swift b/Memola/Utilies/Extensions/Bundle++.swift new file mode 100644 index 0000000..d22aa37 --- /dev/null +++ b/Memola/Utilies/Extensions/Bundle++.swift @@ -0,0 +1,16 @@ +// +// Bundle++.swift +// Memola +// +// Created by Dscyre Scotti on 7/17/24. +// + +import Foundation + +extension Bundle { + var appBuild: String { getInfo("CFBundleVersion") } + var appVersion: String { getInfo("CFBundleShortVersionString") } + var copyright: String { getInfo("NSHumanReadableCopyright") } + + fileprivate func getInfo(_ key: String) -> String { infoDictionary?[key] as! String } +} diff --git a/Memola/Utilies/Extensions/Color++.swift b/Memola/Utilies/Extensions/Color++.swift index 493769c..baddd22 100644 --- a/Memola/Utilies/Extensions/Color++.swift +++ b/Memola/Utilies/Extensions/Color++.swift @@ -29,7 +29,6 @@ extension Color { extension Color { var hsba: (hue: Double, saturation: Double, brightness: Double, alpha: Double) { #if os(macOS) - #warning("TODO: need double check") let nsColor = NSColor(self) var hue: CGFloat = 0 var saturation: CGFloat = 0 @@ -52,7 +51,6 @@ extension Color { extension Platform.Color { var components: [CGFloat] { #if os(macOS) - #warning("TODO: need double check") let nsColor: NSColor = self let ciColor: CIColor = .init(color: nsColor) ?? CIColor(red: 0, green: 0, blue: 0) return [ciColor.red, ciColor.green, ciColor.blue, ciColor.alpha]