diff --git a/Pika.xcodeproj/project.pbxproj b/Pika.xcodeproj/project.pbxproj index dbbf38d..2e9f854 100644 --- a/Pika.xcodeproj/project.pbxproj +++ b/Pika.xcodeproj/project.pbxproj @@ -12,12 +12,10 @@ 226FD61025A940F90021A67F /* VisualEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226FD60F25A940F90021A67F /* VisualEffect.swift */; }; 22EF1D9B25B7AA18001102FA /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22EF1D9A25B7AA18001102FA /* Sequence.swift */; }; 22FE80B325BA0F820063759E /* KeyboardShortcutItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22FE80B225BA0F820063759E /* KeyboardShortcutItem.swift */; }; - EA057887259F54B500ACCD89 /* ColorMenuItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA057886259F54B500ACCD89 /* ColorMenuItems.swift */; }; EA0C525025AA729300AFF716 /* Visualisation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0C524F25AA729300AFF716 /* Visualisation.swift */; }; EA0C526025AB5A2B00AFF716 /* NavigationMenuItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0C525F25AB5A2B00AFF716 /* NavigationMenuItems.swift */; }; EA0C526425AB5D1700AFF716 /* PikaWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0C526325AB5D1700AFF716 /* PikaWindow.swift */; }; EA0C526F25AB683400AFF716 /* EyedropperButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0C526E25AB683400AFF716 /* EyedropperButton.swift */; }; - EA0C527325AB6C6000AFF716 /* ColorMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0C527225AB6C6000AFF716 /* ColorMenu.swift */; }; EA257BD125D8629300C3FC54 /* SwapButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA257BD025D8629300C3FC54 /* SwapButtonStyle.swift */; }; EA424C7D25CDEF98009056A9 /* ComplianceToggleGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA424C7C25CDEF98009056A9 /* ComplianceToggleGroup.swift */; }; EA424CDE25CF0328009056A9 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = EA424CE025CF0328009056A9 /* Localizable.strings */; }; @@ -52,6 +50,7 @@ EAD0B713259CFD2000FA2F67 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD0B712259CFD2000FA2F67 /* Defaults.swift */; }; EAD0B718259D146200FA2F67 /* EyedropperButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD0B717259D146200FA2F67 /* EyedropperButtonStyle.swift */; }; EAD0B71C259D151400FA2F67 /* NavigationMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD0B71B259D151400FA2F67 /* NavigationMenu.swift */; }; + EAEBF64725E878A5002999D1 /* CircleButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAEBF64625E878A5002999D1 /* CircleButtonStyle.swift */; }; EAF100C725C731AD006E1EC3 /* SplashTouchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF100C625C731AD006E1EC3 /* SplashTouchBar.swift */; }; EAF100CA25C75218006E1EC3 /* PikaTouchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF100C325C59EB3006E1EC3 /* PikaTouchBar.swift */; }; EAF100CD25C785C4006E1EC3 /* TouchBarVisual.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF100CC25C785C4006E1EC3 /* TouchBarVisual.swift */; }; @@ -63,12 +62,10 @@ 226FD60F25A940F90021A67F /* VisualEffect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualEffect.swift; sourceTree = ""; }; 22EF1D9A25B7AA18001102FA /* Sequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sequence.swift; sourceTree = ""; }; 22FE80B225BA0F820063759E /* KeyboardShortcutItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardShortcutItem.swift; sourceTree = ""; }; - EA057886259F54B500ACCD89 /* ColorMenuItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorMenuItems.swift; sourceTree = ""; }; EA0C524F25AA729300AFF716 /* Visualisation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Visualisation.swift; sourceTree = ""; }; EA0C525F25AB5A2B00AFF716 /* NavigationMenuItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationMenuItems.swift; sourceTree = ""; }; EA0C526325AB5D1700AFF716 /* PikaWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PikaWindow.swift; sourceTree = ""; }; EA0C526E25AB683400AFF716 /* EyedropperButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EyedropperButton.swift; sourceTree = ""; }; - EA0C527225AB6C6000AFF716 /* ColorMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorMenu.swift; sourceTree = ""; }; EA257BD025D8629300C3FC54 /* SwapButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwapButtonStyle.swift; sourceTree = ""; }; EA424C7C25CDEF98009056A9 /* ComplianceToggleGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComplianceToggleGroup.swift; sourceTree = ""; }; EA424CDF25CF0328009056A9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; @@ -104,6 +101,7 @@ EAD0B712259CFD2000FA2F67 /* Defaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Defaults.swift; sourceTree = ""; }; EAD0B717259D146200FA2F67 /* EyedropperButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EyedropperButtonStyle.swift; sourceTree = ""; }; EAD0B71B259D151400FA2F67 /* NavigationMenu.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = NavigationMenu.swift; sourceTree = ""; tabWidth = 4; }; + EAEBF64625E878A5002999D1 /* CircleButtonStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleButtonStyle.swift; sourceTree = ""; }; EAF100C325C59EB3006E1EC3 /* PikaTouchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PikaTouchBar.swift; sourceTree = ""; }; EAF100C625C731AD006E1EC3 /* SplashTouchBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashTouchBar.swift; sourceTree = ""; }; EAF100CC25C785C4006E1EC3 /* TouchBarVisual.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarVisual.swift; sourceTree = ""; }; @@ -233,8 +231,6 @@ children = ( EAD0B6F3259CF29300FA2F67 /* AboutView.swift */, EAD0B6F5259CF29300FA2F67 /* AppVersion.swift */, - EA0C527225AB6C6000AFF716 /* ColorMenu.swift */, - EA057886259F54B500ACCD89 /* ColorMenuItems.swift */, EA635DE025B4FC580014D91A /* ColorPickers.swift */, EA801284259F8F480026D5D9 /* ComplianceToggle.swift */, EA424C7C25CDEF98009056A9 /* ComplianceToggleGroup.swift */, @@ -263,6 +259,7 @@ children = ( EAD0B717259D146200FA2F67 /* EyedropperButtonStyle.swift */, EA257BD025D8629300C3FC54 /* SwapButtonStyle.swift */, + EAEBF64625E878A5002999D1 /* CircleButtonStyle.swift */, ); path = Styles; sourceTree = ""; @@ -390,13 +387,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - EA0C527325AB6C6000AFF716 /* ColorMenu.swift in Sources */, EA72BB8B25A537C3008205E7 /* MetalShader.swift in Sources */, 22EF1D9B25B7AA18001102FA /* Sequence.swift in Sources */, EAD0B6F9259CF29300FA2F67 /* PreferencesView.swift in Sources */, EA72BB8825A53750008205E7 /* SplashView.swift in Sources */, EAF100CA25C75218006E1EC3 /* PikaTouchBar.swift in Sources */, - EA057887259F54B500ACCD89 /* ColorMenuItems.swift in Sources */, EAF100C725C731AD006E1EC3 /* SplashTouchBar.swift in Sources */, EAA8AE1C25B8F03B0049299B /* KeyboardShortcutGrid.swift in Sources */, EA0C525025AA729300AFF716 /* Visualisation.swift in Sources */, @@ -422,6 +417,7 @@ EA72BBA925A7CE9C008205E7 /* NSWindowFade.swift in Sources */, EAD0B718259D146200FA2F67 /* EyedropperButtonStyle.swift in Sources */, EA635DC925B3B2650014D91A /* Cula.swift in Sources */, + EAEBF64725E878A5002999D1 /* CircleButtonStyle.swift in Sources */, EA635DE125B4FC580014D91A /* ColorPickers.swift in Sources */, 221600F925A62E5B00B8B7D9 /* IconImage.swift in Sources */, EAD0B6FA259CF29300FA2F67 /* AppVersion.swift in Sources */, @@ -579,7 +575,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 14; + CURRENT_PROJECT_VERSION = 17; DEVELOPMENT_ASSET_PATHS = "\"Pika/Preview Content\""; DEVELOPMENT_TEAM = TGHU37N6EX; ENABLE_HARDENED_RUNTIME = YES; @@ -590,7 +586,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 0.0.9; + MARKETING_VERSION = "0.0.10-beta3"; PRODUCT_BUNDLE_IDENTIFIER = com.superhighfives.Pika; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; @@ -606,7 +602,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 14; + CURRENT_PROJECT_VERSION = 17; DEVELOPMENT_ASSET_PATHS = "\"Pika/Preview Content\""; DEVELOPMENT_TEAM = TGHU37N6EX; ENABLE_HARDENED_RUNTIME = YES; @@ -617,7 +613,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 0.0.9; + MARKETING_VERSION = "0.0.10-beta3"; PRODUCT_BUNDLE_IDENTIFIER = com.superhighfives.Pika; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; diff --git a/Pika/AppDelegate.swift b/Pika/AppDelegate.swift index 06429ad..c9952cf 100644 --- a/Pika/AppDelegate.swift +++ b/Pika/AppDelegate.swift @@ -44,12 +44,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate { // Define content view let contentView = ContentView() .environmentObject(eyedroppers) - .frame(minWidth: 400, - idealWidth: 400, - maxWidth: 500, - minHeight: 180, - idealHeight: 200, - maxHeight: 350, + .frame(minWidth: 450, + idealWidth: 450, + maxWidth: 550, + minHeight: 200, + idealHeight: 220, + maxHeight: 360, alignment: .center) pikaWindow = PikaWindow.createPrimaryWindow() @@ -57,9 +57,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate { pikaTouchBarController = PikaTouchBarController(window: pikaWindow) // Define global keyboard shortcuts - KeyboardShortcuts.onKeyUp(for: .togglePika) { [self] in + KeyboardShortcuts.onKeyUp(for: .togglePika) { [] in if Defaults[.viewedSplash] { - togglePopover(nil) + NSApp.sendAction(#selector(AppDelegate.triggerPickForeground), to: nil, from: nil) } } @@ -114,11 +114,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate { action: #selector(checkForUpdates(_:)), keyEquivalent: "" ) - statusBarMenu.addItem( - withTitle: NSLocalizedString("menu.preferences", comment: "Preferences"), + + let preferences = NSMenuItem( + title: NSLocalizedString("menu.preferences", comment: "Preferences"), action: #selector(openPreferencesWindow(_:)), - keyEquivalent: "" + keyEquivalent: "," ) + preferences.keyEquivalentModifierMask = NSEvent.ModifierFlags.command + statusBarMenu.addItem(preferences) + statusBarMenu.addItem(NSMenuItem.separator()) statusBarMenu.addItem( withTitle: NSLocalizedString("menu.quit", comment: "Quit Pika"), @@ -156,7 +160,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate { if aboutWindow == nil { aboutWindow = PikaWindow.createSecondaryWindow( title: "About", - size: NSRect(x: 0, y: 0, width: 300, height: 540), + size: NSRect(x: 0, y: 0, width: 300, height: 610), styleMask: [.titled, .closable, .miniaturizable, .fullSizeContentView] ) aboutTouchBarController = SplashTouchBarController(window: aboutWindow) @@ -169,12 +173,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate { if preferencesWindow == nil { preferencesWindow = PikaWindow.createSecondaryWindow( title: "Preferences", - size: NSRect(x: 0, y: 0, width: 550, height: 480), + size: NSRect(x: 0, y: 0, width: 550, height: 420), styleMask: [.titled, .closable, .miniaturizable, .fullSizeContentView] ) preferencesWindow.contentView = NSHostingView(rootView: PreferencesView()) } preferencesWindow.makeKeyAndOrderFront(nil) + notificationCenter.post(name: Notification.Name(PikaConstants.ncTriggerPreferences), object: self) } @IBAction func openSplashWindow(_: Any?) { @@ -210,12 +215,21 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate { notificationCenter.post(name: Notification.Name(PikaConstants.ncTriggerCopyBackground), object: self) } + @IBAction func triggerSwap(_: Any) { + notificationCenter.post(name: Notification.Name(PikaConstants.ncTriggerSwap), object: self) + } + @IBAction func hidePika(_: Any) { hideMainWindow() } @IBAction func showPika(_: Any) { - pikaWindow.fadeIn(sender: nil, duration: 0.2) + if pikaWindow.isVisible { + pikaWindow.makeKeyAndOrderFront(self) + } else { + pikaWindow.fadeIn(sender: nil, duration: 0.2) + } + NSApp.activate(ignoringOtherApps: true) } @IBAction func checkForUpdates(_: Any) { diff --git a/Pika/Assets/Base.lproj/Main.storyboard b/Pika/Assets/Base.lproj/Main.storyboard index 192755a..df030dc 100644 --- a/Pika/Assets/Base.lproj/Main.storyboard +++ b/Pika/Assets/Base.lproj/Main.storyboard @@ -622,6 +622,12 @@ + + + + + + diff --git a/Pika/Assets/en.lproj/Localizable.strings b/Pika/Assets/en.lproj/Localizable.strings index 3885089..6d02fbc 100644 Binary files a/Pika/Assets/en.lproj/Localizable.strings and b/Pika/Assets/en.lproj/Localizable.strings differ diff --git a/Pika/Constants/Constants.swift b/Pika/Constants/Constants.swift index bca79c3..d6f48c8 100644 --- a/Pika/Constants/Constants.swift +++ b/Pika/Constants/Constants.swift @@ -35,4 +35,6 @@ enum PikaConstants { static let ncTriggerCopyBackground = "triggerCopyBackground" static let ncTriggerPickForeground = "triggerPickForeground" static let ncTriggerPickBackground = "triggerPickBackground" + static let ncTriggerSwap = "triggerSwap" + static let ncTriggerPreferences = "triggerPreferences" } diff --git a/Pika/Styles/CircleButtonStyle.swift b/Pika/Styles/CircleButtonStyle.swift new file mode 100644 index 0000000..3f4455d --- /dev/null +++ b/Pika/Styles/CircleButtonStyle.swift @@ -0,0 +1,57 @@ +import Combine +import SwiftUI + +struct CircleButtonStyle: ButtonStyle { + @State private var isHovered: Bool = false + let isVisible: Bool + + private struct CircleButtonStyleView: View { + @Environment(\.colorScheme) var colorScheme: ColorScheme + + let configuration: Configuration + let isVisible: Bool + + func getBackgroundColor(colorScheme: ColorScheme) -> Color { + if #available(OSX 11.0, *) { + return colorScheme == .dark + ? Color(red: 27 / 255, green: 27 / 255, blue: 27 / 255) + : Color(red: 233 / 255, green: 233 / 255, blue: 233 / 255) + } else { + return colorScheme == .dark + ? Color(red: 50 / 255, green: 52 / 255, blue: 59 / 255) + : Color(red: 236 / 255, green: 236 / 255, blue: 236 / 255) + } + } + + var body: some View { + let fgColor = colorScheme == .dark ? Color.white : .black + let bgColor: Color = getBackgroundColor(colorScheme: colorScheme) + + configuration.label + .padding(.all, 8) + .background( + ZStack { + Circle() + .fill(bgColor) + .shadow( + color: Color.black.opacity(0.2), + radius: configuration.isPressed ? 1 : 2, + x: 0, + y: configuration.isPressed ? 1 : 2 + ) + .overlay( + Circle() + .stroke(fgColor.opacity(0.1)) + ) + } + ) + .opacity(isVisible ? (configuration.isPressed ? 0.8 : 1.0) : 0.0) + .foregroundColor(fgColor.opacity(0.8)) + .animation(.easeInOut) + } + } + + func makeBody(configuration: Self.Configuration) -> some View { + CircleButtonStyleView(configuration: configuration, isVisible: isVisible) + } +} diff --git a/Pika/Styles/SwapButtonStyle.swift b/Pika/Styles/SwapButtonStyle.swift index c1462af..b3c7836 100644 --- a/Pika/Styles/SwapButtonStyle.swift +++ b/Pika/Styles/SwapButtonStyle.swift @@ -4,13 +4,18 @@ import SwiftUI struct SwapButtonStyle: ButtonStyle { @State private var isHovered: Bool = false let isVisible: Bool + let alt: String private struct SwapButtonStyleView: View { @Environment(\.colorScheme) var colorScheme: ColorScheme @State private var isHovered: Bool = false + @State private var timerSubscription: Cancellable? + @State private var timer = Timer.publish(every: 0.25, on: .main, in: .common) + let configuration: Configuration let isVisible: Bool + let alt: String func getBackgroundColor(colorScheme: ColorScheme) -> Color { if #available(OSX 11.0, *) { @@ -31,7 +36,7 @@ struct SwapButtonStyle: ButtonStyle { HStack { configuration.label if isHovered { - Text(NSLocalizedString("color.pick.swap", comment: "Swap")) + Text(alt) .font(.system(size: 12.0)) } } @@ -55,7 +60,21 @@ struct SwapButtonStyle: ButtonStyle { } ) .onHover { hover in - self.isHovered = hover + if hover { + if self.timerSubscription == nil { + self.timer = Timer.publish(every: 1, on: .main, in: .common) + self.timerSubscription = self.timer.connect() + } + } else { + timerSubscription?.cancel() + timerSubscription = nil + self.isHovered = false + } + } + .onReceive(timer) { _ in + self.isHovered = true + timerSubscription?.cancel() + timerSubscription = nil } .opacity(isVisible ? (configuration.isPressed ? 0.8 : 1.0) : 0.0) .foregroundColor(fgColor.opacity(0.8)) @@ -64,6 +83,6 @@ struct SwapButtonStyle: ButtonStyle { } func makeBody(configuration: Self.Configuration) -> some View { - SwapButtonStyleView(configuration: configuration, isVisible: isVisible) + SwapButtonStyleView(configuration: configuration, isVisible: isVisible, alt: alt) } } diff --git a/Pika/Utilities/Eyedroppers.swift b/Pika/Utilities/Eyedroppers.swift index 399c77b..a6b2be2 100644 --- a/Pika/Utilities/Eyedroppers.swift +++ b/Pika/Utilities/Eyedroppers.swift @@ -15,6 +15,7 @@ class Eyedropper: ObservableObject { } let type: Types + var forceShow = false @objc @Published public var color: NSColor @@ -29,18 +30,22 @@ class Eyedropper: ObservableObject { func start() { if Defaults[.hidePikaWhilePicking] { + if NSApp.mainWindow?.isVisible == true { + forceShow = true + } NSApp.sendAction(#selector(AppDelegate.hidePika), to: nil, from: nil) } DispatchQueue.main.asyncAfter(deadline: .now()) { let sampler = NSColorSampler() sampler.show { selectedColor in - if Defaults[.hidePikaWhilePicking] { - NSApp.sendAction(#selector(AppDelegate.showPika), to: nil, from: nil) - } if let selectedColor = selectedColor { self.color = selectedColor.usingColorSpace(Defaults[.colorSpace])! + NSApp.sendAction(#selector(AppDelegate.showPika), to: nil, from: nil) + } else if self.forceShow { + self.forceShow = false + NSApp.sendAction(#selector(AppDelegate.showPika), to: nil, from: nil) } } } diff --git a/Pika/Views/AboutView.swift b/Pika/Views/AboutView.swift index 619bb49..2422f4d 100644 --- a/Pika/Views/AboutView.swift +++ b/Pika/Views/AboutView.swift @@ -43,7 +43,7 @@ struct AboutView: View { VStack(spacing: 0) { KeyboardShortcutGrid() - .frame(height: 140.0) + .frame(height: 210.0) .background(colorScheme == .light ? Color.black.opacity(0.05) : Color.black.opacity(0.2)) } diff --git a/Pika/Views/ColorMenu.swift b/Pika/Views/ColorMenu.swift deleted file mode 100644 index 476c732..0000000 --- a/Pika/Views/ColorMenu.swift +++ /dev/null @@ -1,28 +0,0 @@ -import SwiftUI - -struct ColorMenu: View { - var eyedropper: Eyedropper - - var body: some View { - if #available(OSX 11.0, *) { - Menu { - ColorMenuItems(eyedropper: eyedropper) - } label: { - IconImage(name: "ellipsis.circle") - } - .menuStyle(BorderlessButtonMenuStyle(showsMenuIndicator: false)) - } else { - MenuButton(label: IconImage(name: "ellipsis.circle"), content: { - ColorMenuItems(eyedropper: eyedropper) - }) - .menuButtonStyle(BorderlessButtonMenuButtonStyle()) - } - } -} - -struct ColorMenu_Previews: PreviewProvider { - static var previews: some View { - let eyedropper = Eyedropper(type: .foreground, color: NSColor.white) - ColorMenu(eyedropper: eyedropper) - } -} diff --git a/Pika/Views/ColorMenuItems.swift b/Pika/Views/ColorMenuItems.swift deleted file mode 100644 index 3eb5e1f..0000000 --- a/Pika/Views/ColorMenuItems.swift +++ /dev/null @@ -1,41 +0,0 @@ -import Defaults -import SwiftUI - -struct ColorMenuItems: View { - var eyedropper: Eyedropper - @Default(.colorFormat) var colorFormat - let pasteboard = NSPasteboard.general - - var body: some View { - Button(action: { - pasteboard.clearContents() - pasteboard.setString(eyedropper.color.toFormat(format: colorFormat), forType: .string) - }, label: { Text(NSLocalizedString("color.copy.current", comment: "Copy current format")) }) - VStack { - Divider() - } - Button(action: { - pasteboard.clearContents() - pasteboard.setString(eyedropper.color.toHexString(), forType: .string) - }, label: { Text(NSLocalizedString("color.copy.hex", comment: "Copy HEX values")) }) - Button(action: { - pasteboard.clearContents() - pasteboard.setString(eyedropper.color.toRGB8BitString(), forType: .string) - }, label: { Text(NSLocalizedString("color.copy.rgb", comment: "Copy RGB values")) }) - Button(action: { - pasteboard.clearContents() - pasteboard.setString(eyedropper.color.toHSB8BitString(), forType: .string) - }, label: { Text(NSLocalizedString("color.copy.hsb", comment: "Copy HSB values")) }) - Button(action: { - pasteboard.clearContents() - pasteboard.setString(eyedropper.color.toHSL8BitString(), forType: .string) - }, label: { Text(NSLocalizedString("color.copy.hsl", comment: "Copy HSL values")) }) - } -} - -struct ColorMenuItems_Previews: PreviewProvider { - static var previews: some View { - let eyedropper = Eyedropper(type: .foreground, color: NSColor.white) - ColorMenuItems(eyedropper: eyedropper) - } -} diff --git a/Pika/Views/ContentView.swift b/Pika/Views/ContentView.swift index 91af984..0bf6b94 100644 --- a/Pika/Views/ContentView.swift +++ b/Pika/Views/ContentView.swift @@ -9,10 +9,8 @@ struct ContentView: View { @Environment(\.colorScheme) var colorScheme: ColorScheme @State var swapVisible: Bool = false - @State private var timerSubscription: Cancellable? @State private var timer = Timer.publish(every: 0.25, on: .main, in: .common) - @State private var countDown = 0 @State private var angle: Double = 0 var body: some View { @@ -38,14 +36,21 @@ struct ContentView: View { } .overlay( Button(action: { - swap(&eyedroppers.foreground.color, &eyedroppers.background.color) + NSApp.sendAction(#selector(AppDelegate.triggerSwap), to: nil, from: nil) angle -= 180 }, label: { IconImage(name: "arrow.triangle.swap") .rotationEffect(.degrees(angle)) .animation(.easeInOut) }) - .buttonStyle(SwapButtonStyle(isVisible: swapVisible)) + .buttonStyle(SwapButtonStyle( + isVisible: swapVisible, + alt: NSLocalizedString("color.swap", comment: "Swap") + )) + .onReceive(NotificationCenter.default.publisher( + for: Notification.Name(PikaConstants.ncTriggerSwap))) { _ in + swap(&eyedroppers.foreground.color, &eyedroppers.background.color) + } ) Divider() Footer(foreground: eyedroppers.foreground, background: eyedroppers.background) diff --git a/Pika/Views/EyedropperButton.swift b/Pika/Views/EyedropperButton.swift index 6ca9119..89fd253 100644 --- a/Pika/Views/EyedropperButton.swift +++ b/Pika/Views/EyedropperButton.swift @@ -1,46 +1,89 @@ +import Combine import Defaults import SwiftUI struct EyedropperButton: View { @ObservedObject var eyedropper: Eyedropper @Default(.colorFormat) var colorFormat + let pasteboard = NSPasteboard.general + + @State var copyVisible: Bool = false + @State private var timerSubscription: Cancellable? + @State private var timer = Timer.publish(every: 0.25, on: .main, in: .common) var body: some View { - Button(action: { eyedropper.start() }, label: { - ZStack { - Group { - Rectangle() - .fill(Color(eyedropper.color.overlay(with: NSColor.black))) - .frame(height: 55.0) + ZStack { + Button(action: { + let action = eyedropper.type == .foreground + ? #selector(AppDelegate.triggerPickForeground) + : #selector(AppDelegate.triggerPickBackground) + NSApp.sendAction(action, to: nil, from: nil) + }, label: { + ZStack { + VStack(alignment: .leading, spacing: 0.0) { + Text(eyedropper.type == .foreground + ? NSLocalizedString("color.foreground", comment: "Foreground") + : NSLocalizedString("color.background", comment: "Background")) + .font(.caption) + .bold() + .foregroundColor(eyedropper.color.getUIColor().opacity(0.75)) + HStack { + Text(eyedropper.color.toFormat(format: colorFormat)) + .foregroundColor(eyedropper.color.getUIColor()) + .font(.system(size: 18, weight: .regular)) + } + } + .padding(.all, 10.0) + .modify { + if eyedropper.color.getUIColor() == .white { + $0 + .shadow(color: Color.black.opacity(0.40), radius: 0, x: 0, y: 1) + .shadow(color: Color.black.opacity(0.15), radius: 3, x: 0, y: 0) + } else { + $0 + .shadow(color: Color.white.opacity(0.40), radius: 0, x: 0, y: 1) + .shadow(color: Color.white.opacity(0.15), radius: 3, x: 0, y: 0) + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading) } - .opacity(0.2) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) + }) + .buttonStyle(EyedropperButtonStyle(color: Color(eyedropper.color))) - VStack(alignment: .leading, spacing: 0.0) { - Text(eyedropper.type == .foreground - ? NSLocalizedString("color.foreground", comment: "Foreground") - : NSLocalizedString("color.background", comment: "Background")) - .font(.caption) - .bold() - .foregroundColor(eyedropper.color.getUIColor().opacity(0.5)) - HStack { - Text(eyedropper.color.toFormat(format: colorFormat)) - .foregroundColor(eyedropper.color.getUIColor()) - .font(.system(size: 18, weight: .regular)) - IconImage(name: "eyedropper") - .foregroundColor(eyedropper.color.getUIColor()) - .padding(.leading, 0.0) - .opacity(0.8) - } + Button(action: { + let action = eyedropper.type == .foreground + ? #selector(AppDelegate.triggerCopyForeground) + : #selector(AppDelegate.triggerCopyBackground) + NSApp.sendAction(action, to: nil, from: nil) + }, label: { + IconImage(name: "doc.on.doc", resizable: true) + .aspectRatio(contentMode: .fit) + .frame(width: 14, height: 14) + }) + .buttonStyle(SwapButtonStyle( + isVisible: copyVisible, + alt: NSLocalizedString("color.copy", comment: "Copy") + )) + .padding(.all, 8.0) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomTrailing) + } + .onHover { hover in + if hover { + copyVisible = true + timerSubscription?.cancel() + timerSubscription = nil + } else { + if self.timerSubscription == nil { + self.timer = Timer.publish(every: 0.25, on: .main, in: .common) + self.timerSubscription = self.timer.connect() } - .padding([.bottom, .leading], 10.0) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading) - } - }) - .buttonStyle(EyedropperButtonStyle(color: Color(eyedropper.color))) - .contextMenu { - ColorMenuItems(eyedropper: eyedropper) } + } + .onReceive(timer) { _ in + copyVisible = false + timerSubscription?.cancel() + timerSubscription = nil + } } } diff --git a/Pika/Views/EyedropperItem.swift b/Pika/Views/EyedropperItem.swift index 6c41f0e..629a6ee 100644 --- a/Pika/Views/EyedropperItem.swift +++ b/Pika/Views/EyedropperItem.swift @@ -42,25 +42,6 @@ struct EyedropperItem: View { : NSLocalizedString("color.background", comment: "Background").lowercased() )) ) - - ColorMenu(eyedropper: eyedropper) - .shadow( - color: (colorScheme == .light ? Color.white : Color.black).opacity(0.5), - radius: 0, - x: 0, - y: 1 - ) - .opacity(0.8) - .fixedSize() - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing) - .padding(8.0) - .modify { - if colorScheme == .dark { - $0.colorMultiply(Color.white) - } else { - $0 - } - } } } } diff --git a/Pika/Views/IconImage.swift b/Pika/Views/IconImage.swift index e423af5..db9df11 100644 --- a/Pika/Views/IconImage.swift +++ b/Pika/Views/IconImage.swift @@ -2,12 +2,24 @@ import SwiftUI struct IconImage: View { var name: String + var resizable: Bool = false var body: some View { + let image: Image if #available(OSX 11.0, *) { - return Image(systemName: name) + image = Image(systemName: name) } else { - return Image(name) + image = Image(name) } + + return image + .modify { (image: Image) -> Image in + if resizable { + return image + .resizable() + } else { + return image + } + } } } diff --git a/Pika/Views/KeyboardShortcutGrid.swift b/Pika/Views/KeyboardShortcutGrid.swift index 3456f6b..cc15ef0 100644 --- a/Pika/Views/KeyboardShortcutGrid.swift +++ b/Pika/Views/KeyboardShortcutGrid.swift @@ -8,6 +8,10 @@ struct KeyboardShortcutGrid: View { GeometryReader { geometry in let width = geometry.size.width let height = geometry.size.height + + let horizontalUnit = width / 2 + let verticalUnit = floor(height / 3) + VStack(spacing: 0.0) { HStack(spacing: 0.0) { KeyboardShortcutItem( @@ -15,17 +19,17 @@ struct KeyboardShortcutGrid: View { event: PikaConstants.ncTriggerPickForeground, keys: ["⌘", "D"] ) - .frame(width: width / 2, height: height / 2) + .frame(width: horizontalUnit, height: verticalUnit) Divider() - .frame(height: height / 2) + .frame(height: verticalUnit) KeyboardShortcutItem( title: NSLocalizedString("color.pick.background", comment: "Pick background"), event: PikaConstants.ncTriggerPickBackground, keys: ["⇧", "⌘", "D"] ) - .frame(width: width / 2, height: height / 2) + .frame(width: horizontalUnit, height: verticalUnit) } Divider() @@ -37,23 +41,46 @@ struct KeyboardShortcutGrid: View { event: PikaConstants.ncTriggerCopyForeground, keys: ["⌘", "C"] ) - .frame(width: width / 2, height: height / 2) + .frame(width: horizontalUnit, height: verticalUnit) Divider() - .frame(height: height / 2) + .frame(height: verticalUnit) KeyboardShortcutItem( title: NSLocalizedString("color.copy.background", comment: "Copy background"), event: PikaConstants.ncTriggerCopyBackground, keys: ["⇧", "⌘", "C"] ) - .frame(width: width / 2, height: height / 2) + .frame(width: horizontalUnit, height: verticalUnit) + } + + Divider() + .frame(maxWidth: .infinity) + + HStack(spacing: 0) { + KeyboardShortcutItem( + title: NSLocalizedString("menu.preferences", comment: "Preferences"), + event: PikaConstants.ncTriggerPreferences, + keys: ["⌘", ","] + ) + .frame(width: horizontalUnit, height: verticalUnit) + + Divider() + .frame(height: verticalUnit) + + KeyboardShortcutItem( + title: NSLocalizedString("color.swap.detail", comment: "Swap colors"), + event: PikaConstants.ncTriggerSwap, + keys: ["⇧", "⌘", "X"] + ) + .frame(width: horizontalUnit, height: verticalUnit) } } .frame(maxWidth: .infinity, maxHeight: .infinity) } Divider() + .offset(y: 1) } } } diff --git a/Pika/Views/NavigationMenu.swift b/Pika/Views/NavigationMenu.swift index f716ad7..9343308 100644 --- a/Pika/Views/NavigationMenu.swift +++ b/Pika/Views/NavigationMenu.swift @@ -1,8 +1,11 @@ +import Defaults import SwiftUI struct NavigationMenu: View { + @Default(.colorFormat) var colorFormat + // These are a hack to trigger a redraw on OSX 11.0 - otherwise the - // button displays wtih 50% opacity until you interract with it. If + // button displays wtih 50% opacity until you interact with it. If // anyone knows of a better way to do this, let me know. @State var once: Bool = false @State var showMenu: Bool = true @@ -21,10 +24,10 @@ struct NavigationMenu: View { .menuStyle(BorderlessButtonMenuStyle(showsMenuIndicator: true)) .onAppear { if !once { + self.showMenu.toggle() DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.showMenu.toggle() } - self.showMenu.toggle() } once = true } @@ -34,17 +37,40 @@ struct NavigationMenu: View { NavigationMenuItems() }) .menuButtonStyle(BorderlessPullDownMenuButtonStyle()) - .padding(EdgeInsets(top: 40, leading: 0, bottom: 0, trailing: 0)) + .padding(EdgeInsets(top: 40, leading: 5, bottom: 0, trailing: 20)) .edgesIgnoringSafeArea(.all) .fixedSize() } } var body: some View { - HStack { + HStack(spacing: 0) { + Picker(NSLocalizedString("preferences.format.title", comment: "Color Format"), selection: $colorFormat) { + ForEach(ColorFormat.allCases, id: \.self) { value in + Text(value.rawValue) + } + } + .modify { + if #available(OSX 11.0, *) { + $0.offset(y: 1.0) + } else { + $0.offset(x: 6.0, y: -18.0) + } + } + .pickerStyle(SegmentedPickerStyle()) + .labelsHidden() + getMenu() .frame(alignment: .leading) - .padding(.horizontal, 16.0) + .modify { + if #available(OSX 11.0, *) { + $0 + .padding(.trailing, 10.0) + .padding(.leading, 5.0) + } else { + $0 + } + } .fixedSize() } } diff --git a/Pika/Views/NavigationMenuItems.swift b/Pika/Views/NavigationMenuItems.swift index 147a0bc..d74692c 100644 --- a/Pika/Views/NavigationMenuItems.swift +++ b/Pika/Views/NavigationMenuItems.swift @@ -36,13 +36,11 @@ struct NavigationMenuItems: View { $0 } } - } - VStack { - Divider() - } + VStack { + Divider() + } - Group { Button(NSLocalizedString("color.copy.foreground", comment: "Copy foreground"), action: { NSApp.sendAction(#selector(AppDelegate.triggerCopyForeground), to: nil, from: nil) }) @@ -64,6 +62,21 @@ struct NavigationMenuItems: View { $0 } } + + VStack { + Divider() + } + + Button(NSLocalizedString("color.swap.detail", comment: "Swap Colors"), action: { + NSApp.sendAction(#selector(AppDelegate.triggerSwap), to: nil, from: nil) + }) + .modify { + if #available(OSX 11.0, *) { + $0.keyboardShortcut("X", modifiers: .command) + } else { + $0 + } + } } VStack { diff --git a/Pika/Views/PreferencesView.swift b/Pika/Views/PreferencesView.swift index ef7c251..a91ddaf 100644 --- a/Pika/Views/PreferencesView.swift +++ b/Pika/Views/PreferencesView.swift @@ -4,7 +4,6 @@ import LaunchAtLogin import SwiftUI struct PreferencesView: View { - @Default(.colorFormat) var colorFormat @Default(.hideMenuBarIcon) var hideMenuBarIcon @Default(.betaUpdates) var betaUpdates @State var colorSpace: NSColorSpace = Defaults[.colorSpace] @@ -42,11 +41,6 @@ struct PreferencesView: View { VStack(alignment: .leading, spacing: 10.0) { // Color Format let textFormatTitle = NSLocalizedString("preferences.format.title", comment: "Color Format") - let textFormatDescription = NSLocalizedString( - "preferences.format.description", - comment: "Set your preferred display format for colors" - ) - let textSpaceTitle = NSLocalizedString("preferences.space.title", comment: "Color Space") let textSpaceDescription = NSLocalizedString( "preferences.space.description", @@ -54,21 +48,6 @@ struct PreferencesView: View { ) Section(header: Text(textFormatTitle).font(.system(size: 16))) { - VStack(alignment: .leading, spacing: 15.0) { - Text(textFormatDescription) - Picker(textFormatTitle, selection: $colorFormat) { - ForEach(ColorFormat.allCases, id: \.self) { value in - Text(value.rawValue) - } - } - .pickerStyle(RadioGroupPickerStyle()) - .horizontalRadioGroupLayout() - .labelsHidden() - } - - Spacer() - .frame(height: 0.0) - VStack(alignment: .leading, spacing: 15.0) { Text(textSpaceDescription) Picker(textSpaceTitle, selection: diff --git a/Pika/Views/Toast.swift b/Pika/Views/Toast.swift index f530d3b..5c26c64 100644 --- a/Pika/Views/Toast.swift +++ b/Pika/Views/Toast.swift @@ -18,7 +18,7 @@ struct Toast: View where Presenting: View { var body: some View { if self.isShowing { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { withAnimation { self.isShowing = false } @@ -35,16 +35,16 @@ struct Toast: View where Presenting: View { } .padding(.vertical, 5.0) .padding(.horizontal, 10.0) - .font(.system(size: 10.0)) + .font(.system(size: 11.0)) .foregroundColor(color) - .cornerRadius(12) + .cornerRadius(4) .overlay( - RoundedRectangle(cornerRadius: 12) - .stroke(color.opacity(0.25), lineWidth: 1) + RoundedRectangle(cornerRadius: 4) + .stroke(color.opacity(0.4), lineWidth: 1) ) .transition(.slide) .opacity(self.isShowing ? 1 : 0) - .offset(x: 4.0, y: 4.0) + .offset(x: 8.0, y: 8.0) } } }