Skip to content

Commit

Permalink
Add right-click menu to menu bar
Browse files Browse the repository at this point in the history
Closes #4
  • Loading branch information
jordanbaird committed Feb 17, 2024
1 parent 60763a3 commit 69198fb
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Ice.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
17540BDC2B23BD5700A0F965 /* NSBezierPath+intersects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17540BDB2B23BD5700A0F965 /* NSBezierPath+intersects.swift */; };
175584122B541CFC00EDC9D3 /* CustomTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 175584112B541CFC00EDC9D3 /* CustomTabView.swift */; };
175584152B541D6F00EDC9D3 /* MenuBarLayoutTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 175584142B541D6F00EDC9D3 /* MenuBarLayoutTab.swift */; };
175812542B80FBFA00D622DA /* MenuBarHelperPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 175812532B80FBFA00D622DA /* MenuBarHelperPanel.swift */; };
176B23F42ADB76A1008AE86B /* CustomColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 176B23F32ADB76A1008AE86B /* CustomColorPicker.swift */; };
177354662B1B8502001CF731 /* MenuBarShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 177354652B1B8502001CF731 /* MenuBarShape.swift */; };
1773546A2B1BBACF001CF731 /* MenuBarAppearanceTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 177354692B1BBACF001CF731 /* MenuBarAppearanceTab.swift */; };
Expand Down Expand Up @@ -109,6 +110,7 @@
17540BDB2B23BD5700A0F965 /* NSBezierPath+intersects.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSBezierPath+intersects.swift"; sourceTree = "<group>"; };
175584112B541CFC00EDC9D3 /* CustomTabView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomTabView.swift; sourceTree = "<group>"; };
175584142B541D6F00EDC9D3 /* MenuBarLayoutTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarLayoutTab.swift; sourceTree = "<group>"; };
175812532B80FBFA00D622DA /* MenuBarHelperPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarHelperPanel.swift; sourceTree = "<group>"; };
176B23F32ADB76A1008AE86B /* CustomColorPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomColorPicker.swift; sourceTree = "<group>"; };
177354652B1B8502001CF731 /* MenuBarShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarShape.swift; sourceTree = "<group>"; };
177354692B1BBACF001CF731 /* MenuBarAppearanceTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarAppearanceTab.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -532,6 +534,7 @@
children = (
17B7F32A2B264C1800CDCF49 /* MenuBarAppearanceManager.swift */,
174AA5D52B71D97100E3FE74 /* MenuBarAppearancePanel.swift */,
175812532B80FBFA00D622DA /* MenuBarHelperPanel.swift */,
7166834F2A7681AF006ABF84 /* MenuBarManager.swift */,
7162406E2AA0A323003EC671 /* MenuBarSection.swift */,
177354652B1B8502001CF731 /* MenuBarShape.swift */,
Expand Down Expand Up @@ -725,6 +728,7 @@
1787C4302B16AE78002F50DF /* PermissionsManager.swift in Sources */,
170CF88C2B0ED4FA0073F982 /* HotkeyRecordingFailure.swift in Sources */,
170CF88E2B0EDD780073F982 /* LocalizedErrorBox.swift in Sources */,
175812542B80FBFA00D622DA /* MenuBarHelperPanel.swift in Sources */,
7166832E2A767E6A006ABF84 /* IceApp.swift in Sources */,
71FEA2502A8D5D590048341A /* HotkeyRecorder.swift in Sources */,
17C3C5F72B75A36100B9648C /* NSScreen+displayID.swift in Sources */,
Expand Down
138 changes: 138 additions & 0 deletions Ice/MenuBar/MenuBarHelperPanel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//
// MenuBarHelperPanel.swift
// Ice
//

import SwiftUI

// MARK: - MenuBarHelperPanel

/// A panel that manages the menu that appears when the user
/// right-clicks on the menu bar.
class MenuBarHelperPanel: NSPanel {
/// A Boolean value that indicates whether the panel is
/// currently showing the appearance editor popover.
private var isShowingPopover = false

init(origin: CGPoint) {
super.init(
contentRect: CGRect(origin: origin, size: CGSize(width: 1, height: 1)),
styleMask: [.borderless, .nonactivatingPanel],
backing: .buffered,
defer: false
)
level = .statusBar
backgroundColor = .clear
}

/// Shows the right-click menu.
func showMenu() {
let menu = NSMenu(title: Constants.appName)
menu.delegate = self

let editItem = NSMenuItem(
title: "Edit Menu Bar Appearance…",
action: #selector(showAppearanceEditorPopover),
keyEquivalent: ""
)
editItem.target = self
menu.addItem(editItem)

menu.addItem(.separator())

let settingsItem = NSMenuItem(
title: "\(Constants.appName) Settings…",
action: #selector(AppDelegate.openSettingsWindow),
keyEquivalent: ","
)
menu.addItem(settingsItem)

menu.popUp(positioning: nil, at: frame.origin, in: nil)
}

/// Shows the appearance editor popover, centered under
/// the menu bar.
@objc private func showAppearanceEditorPopover() {
guard
let contentView,
let screen
else {
return
}
setFrameOrigin(CGPoint(x: screen.frame.midX - (frame.width / 2), y: screen.visibleFrame.maxY))
let popover = MenuBarAppearanceEditorPopover()
popover.delegate = self
popover.show(relativeTo: .zero, of: contentView, preferredEdge: .minY)
isShowingPopover = true
}
}

// MARK: MenuBarHelperPanel: NSMenuDelegate
extension MenuBarHelperPanel: NSMenuDelegate {
func menuDidClose(_ menu: NSMenu) {
// async so that `isShowingPopover` has a chance to get set
DispatchQueue.main.async { [self] in
guard !isShowingPopover else {
return
}
orderOut(menu)
}
}
}

// MARK: MenuBarHelperPanel: NSPopoverDelegate
extension MenuBarHelperPanel: NSPopoverDelegate {
func popoverDidClose(_ notification: Notification) {
guard let popover = notification.object as? MenuBarAppearanceEditorPopover else {
return
}
isShowingPopover = false
orderOut(popover)
popover.mouseDownMonitor.stop()
}
}

// MARK: - MenuBarAppearanceEditorPopover

/// A popover that displays the menu bar appearance editor at
/// a centered location under the menu bar.
private class MenuBarAppearanceEditorPopover: NSPopover {
private(set) lazy var mouseDownMonitor = GlobalEventMonitor(mask: .leftMouseDown) { [weak self] event in

Check warning on line 100 in Ice/MenuBar/MenuBarHelperPanel.swift

View workflow job for this annotation

GitHub Actions / swiftlint

Unused Closure Parameter Violation: Unused parameter in a closure should be replaced with _ (unused_closure_parameter)
self?.performClose(self)
}

@ViewBuilder
private var contentView: some View {
VStack {
Text("Menu Bar Appearance")
.font(.title2)
.padding(.top)

MenuBarAppearanceTab()
.padding(.top, -14)
.environmentObject(AppState.shared)

HStack {
Spacer()
Button("Done") { [weak self] in
self?.performClose(self)
}
.controlSize(.large)
}
.padding()
}
}

override init() {
super.init()
self.contentViewController = NSHostingController(rootView: contentView)
self.contentSize = CGSize(width: 500, height: 500)
self.behavior = .applicationDefined
self.mouseDownMonitor.start()
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
3 changes: 3 additions & 0 deletions Ice/MenuBar/MenuBarManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ final class MenuBarManager: ObservableObject {
case .rightMouseDown:
if isMouseInsideMenuBar {
showOnHoverPreventedByUserInteraction = true
let helperPanel = MenuBarHelperPanel(origin: NSEvent.mouseLocation)
helperPanel.orderFrontRegardless()
helperPanel.showMenu()
}
default:
break
Expand Down

0 comments on commit 69198fb

Please sign in to comment.