Skip to content

Commit

Permalink
Various reworks
Browse files Browse the repository at this point in the history
* Remove `ObservableObject` conformance from `ControlItem` and `MenuBarSection`
* Comment changes
* Misc other minor reworks
  • Loading branch information
jordanbaird committed Aug 29, 2024
1 parent dc7e4ab commit 53d5819
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 102 deletions.
2 changes: 1 addition & 1 deletion Ice/ControlItem/ControlItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import OSLog

/// A status item that controls the a section in the menu bar.
@MainActor
final class ControlItem: ObservableObject {
final class ControlItem {
enum Identifier: String, Hashable, CaseIterable {
case iceIcon = "SItem"
case hidden = "HItem"
Expand Down
61 changes: 22 additions & 39 deletions Ice/MenuBar/MenuBarManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,22 @@ final class MenuBarManager: ObservableObject {
@Published private(set) var isMenuBarHiddenBySystemUserDefaults = false

private(set) weak var appState: AppState?

private(set) var sections = [MenuBarSection]()
private var cancellables = Set<AnyCancellable>()

let appearanceManager: MenuBarAppearanceManager

let iceBarPanel: IceBarPanel

private let encoder = JSONEncoder()

private let decoder = JSONDecoder()

private var isHidingApplicationMenus = false

private var canUpdateAverageColor = false

private var cancellables = Set<AnyCancellable>()
private(set) var sections = [MenuBarSection]()

/// The currently shown section.
var shownSection: MenuBarSection? {
// filter out the visible section;
// if multiple sections are shown, return the last one
// Filter out the visible section. If multiple sections are shown, return the last one.
sections.lazy
.filter { section in
section.name != .visible
Expand All @@ -71,7 +66,7 @@ final class MenuBarManager: ObservableObject {

/// Performs the initial setup of the menu bar's section list.
private func initializeSections() {
// make sure initialization can only happen once
// Make sure initialization can only happen once.
guard sections.isEmpty else {
Logger.menuBarManager.warning("Sections already initialized")
return
Expand All @@ -83,7 +78,7 @@ final class MenuBarManager: ObservableObject {
MenuBarSection(name: .alwaysHidden),
]

// assign the global app state to each section
// Assign the global app state to each section.
if let appState {
for section in sections {
section.assignAppState(appState)
Expand Down Expand Up @@ -123,7 +118,7 @@ final class MenuBarManager: ObservableObject {
.store(in: &c)
}

// handle focusedApp rehide strategy
// Handle the `focusedApp` rehide strategy.
NSWorkspace.shared.publisher(for: \.frontmostApplication)
.sink { [weak self] _ in
if
Expand Down Expand Up @@ -173,7 +168,7 @@ final class MenuBarManager: ObservableObject {
}
.store(in: &c)

// hide application menus when a section is shown (if applicable)
// Hide application menus when a section is shown (if applicable).
Publishers.MergeMany(sections.map { $0.controlItem.$state })
.removeDuplicates()
.receive(on: DispatchQueue.main)
Expand All @@ -185,11 +180,11 @@ final class MenuBarManager: ObservableObject {
return
}

// don't continue if:
// * the "HideApplicationMenus" setting isn't enabled
// * the menu bar is hidden by the system
// * the active space is fullscreen
// * the settings window is visible
// Don't continue if:
// * The "HideApplicationMenus" setting isn't enabled.
// * The menu bar is hidden by the system.
// * The active space is fullscreen.
// * The settings window is visible.
guard
appState.settingsManager.advancedSettingsManager.hideApplicationMenus,
!isMenuBarHiddenBySystem,
Expand All @@ -206,25 +201,25 @@ final class MenuBarManager: ObservableObject {

let displayID = screen.displayID

// get the application menu frame for the display
// Get the application menu frame for the display.
guard let applicationMenuFrame = getApplicationMenuFrame(for: displayID) else {
return
}

let items = MenuBarItem.getMenuBarItems(on: displayID, using: .coreGraphics, onScreenOnly: true, sortingBy: .orderInMenuBar)

// get the leftmost item on the screen; the application menu should
// be hidden if the item's minX is close to the maxX of the menu
// Get the leftmost item on the screen. The application menu should
// be hidden if the item's minX is close to the maxX of the menu.
guard let leftmostItem = items.min(by: { $0.frame.minX < $1.frame.minX }) else {
return
}

// offset the leftmost item's minX by its width to give ourselves
// a little wiggle room
// Offset the leftmost item's minX by its width to give ourselves
// a little wiggle room.
let offsetMinX = leftmostItem.frame.minX - leftmostItem.frame.width

// if the offset value is less than or equal to the maxX of the
// application menu frame, activate the app to hide the menu
// If the offset value is less than or equal to the maxX of the
// application menu frame, activate the app to hide the menu.
if offsetMinX <= applicationMenuFrame.maxX + 15 {
hideApplicationMenus()
}
Expand All @@ -234,15 +229,6 @@ final class MenuBarManager: ObservableObject {
}
.store(in: &c)

// propagate changes from all sections
for section in sections {
section.objectWillChange
.sink { [weak self] in
self?.objectWillChange.send()
}
.store(in: &c)
}

cancellables = c
}

Expand Down Expand Up @@ -329,10 +315,9 @@ final class MenuBarManager: ObservableObject {
return nil
}

// the Accessibility API returns the menu bar for the active screen, regardless of
// the display origin used; this workaround prevents an incorrect frame from being
// returned for inactive displays in multi-display setups where one display has a
// notch
// The Accessibility API returns the menu bar for the active screen, regardless of the
// display origin used. This workaround prevents an incorrect frame from being returned
// for inactive displays in multi-display setups where one display has a notch.
if
let mainScreen = NSScreen.main,
let thisScreen = NSScreen.screens.first(where: { $0.displayID == displayID }),
Expand Down Expand Up @@ -416,10 +401,8 @@ final class MenuBarManager: ObservableObject {
}
}

// MARK: MenuBarManager: BindingExposable
extension MenuBarManager: BindingExposable { }

// MARK: - Logger
private extension Logger {
static let menuBarManager = Logger(category: "MenuBarManager")
}
100 changes: 38 additions & 62 deletions Ice/MenuBar/MenuBarSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,40 @@
//

import Cocoa
import Combine
import OSLog

/// A representation of a section in a menu bar.
@MainActor
final class MenuBarSection: ObservableObject {
let name: Name
final class MenuBarSection {
/// The name of a menu bar section.
enum Name: CaseIterable {
case visible
case hidden
case alwaysHidden

var menuString: String {
switch self {
case .visible: "Visible"
case .hidden: "Hidden"
case .alwaysHidden: "Always Hidden"
}
}

var logString: String {
switch self {
case .visible: "visible section"
case .hidden: "hidden section"
case .alwaysHidden: "always hidden section"
}
}
}

let name: Name
let controlItem: ControlItem

private var rehideTimer: Timer?

private var rehideMonitor: UniversalEventMonitor?

private var cancellables = Set<AnyCancellable>()

private var useIceBar: Bool {
appState?.settingsManager.generalSettingsManager.useIceBar ?? false
}
Expand Down Expand Up @@ -52,6 +70,7 @@ final class MenuBarSection: ObservableObject {
appState?.menuBarManager
}

/// A Boolean value that indicates whether the section is hidden.
var isHidden: Bool {
if useIceBar {
if controlItem.state == .showItems {
Expand All @@ -78,44 +97,29 @@ final class MenuBarSection: ObservableObject {
}
}

/// A Boolean value that indicates whether the section is enabled.
var isEnabled: Bool {
if case .visible = name {
// visible section should always be enabled
// The visible section should always be enabled.
return true
}
return controlItem.isAddedToMenuBar
}

/// Creates a section with the given name and control item.
init(name: Name, controlItem: ControlItem) {
self.name = name
self.controlItem = controlItem
configureCancellables()
}

/// Creates a menu bar section with the given name.
/// Creates a section with the given name.
convenience init(name: Name) {
let controlItem = switch name {
case .visible:
ControlItem(identifier: .iceIcon)
case .hidden:
ControlItem(identifier: .hidden)
case .alwaysHidden:
ControlItem(identifier: .alwaysHidden)
let identifier: ControlItem.Identifier = switch name {
case .visible: .iceIcon
case .hidden: .hidden
case .alwaysHidden: .alwaysHidden
}
self.init(name: name, controlItem: controlItem)
}

private func configureCancellables() {
var c = Set<AnyCancellable>()

// propagate changes from the section's control item
controlItem.objectWillChange
.sink { [weak self] in
self?.objectWillChange.send()
}
.store(in: &c)

cancellables = c
self.init(name: name, controlItem: ControlItem(identifier: identifier))
}

/// Assigns the section's app state.
Expand All @@ -127,7 +131,7 @@ final class MenuBarSection: ObservableObject {
self.appState = appState
}

/// Shows the status items in the section.
/// Shows the section.
func show() {
guard
let menuBarManager,
Expand All @@ -136,7 +140,7 @@ final class MenuBarSection: ObservableObject {
return
}
guard controlItem.isAddedToMenuBar else {
// section is disabled
// The section is disabled.
return
}
switch name {
Expand Down Expand Up @@ -187,7 +191,7 @@ final class MenuBarSection: ObservableObject {
startRehideChecks()
}

/// Hides the status items in the section.
/// Hides the section.
func hide() {
guard
let appState,
Expand Down Expand Up @@ -229,7 +233,7 @@ final class MenuBarSection: ObservableObject {
stopRehideChecks()
}

/// Toggles the visibility of the status items in the section.
/// Toggles the visibility of the section.
func toggle() {
if isHidden {
show()
Expand Down Expand Up @@ -298,36 +302,8 @@ final class MenuBarSection: ObservableObject {
}
}

// MARK: MenuBarSection: BindingExposable
extension MenuBarSection: BindingExposable { }

// MARK: MenuBarSection.Name
extension MenuBarSection {
/// The name of a menu bar section.
enum Name: CaseIterable {
case visible
case hidden
case alwaysHidden

var menuString: String {
switch self {
case .visible: "Visible"
case .hidden: "Hidden"
case .alwaysHidden: "Always-Hidden"
}
}

var logString: String {
switch self {
case .visible: "visible section"
case .hidden: "hidden section"
case .alwaysHidden: "always-hidden section"
}
}
}
}

// MARK: - Logger
private extension Logger {
static let menuBarSection = Logger(category: "MenuBarSection")
}

0 comments on commit 53d5819

Please sign in to comment.