Skip to content

Commit

Permalink
Add preference to hide menu bar icon
Browse files Browse the repository at this point in the history
Fixes #21
  • Loading branch information
sindresorhus committed Nov 13, 2021
1 parent 193eeb7 commit 377c06d
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 7 deletions.
9 changes: 8 additions & 1 deletion Color Picker/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct AppMain: App {
if false {}
}
// TODO: How to replace `File` menu with `Color`?
// TODO: Remove `View` menu.
// TODO: Would be nice to be able to remove the `View` menu: https://github.com/feedback-assistant/reports/issues/252
.commands {
CommandGroup(replacing: .newItem) {}
CommandMenu("Color") {
Expand Down Expand Up @@ -119,4 +119,11 @@ struct AppMain: App {

private final class AppDelegate: NSObject, NSApplicationDelegate {
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { true }

// Does not work on macOS 12.0.1 because of `WindowGroup`: https://github.com/feedback-assistant/reports/issues/246
// This is only run when the app is started when it's already running.
// func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
// AppState.shared.handleAppReopen()
// return true
// }
}
28 changes: 26 additions & 2 deletions Color Picker/AppState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ final class AppState: ObservableObject {
private func didLaunch() {
fixStuff()
setUpEvents()
handleMenuBarIcon()
showWelcomeScreenIfNeeded()
requestReview()

Expand All @@ -151,13 +152,16 @@ final class AppState: ObservableObject {

private func fixStuff() {
// Make the invisible native SwitUI window not block access to the desktop. (macOS 12.0)
// https://github.com/feedback-assistant/reports/issues/253
NSApp.windows.first?.ignoresMouseEvents = true

// Make the invisible native SwiftUI window not show up in mission control when in menu bar mode. (macOS 11.6)
NSApp.windows.first?.collectionBehavior = .stationary

// We hide the “View” menu as there's a macOS bug where it sometimes enables even though it doesn't work and then causes a crash when clicked.
NSApp.mainMenu?.item(withTitle: "View")?.isHidden = true
// We hide the “View” menu as there's a macOS bug where it sometimes enables even though it doesn't work and then causes a crash when clicked. Hiding it does not work on macOS 12.
if #available(macOS 12, *) {} else {
NSApp.mainMenu?.item(withTitle: "View")?.isHidden = true
}
}

private func requestReview() {
Expand All @@ -171,6 +175,22 @@ final class AppState: ObservableObject {
.truncatingFromStart(toCount: 6)
}

private func handleMenuBarIcon() {
guard Defaults[.showInMenuBar] else {
return
}

statusItem.isVisible = true

delay(seconds: 5) { [self] in
guard Defaults[.hideMenuBarIcon] else {
return
}

statusItem.isVisible = false
}
}

func pickColor() {
NSColorSampler().show { [weak self] in
guard
Expand All @@ -197,4 +217,8 @@ final class AppState: ObservableObject {

colorPanel.color = color.usingColorSpace(.sRGB) ?? color
}

func handleAppReopen() {
handleMenuBarIcon()
}
}
13 changes: 11 additions & 2 deletions Color Picker/ColorPickerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ private struct RecentlyPickedColorsButton: View {
Text(color.stringRepresentation)
} icon: {
// We don't use SwiftUI here as it only supports showing an actual image. (macOS 12.0)
// https://github.com/feedback-assistant/reports/issues/247
Image(nsImage: color.swatchImage)
}
.labelStyle(.titleAndIcon)
Expand All @@ -47,6 +48,7 @@ private struct BarView: View {
@Environment(\.colorScheme) private var colorScheme
@EnvironmentObject private var appState: AppState
@StateObject private var pasteboardObserver = NSPasteboard.SimpleObservable(.general).stop()
@Default(.showInMenuBar) private var showInMenuBar

var body: some View {
HStack(spacing: 12) {
Expand Down Expand Up @@ -76,6 +78,7 @@ private struct BarView: View {
Spacer()
}
// Cannot do this as the `Menu` buttons don't respect it. (macOS 12.0.1)
// https://github.com/feedback-assistant/reports/issues/249
// .font(.title3)
.background2 {
RoundedRectangle(cornerRadius: 6, style: .continuous)
Expand All @@ -97,13 +100,19 @@ private struct BarView: View {
Button("Copy as HSB") {
appState.colorPanel.color.hsbColorString.copyToPasteboard()
}
// Without, it becomes disabled. (macOS 12.0.1)
.buttonStyle(.automatic)
if showInMenuBar {
Divider()
Button("Preferences…") {
SSApp.showSettingsWindow()
}
.keyboardShortcut(",")
}
} label: {
Label("More", systemImage: "ellipsis.circle.fill")
.labelStyle(.iconOnly)
// .padding(8) // Has no effect. (macOS 12.0.1)
}
.buttonStyle(.automatic) // Without, it becomes disabled: https://github.com/feedback-assistant/reports/issues/250 (macOS 12.0.1)
.padding(8)
.contentShape(.rectangle)
.fixedSize()
Expand Down
1 change: 1 addition & 0 deletions Color Picker/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ extension Defaults.Keys {

// Settings
static let showInMenuBar = Key<Bool>("showInMenuBar", default: false)
static let hideMenuBarIcon = Key<Bool>("hideMenuBarIcon", default: false)
static let showColorSamplerOnOpen = Key<Bool>("showColorSamplerOnOpen", default: false)
static let menuBarItemClickAction = Key<MenuBarItemClickAction>("menuBarItemClickAction", default: .showMenu)
static let preferredColorFormat = Key<ColorFormat>("preferredColorFormat", default: .hex)
Expand Down
20 changes: 19 additions & 1 deletion Color Picker/Events.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ extension AppState {
return
}

self.statusItem.isVisible = $0.newValue
// We only set the state if it's in Dock mode or menu bar mode showing the icon.
if !$0.newValue || ($0.newValue && !Defaults[.hideMenuBarIcon]) {
self.statusItem.isVisible = $0.newValue
}

SSApp.isDockIconVisible = !$0.newValue
NSApp.activate(ignoringOtherApps: true)

Expand All @@ -30,6 +34,13 @@ extension AppState {
}
.store(in: &cancellables)

Defaults.publisher(.hideMenuBarIcon, options: [])
.receive(on: DispatchQueue.main)
.sink { [self] in
statusItem.isVisible = !$0.newValue
}
.store(in: &cancellables)

KeyboardShortcuts.onKeyUp(for: .pickColor) { [weak self] in
self?.pickColor()
}
Expand All @@ -38,6 +49,13 @@ extension AppState {
self?.colorPanel.toggle()
}

// We use this instead of `applicationShouldHandleReopen` because of the macOS bug.
NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)
.sink { [self] _ in
handleAppReopen()
}
.store(in: &cancellables)

// Workaround for the color picker window not becoming active after the settings window closes. (macOS 11.3)
NotificationCenter.default.publisher(for: NSWindow.willCloseNotification)
.sink { [self] _ in
Expand Down
18 changes: 18 additions & 0 deletions Color Picker/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@ import Defaults
import LaunchAtLogin
import KeyboardShortcuts

private struct HideMenuBarIconSetting: View {
@State private var isShowingAlert = false

var body: some View {
Defaults.Toggle("Hide menu bar icon", key: .hideMenuBarIcon)
.onChange {
isShowingAlert = $0
}
.help("This can be useful if you only use this app with the global keyboard shortcuts.")
.alert2(isPresented: $isShowingAlert) {
Alert(
title: Text("If you need to access the menu bar icon, launch the app to reveal it for 5 seconds.")
)
}
}
}

private struct MenuBarItemClickActionSetting: View {
@Default(.menuBarItemClickAction) private var menuBarItemClickAction

Expand Down Expand Up @@ -68,6 +85,7 @@ private struct GeneralSettings: View {
Group {
LaunchAtLogin.Toggle()
.help(showInMenuBar ? "" : "There is really no point in launching the app at login if it is not in the menu bar. You can instead just put it in the Dock and launch it when needed.")
HideMenuBarIconSetting()
MenuBarItemClickActionSetting()
}
.disabled(!showInMenuBar)
Expand Down
36 changes: 35 additions & 1 deletion Color Picker/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ typealias XColor = UIColor
#endif


func delay(seconds: TimeInterval, closure: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + seconds, execute: closure)
}


extension XColor {
/**
Generate a random color, avoiding black and white.
Expand Down Expand Up @@ -109,7 +114,18 @@ enum SSApp {
static var isDarkMode: Bool { NSApp?.effectiveAppearance.isDarkMode ?? false }

static func quit() {
NSApp.terminate(nil)
DispatchQueue.main.async {
NSApp.terminate(nil)
}
}

static func relaunch() {
let configuration = NSWorkspace.OpenConfiguration()
configuration.createsNewApplicationInstance = true

NSWorkspace.shared.openApplication(at: url, configuration: configuration) { _, _ in
quit()
}
}

static let isFirstLaunch: Bool = {
Expand Down Expand Up @@ -2746,3 +2762,21 @@ extension View {
}
}
}


extension View {
/**
This allows multiple alerts on a single view, which `.alert()` doesn't.
*/
func alert2(
isPresented: Binding<Bool>,
content: @escaping () -> Alert
) -> some View {
background(
EmptyView().alert(
isPresented: isPresented,
content: content
)
)
}
}
1 change: 1 addition & 0 deletions app-store-description.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Pick colors from anywhere using the built-in color picker.
- Launch it at login (when in the menu bar)
- Recently picked colors
- Shortcuts support
- Hide menu bar icon


■ Keyboard shortcuts
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Requires macOS 11 or later.
- Launch it at login (when in the menu bar)
- Recently picked colors
- Shortcuts support
- Hide menu bar icon

## Keyboard shortcuts

Expand Down

0 comments on commit 377c06d

Please sign in to comment.