diff --git a/ToDoBar.xcodeproj/project.pbxproj b/ToDoBar.xcodeproj/project.pbxproj index 8a1a10f..14dac3a 100644 --- a/ToDoBar.xcodeproj/project.pbxproj +++ b/ToDoBar.xcodeproj/project.pbxproj @@ -15,6 +15,8 @@ 76B1F8ED28B70EA10027F7C8 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76B1F8EC28B70EA10027F7C8 /* ContentView.swift */; }; 76B1F8F028B70EDC0027F7C8 /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = 76B1F8EF28B70EDC0027F7C8 /* Defaults */; }; 76B1F8F328B70EE90027F7C8 /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = 76B1F8F228B70EE90027F7C8 /* HotKey */; }; + 76BD41E02A585DEB00859EA7 /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 76BD41DF2A585DEB00859EA7 /* LaunchAtLogin */; }; + 76BD41E32A58602400859EA7 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76BD41E22A58602400859EA7 /* AboutView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -26,6 +28,7 @@ 76B1F8E828B70E650027F7C8 /* ToDo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDo.swift; sourceTree = ""; }; 76B1F8EA28B70E850027F7C8 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 76B1F8EC28B70EA10027F7C8 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 76BD41E22A58602400859EA7 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -33,6 +36,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 76BD41E02A585DEB00859EA7 /* LaunchAtLogin in Frameworks */, 76B1F8F328B70EE90027F7C8 /* HotKey in Frameworks */, 76B1F8F028B70EDC0027F7C8 /* Defaults in Frameworks */, ); @@ -67,6 +71,7 @@ 76B1F8E828B70E650027F7C8 /* ToDo.swift */, 76B1F8EA28B70E850027F7C8 /* Extensions.swift */, 76B1F8EC28B70EA10027F7C8 /* ContentView.swift */, + 76BD41E22A58602400859EA7 /* AboutView.swift */, ); path = ToDoBar; sourceTree = ""; @@ -81,6 +86,7 @@ 76B1F8D228B70DE80027F7C8 /* Sources */, 76B1F8D328B70DE80027F7C8 /* Frameworks */, 76B1F8D428B70DE80027F7C8 /* Resources */, + 76BD41E12A585DF500859EA7 /* Copy "Launch at Login Helper" */, ); buildRules = ( ); @@ -90,6 +96,7 @@ packageProductDependencies = ( 76B1F8EF28B70EDC0027F7C8 /* Defaults */, 76B1F8F228B70EE90027F7C8 /* HotKey */, + 76BD41DF2A585DEB00859EA7 /* LaunchAtLogin */, ); productName = ToDoBar; productReference = 76B1F8D628B70DE80027F7C8 /* ToDoBar.app */; @@ -103,7 +110,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1310; - LastUpgradeCheck = 1310; + LastUpgradeCheck = 1410; TargetAttributes = { 76B1F8D528B70DE80027F7C8 = { CreatedOnToolsVersion = 13.1; @@ -122,6 +129,7 @@ packageReferences = ( 76B1F8EE28B70EDC0027F7C8 /* XCRemoteSwiftPackageReference "Defaults" */, 76B1F8F128B70EE90027F7C8 /* XCRemoteSwiftPackageReference "HotKey" */, + 76BD41DE2A585DEB00859EA7 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */, ); productRefGroup = 76B1F8D728B70DE80027F7C8 /* Products */; projectDirPath = ""; @@ -144,6 +152,28 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 76BD41E12A585DF500859EA7 /* Copy "Launch at Login Helper" */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Copy \"Launch at Login Helper\""; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n\"${BUILT_PRODUCTS_DIR}/LaunchAtLogin_LaunchAtLogin.bundle/Contents/Resources/copy-helper-swiftpm.sh\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 76B1F8D228B70DE80027F7C8 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -153,6 +183,7 @@ 76B1F8ED28B70EA10027F7C8 /* ContentView.swift in Sources */, 76B1F8DA28B70DE80027F7C8 /* AppDelegate.swift in Sources */, 76B1F8EB28B70E850027F7C8 /* Extensions.swift in Sources */, + 76BD41E32A58602400859EA7 /* AboutView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -204,6 +235,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -266,6 +298,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -293,9 +326,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = ToDoBar/ToDoBar.entitlements; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 2; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = UV3HUS49VJ; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; @@ -320,9 +355,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = ToDoBar/ToDoBar.entitlements; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 2; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = UV3HUS49VJ; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; @@ -381,6 +418,14 @@ kind = branch; }; }; + 76BD41DE2A585DEB00859EA7 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin"; + requirement = { + branch = main; + kind = branch; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -394,6 +439,11 @@ package = 76B1F8F128B70EE90027F7C8 /* XCRemoteSwiftPackageReference "HotKey" */; productName = HotKey; }; + 76BD41DF2A585DEB00859EA7 /* LaunchAtLogin */ = { + isa = XCSwiftPackageProductDependency; + package = 76BD41DE2A585DEB00859EA7 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */; + productName = LaunchAtLogin; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 76B1F8CE28B70DE80027F7C8 /* Project object */; diff --git a/ToDoBar.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ToDoBar.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 95b5a1b..d37a531 100644 --- a/ToDoBar.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ToDoBar.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,25 +1,32 @@ { - "object": { - "pins": [ - { - "package": "Defaults", - "repositoryURL": "https://github.com/sindresorhus/Defaults", - "state": { - "branch": "main", - "revision": "981ccb0a01c54abbe3c12ccb8226108527bbf115", - "version": null - } - }, - { - "package": "HotKey", - "repositoryURL": "https://github.com/soffes/HotKey", - "state": { - "branch": "master", - "revision": "c13662730cb5bc28de4a799854bbb018a90649bf", - "version": null - } + "pins" : [ + { + "identity" : "defaults", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sindresorhus/Defaults", + "state" : { + "branch" : "main", + "revision" : "981ccb0a01c54abbe3c12ccb8226108527bbf115" } - ] - }, - "version": 1 + }, + { + "identity" : "hotkey", + "kind" : "remoteSourceControl", + "location" : "https://github.com/soffes/HotKey", + "state" : { + "branch" : "master", + "revision" : "c13662730cb5bc28de4a799854bbb018a90649bf" + } + }, + { + "identity" : "launchatlogin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sindresorhus/LaunchAtLogin", + "state" : { + "branch" : "main", + "revision" : "7ad6331f9c38953eb1ce8737758e18f7607e984a" + } + } + ], + "version" : 2 } diff --git a/ToDoBar/AboutView.swift b/ToDoBar/AboutView.swift new file mode 100644 index 0000000..e3e07d3 --- /dev/null +++ b/ToDoBar/AboutView.swift @@ -0,0 +1,30 @@ +// +// AboutView.swift +// ToDoBar +// +// Created by Pavel Makhov on 2023-07-07. +// + +import SwiftUI + +struct AboutView: View { + let currentVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String + + var body: some View { + VStack { + Image(nsImage: NSImage(named: "AppIcon")!) + Text("ToDoBar").font(.title) + Text("by Pavel Makhov").font(.caption) + Text("version " + currentVersion).font(.footnote) + Divider() + Link("ToDoBar on GitHub", destination: URL(string: "https://github.com/menubar-apps/ToDoBar")!) + + }.padding() + } +} + +struct AboutView_Previews: PreviewProvider { + static var previews: some View { + AboutView() + } +} diff --git a/ToDoBar/AppDelegate.swift b/ToDoBar/AppDelegate.swift index 175e570..873d1a5 100644 --- a/ToDoBar/AppDelegate.swift +++ b/ToDoBar/AppDelegate.swift @@ -8,30 +8,30 @@ import Cocoa import SwiftUI import HotKey +import Defaults @main class AppDelegate: NSObject, NSApplicationDelegate { - + var popover: NSPopover! - var statusBarItem: NSStatusItem! + var statusBarItem: NSStatusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) let hotKey = HotKey(key: .x, modifiers: [.control, .shift]) // Global hotke + var aboutWindow: NSWindow! + @Default(.todos) var todos func applicationDidFinishLaunching(_ aNotification: Notification) { let contentView = ContentView() - + let popover = NSPopover() popover.contentSize = NSSize(width: 400, height: 400) popover.behavior = .transient popover.contentViewController = NSHostingController(rootView: contentView) self.popover = popover - self.statusBarItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.variableLength)) - - if let button = self.statusBarItem.button { - button.image = NSImage(systemSymbolName: "checklist", accessibilityDescription: nil) - button.action = #selector(togglePopover(_:)) - } + guard let statusButton = statusBarItem.button else { return } + statusButton.image = NSImage(systemSymbolName: "checklist", accessibilityDescription: nil) + statusButton.action = #selector(togglePopover(_:)) hotKey.keyUpHandler = { self.togglePopover(nil) } @@ -48,6 +48,35 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } + @objc + func openAboutWindow(_: NSStatusBarButton?) { + NSLog("Open about window") + let contentView = AboutView() + if aboutWindow != nil { + aboutWindow.close() + } + aboutWindow = NSWindow( + contentRect: NSRect(x: 0, y: 0, width: 240, height: 340), + styleMask: [.closable, .titled], + backing: .buffered, + defer: false + ) + + aboutWindow.title = "About" + aboutWindow.contentView = NSHostingView(rootView: contentView) + aboutWindow.makeKeyAndOrderFront(nil) + aboutWindow.styleMask.remove(.resizable) + + // allow the preference window can be focused automatically when opened + NSApplication.shared.activate(ignoringOtherApps: true) + + let controller = NSWindowController(window: aboutWindow) + controller.showWindow(self) + + aboutWindow.center() + aboutWindow.orderFrontRegardless() + } + @objc func quit() { NSLog("User click Quit") diff --git a/ToDoBar/ContentView.swift b/ToDoBar/ContentView.swift index 055dbdf..58b5e5c 100644 --- a/ToDoBar/ContentView.swift +++ b/ToDoBar/ContentView.swift @@ -9,13 +9,19 @@ import SwiftUI import SwiftUI import Defaults +import LaunchAtLogin struct ContentView: View { @Default(.todos) var todos - @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate - @State private var asd: String = "" + @NSApplicationDelegateAdaptor private var appDelegate: AppDelegate + + @State private var text: String = "" + @FocusState private var isTextFieldFocused: Bool + + @State private var newTodo: String = "" + @ObservedObject private var launchAtLogin = LaunchAtLogin.observable var body: some View { VStack { @@ -44,30 +50,35 @@ struct ContentView: View { .resizable() .frame(width: 18, height: 18) .padding(.top, 1) + .foregroundColor(.secondary) }.buttonStyle(PlainButtonStyle()) } - - } -// .listStyle(.sidebar) - + HStack { - TextField("Type a task and hit enter", text: $asd) + TextField("Type a task and hit enter", text: $newTodo) .padding(.vertical, 8) .padding(.horizontal, 8) .padding(.leading, 22) .cornerRadius(8) .textFieldStyle(PlainTextFieldStyle()) + .focused($isTextFieldFocused) + .focusable(true) { isFocused in + self.isTextFieldFocused = isFocused + } .overlay( Image(systemName: "plus.circle.fill") .frame(maxWidth: .infinity, alignment: .leading) .foregroundColor(.gray) .padding(.leading, 8) ).onSubmit { - self.todos.append(Todo(text: asd)) - asd = "" + self.todos.append(Todo(text: newTodo)) + newTodo = "" } + .onAppear { + isTextFieldFocused = true + } Spacer() Menu { @@ -82,6 +93,11 @@ struct ContentView: View { Label("Clear All", systemImage: "book") } Divider() + Toggle("Launch at login", isOn: $launchAtLogin.isEnabled) + Divider() + Button(action: { appDelegate.openAboutWindow(nil) } ) { + Label("About ToDoBar", systemImage: "books.vertical") + } Button(action: { appDelegate.quit()}) { Label("Quit", systemImage: "books.vertical") }