feat: add find keyboard shortcut

This commit is contained in:
dscyrescotti
2024-07-14 16:03:05 +07:00
parent 00d9a8e5f9
commit ca93472cd4
12 changed files with 123 additions and 17 deletions

View File

@@ -25,7 +25,6 @@
EC2002D52C416033002EBD5F /* FileCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2002D42C416033002EBD5F /* FileCommands.swift */; };
EC2002D72C4160EF002EBD5F /* EditCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2002D62C4160EF002EBD5F /* EditCommands.swift */; };
EC2002D92C4161ED002EBD5F /* ViewCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2002D82C4161ED002EBD5F /* ViewCommands.swift */; };
EC2002DB2C4162E5002EBD5F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2002DA2C4162E5002EBD5F /* AppDelegate.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 */; };
@@ -45,6 +44,9 @@
EC50500D2BF6674400B4D86E /* OnDragViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC50500C2BF6674400B4D86E /* OnDragViewModifier.swift */; };
EC5D40812C21CE270067F090 /* PhotoBackgroundRenderPass.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC5D40802C21CE270067F090 /* PhotoBackgroundRenderPass.swift */; };
EC5E83902BFDB69C00261D9C /* MovingAverage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC5E838F2BFDB69C00261D9C /* MovingAverage.swift */; };
EC6E3BD72C43C6A400DD20F3 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6E3BD62C43C6A400DD20F3 /* Application.swift */; };
EC6E3BD92C43C6C000DD20F3 /* OnDismissSearchViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6E3BD82C43C6C000DD20F3 /* OnDismissSearchViewModifier.swift */; };
EC6E3BDB2C43C78700DD20F3 /* NavigationSplitViewVisibility++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6E3BDA2C43C78700DD20F3 /* NavigationSplitViewVisibility++.swift */; };
EC7F6BEC2BE5E6E300A34A7B /* MemolaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7F6BEB2BE5E6E300A34A7B /* MemolaApp.swift */; };
EC7F6BF02BE5E6E400A34A7B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC7F6BEF2BE5E6E400A34A7B /* Assets.xcassets */; };
EC7F6BF32BE5E6E400A34A7B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC7F6BF22BE5E6E400A34A7B /* Preview Assets.xcassets */; };
@@ -160,7 +162,6 @@
EC2002D42C416033002EBD5F /* FileCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileCommands.swift; sourceTree = "<group>"; };
EC2002D62C4160EF002EBD5F /* EditCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCommands.swift; sourceTree = "<group>"; };
EC2002D82C4161ED002EBD5F /* ViewCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewCommands.swift; sourceTree = "<group>"; };
EC2002DA2C4162E5002EBD5F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
EC2002DC2C4163E8002EBD5F /* AppCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCommands.swift; sourceTree = "<group>"; };
EC2002E02C416470002EBD5F /* Shortcut.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shortcut.swift; sourceTree = "<group>"; };
EC2002E42C416551002EBD5F /* Shortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shortcuts.swift; sourceTree = "<group>"; };
@@ -181,6 +182,9 @@
EC50500E2BF670EA00B4D86E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
EC5D40802C21CE270067F090 /* PhotoBackgroundRenderPass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoBackgroundRenderPass.swift; sourceTree = "<group>"; };
EC5E838F2BFDB69C00261D9C /* MovingAverage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovingAverage.swift; sourceTree = "<group>"; };
EC6E3BD62C43C6A400DD20F3 /* Application.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
EC6E3BD82C43C6C000DD20F3 /* OnDismissSearchViewModifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnDismissSearchViewModifier.swift; sourceTree = "<group>"; };
EC6E3BDA2C43C78700DD20F3 /* NavigationSplitViewVisibility++.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NavigationSplitViewVisibility++.swift"; sourceTree = "<group>"; };
EC7F6BE82BE5E6E300A34A7B /* Memola.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Memola.app; sourceTree = BUILT_PRODUCTS_DIR; };
EC7F6BEB2BE5E6E300A34A7B /* MemolaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemolaApp.swift; sourceTree = "<group>"; };
EC7F6BEF2BE5E6E400A34A7B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -492,6 +496,7 @@
EC50500B2BF6673300B4D86E /* ViewModifiers */ = {
isa = PBXGroup;
children = (
EC6E3BD82C43C6C000DD20F3 /* OnDismissSearchViewModifier.swift */,
EC50500C2BF6674400B4D86E /* OnDragViewModifier.swift */,
EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.swift */,
);
@@ -585,7 +590,7 @@
isa = PBXGroup;
children = (
EC7F6BEB2BE5E6E300A34A7B /* MemolaApp.swift */,
EC2002DA2C4162E5002EBD5F /* AppDelegate.swift */,
EC6E3BD62C43C6A400DD20F3 /* Application.swift */,
);
path = App;
sourceTree = "<group>";
@@ -950,6 +955,7 @@
ECF7B2CF2C39169C004D2C57 /* Extensions */ = {
isa = PBXGroup;
children = (
EC6E3BDA2C43C78700DD20F3 /* NavigationSplitViewVisibility++.swift */,
ECF7B2BD2C39169C004D2C57 /* Array++.swift */,
ECF7B2BE2C39169C004D2C57 /* CGAffineTransform++.swift */,
ECF7B2BF2C39169C004D2C57 /* CGFloat++.swift */,
@@ -1120,6 +1126,7 @@
EC2002E52C416551002EBD5F /* Shortcuts.swift in Sources */,
EC2002DD2C4163E8002EBD5F /* AppCommands.swift in Sources */,
EC1815082C2D980B00541369 /* Sort.swift in Sources */,
EC6E3BD92C43C6C000DD20F3 /* OnDismissSearchViewModifier.swift in Sources */,
ECA7387D2BE5EF4B00A4542E /* MemoView.swift in Sources */,
ECDDD40D2C366B3B00DF9D5E /* PreviewRenderPass.swift in Sources */,
ECF7B2E12C39169C004D2C57 /* View++.swift in Sources */,
@@ -1129,6 +1136,7 @@
ECFA15202BEF21EF00455818 /* MemoObject.swift in Sources */,
ECE883C12C00C9CB0045C53D /* StrokeStyle.swift in Sources */,
EC37FB122C1B2DD90008D976 /* ToolSelection.swift in Sources */,
EC6E3BDB2C43C78700DD20F3 /* NavigationSplitViewVisibility++.swift in Sources */,
ECA738C62BE60E9D00A4542E /* EraserPenStyle.swift in Sources */,
ECA738A62BE6023F00A4542E /* GridUniforms.swift in Sources */,
ECA738D72BE60FC100A4542E /* SolidPointStrokeGenerator.swift in Sources */,
@@ -1139,6 +1147,7 @@
ECA738862BE5FF2500A4542E /* Canvas.swift in Sources */,
ECF7B2DE2C39169C004D2C57 /* NSManagedObjectContext++.swift in Sources */,
ECA738882BE5FF4400A4542E /* Renderer.swift in Sources */,
EC6E3BD72C43C6A400DD20F3 /* Application.swift in Sources */,
EC8C9DCE2C39882500A8F3C4 /* NSSyncScrollView.swift in Sources */,
ECF7B2D72C39169C004D2C57 /* Color++.swift in Sources */,
EC01512C2C306BEF008A115E /* MemoCard.swift in Sources */,
@@ -1179,7 +1188,6 @@
ECDAC07B2C318DBC0000ED77 /* ElementToolbar.swift in Sources */,
EC50500D2BF6674400B4D86E /* OnDragViewModifier.swift in Sources */,
EC9AB09F2C1401A40076AF58 /* EraserObject.swift in Sources */,
EC2002DB2C4162E5002EBD5F /* AppDelegate.swift in Sources */,
ECBE529C2C1D94A4006BDB3D /* CameraView.swift in Sources */,
ECA7389E2BE601CB00A4542E /* QuadVertex.swift in Sources */,
ECA738B32BE60D9E00A4542E /* CanvasView.swift in Sources */,

View File

@@ -1,19 +1,26 @@
//
// AppDelegate.swift
// Application.swift
// Memola
//
// Created by Dscyre Scotti on 7/12/24.
//
import Combine
import SwiftUI
class AppDelegate: NSObject, ObservableObject { }
class Application: NSObject, ObservableObject {
}
#if os(macOS)
extension AppDelegate: NSApplicationDelegate {
extension Application: NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
NSWindow.allowsAutomaticWindowTabbing = false
UserDefaults.standard.register(defaults: ["NSQuitAlwaysKeepsWindows": false])
}
}
#else
extension Application: UIApplicationDelegate {
func applicationDidFinishLaunching(_ application: UIApplication) { }
}
#endif

View File

@@ -10,9 +10,11 @@ import SwiftUI
@main
struct MemolaApp: App {
#if os(macOS)
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@NSApplicationDelegateAdaptor(Application.self) var application
#else
@UIApplicationDelegateAdaptor(Application.self) var application
#endif
var body: some Scene {
WindowGroup {
DashboardView()

View File

@@ -8,7 +8,7 @@
import SwiftUI
import Foundation
struct ContextMenuViewModifier<MenuContent: View, Preview: View>: ViewModifier {
private struct ContextMenuViewModifier<MenuContent: View, Preview: View>: ViewModifier {
let condition: Bool
let menuItems: () -> MenuContent
let preview: () -> Preview

View File

@@ -0,0 +1,29 @@
//
// OnDismissSearchViewModifier.swift
// Memola
//
// Created by Dscyre Scotti on 7/14/24.
//
import SwiftUI
private struct OnDismissSearchViewModifier: ViewModifier {
@Environment(\.dismissSearch) var dismissSearch
@Binding var isActive: Bool
func body(content: Content) -> some View {
content
.onChange(of: isActive) { oldValue, newValue in
if !newValue {
dismissSearch()
}
}
}
}
extension View {
func onDismissSearch(isActive: Binding<Bool>) -> some View {
modifier(OnDismissSearchViewModifier(isActive: isActive))
}
}

View File

@@ -8,7 +8,7 @@
import SwiftUI
import Foundation
struct OnDragViewModifier<Preview: View>: ViewModifier {
private struct OnDragViewModifier<Preview: View>: ViewModifier {
let condition: Bool
let data: () -> NSItemProvider
let preview: () -> Preview

View File

@@ -13,12 +13,13 @@ struct DashboardView: View {
@StateObject var memoManager: MemoManager = .shared
@State var sidebarItem: SidebarItem? = .memos
@AppStorage("memola.app.scene.side-bar.column-visibility") var columnVisibility: NavigationSplitViewVisibility = .all
@Namespace var namespace
var body: some View {
#if os(macOS)
NavigationSplitView {
NavigationSplitView(columnVisibility: $columnVisibility) {
Sidebar(sidebarItem: $sidebarItem, horizontalSizeClass: horizontalSizeClass)
} detail: {
switch sidebarItem {
@@ -45,7 +46,7 @@ struct DashboardView: View {
}
}
#else
NavigationSplitView {
NavigationSplitView(columnVisibility: $columnVisibility) {
Sidebar(sidebarItem: $sidebarItem, horizontalSizeClass: horizontalSizeClass)
} detail: {
switch sidebarItem {

View File

@@ -15,6 +15,7 @@ struct MemosView: View {
@State var query: String = ""
@State var currentDate: Date = .now
@State var isActiveSearch: Bool = false
@AppStorage("memola.memo-objects.memos.sort") var sort: Sort = .recent
@AppStorage("memola.memo-objects.memos.filter") var filter: Filter = .none
@@ -43,12 +44,16 @@ struct MemosView: View {
MemoGrid(memoObjects: memoObjects, placeholder: placeholder) { memoObject, cellWidth in
memoCard(memoObject, cellWidth)
}
.onDismissSearch(isActive: $isActiveSearch)
.focusedSceneValue(\.activeSceneKey, .memos)
.navigationTitle(horizontalSizeClass == .compact ? "Memos" : "")
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.searchable(text: $query, placement: .toolbar, prompt: Text("Search"))
.searchable(text: $query, isPresented: $isActiveSearch, placement: .toolbar, prompt: Text("Search"))
.onSubmit(of: .search) {
isActiveSearch = false
}
.toolbar {
#if os(macOS)
ToolbarItem(placement: .navigation) {

View File

@@ -16,6 +16,7 @@ struct TrashView: View {
@State var query: String = ""
@State var restoredMemo: MemoObject?
@State var deletedMemo: MemoObject?
@State var isActiveSearch: Bool = false
@Binding var sidebarItem: SidebarItem?
@@ -44,12 +45,16 @@ struct TrashView: View {
MemoGrid(memoObjects: memoObjects, placeholder: placeholder) { memoObject, cellWidth in
memoCard(memoObject, cellWidth)
}
.onDismissSearch(isActive: $isActiveSearch)
.focusedSceneValue(\.activeSceneKey, .trash)
.navigationTitle(horizontalSizeClass == .compact ? "Trash" : "")
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.searchable(text: $query, placement: .toolbar, prompt: Text("Search"))
.searchable(text: $query, isPresented: $isActiveSearch, placement: .toolbar, prompt: Text("Search"))
.onSubmit(of: .search) {
isActiveSearch = false
}
.toolbar {
#if os(macOS)
ToolbarItem(placement: .navigation) {

View File

@@ -8,7 +8,23 @@
import SwiftUI
struct ViewCommands: Commands {
@FocusedValue(\.activeSceneKey) var appScene
var body: some Commands {
CommandGroup(replacing: .toolbar) { }
CommandGroup(replacing: .toolbar) {
if appScene == .trash || appScene == .memos {
Button {
#if os(macOS)
guard let toolbar = NSApp.keyWindow?.toolbar else { return }
if let search = toolbar.items.first(where: { $0.itemIdentifier.rawValue == "com.apple.SwiftUI.search" }) as? NSSearchToolbarItem {
search.beginSearchInteraction()
}
#endif
} label: {
Text("Find Memo")
}
.keyboardShortcut("f", modifiers: [.command])
}
}
}
}

View File

@@ -8,7 +8,7 @@
import Combine
import Foundation
class Shortcut: ObservableObject {
class Shortcut {
static let shared: Shortcut = .init()
private let shortcutPublisher = PassthroughSubject<Shortcuts, Never>()

View File

@@ -0,0 +1,33 @@
//
// NavigationSplitViewVisibility++.swift
// Memola
//
// Created by Dscyre Scotti on 7/14/24.
//
import SwiftUI
extension NavigationSplitViewVisibility: RawRepresentable {
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .all
case 1: self = .automatic
case 2: self = .detailOnly
case 3: self = .doubleColumn
default: self = .all
}
}
public var rawValue: Int {
switch self {
case .all: 0
case .automatic: 1
case .detailOnly: 2
case .doubleColumn: 3
default: -1
}
}
public typealias RawValue = Int
}