Skip to content

Commit

Permalink
Merge pull request #349 from jordanbaird/0.11.0
Browse files Browse the repository at this point in the history
Ice 0.11.0
  • Loading branch information
jordanbaird authored Sep 16, 2024
2 parents a109c86 + c6fd3c1 commit 83f5d25
Show file tree
Hide file tree
Showing 63 changed files with 2,414 additions and 1,326 deletions.
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ disabled_rules:
- file_length
- function_body_length
- function_parameter_count
- generic_type_name
- identifier_name
- large_tuple
- line_length
Expand Down
932 changes: 41 additions & 891 deletions Ice.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "041f42d8ff3842bfd2a979ca1640e0292a5910cb327bf60beb5977a50e12edf5",
"originHash" : "24a4ad9c87c9d787a96ab567d6386b59cd16d86f1ddaee8967a10a304dca7993",
"pins" : [
{
"identity" : "axswift",
Expand All @@ -19,6 +19,15 @@
"version" : "1.1.6"
}
},
{
"identity" : "ifrit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ukushu/Ifrit",
"state" : {
"revision" : "6c8a4bc89881e641c1ca8e559d7f41ad030d8561",
"version" : "1.1.1"
}
},
{
"identity" : "launchatlogin-modern",
"kind" : "remoteSourceControl",
Expand Down
5 changes: 4 additions & 1 deletion Ice/Hotkeys/HotkeyAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ enum HotkeyAction: String, Codable, CaseIterable {
case toggleAlwaysHiddenSection = "ToggleAlwaysHiddenSection"
case toggleApplicationMenus = "ToggleApplicationMenus"
case showSectionDividers = "ShowSectionDividers"
case searchMenuBarItems = "SearchMenuBarItems"

@MainActor
func perform(appState: AppState) {
func perform(appState: AppState) async {
switch self {
case .toggleHiddenSection:
guard let section = appState.menuBarManager.section(withName: .hidden) else {
Expand All @@ -34,6 +35,8 @@ enum HotkeyAction: String, Codable, CaseIterable {
appState.menuBarManager.toggleApplicationMenus()
case .showSectionDividers:
appState.settingsManager.advancedSettingsManager.showSectionDividers.toggle()
case .searchMenuBarItems:
await appState.menuBarManager.searchPanel.toggle()
}
}
}
13 changes: 9 additions & 4 deletions Ice/Hotkeys/HotkeyRecorder/HotkeyRecorder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@ struct HotkeyRecorder<Label: View>: View {
}

var body: some View {
LabeledContent {
IceLabeledContent {
HStack(spacing: 1) {
leadingSegment
trailingSegment
}
.frame(width: 130, height: 22)
.alignmentGuide(.firstTextBaseline) { dimension in
dimension[VerticalAlignment.center]
}
} label: {
label
.alignmentGuide(.firstTextBaseline) { dimension in
dimension[VerticalAlignment.center]
}
}
.alert(
"Hotkey is reserved by macOS",
Expand Down Expand Up @@ -128,9 +134,8 @@ private struct HotkeyRecorderSegmentButtonStyle: PrimitiveButtonStyle {
}

func makeBody(configuration: Configuration) -> some View {
UnevenRoundedRectangle(cornerRadii: radii)
.foregroundStyle(Color.primary) // explicitly specify `Color.primary`
.opacity(isHighlighted || isPressed ? 0.2 : 0.1)
UnevenRoundedRectangle(cornerRadii: radii, style: .circular)
.fill(isHighlighted || isPressed ? .tertiary : .quaternary)
.overlay {
configuration.label
.lineLimit(1)
Expand Down
26 changes: 12 additions & 14 deletions Ice/Main/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import OSLog
import SwiftUI

@MainActor
class AppDelegate: NSObject, NSApplicationDelegate {
final class AppDelegate: NSObject, NSApplicationDelegate {
private weak var appState: AppState?

// MARK: NSApplicationDelegate Methods
Expand All @@ -34,25 +34,23 @@ class AppDelegate: NSObject, NSApplicationDelegate {
return
}

// Assign and close the various windows.
let windowAssignments: KeyValuePairs = [
Constants.settingsWindowID: appState.assignSettingsWindow,
Constants.permissionsWindowID: appState.assignPermissionsWindow,
]
for (identifier, assign) in windowAssignments {
if let window = NSApp.window(withIdentifier: identifier) {
assign(window)
window.close()
}
}

// Hide the main menu to make more space in the menu bar.
if let mainMenu = NSApp.mainMenu {
for item in mainMenu.items {
item.isHidden = true
}
}

// On macOS 15, the windows handle their own closure. If on macOS 14,
// close them here.
//
// NOTE: The windows might not close when running from Xcode, but it
// does work when running standalone.
if #unavailable(macOS 15.0) {
appState.settingsWindow?.close()
appState.permissionsWindow?.close()
}

if !appState.isPreview {
// If we have the required permissions, set up the shared app state.
// Otherwise, open the permissions window.
Expand Down Expand Up @@ -95,7 +93,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let appState,
let settingsWindow = appState.settingsWindow
else {
Logger.appDelegate.warning("Failed to open settings window")
Logger.appDelegate.error("Failed to open settings window")
return
}
// Small delay makes this more reliable.
Expand Down
75 changes: 30 additions & 45 deletions Ice/Main/AppState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ final class AppState: ObservableObject {
/// A Boolean value that indicates whether the active space is fullscreen.
@Published private(set) var isActiveSpaceFullscreen = Bridging.isSpaceFullscreen(Bridging.activeSpaceID)

private var cancellables = Set<AnyCancellable>()

/// Manager for events received by the app.
private(set) lazy var eventManager = EventManager(appState: self)

Expand All @@ -33,35 +31,26 @@ final class AppState: ObservableObject {
/// Global cache for menu bar item images.
private(set) lazy var imageCache = MenuBarItemImageCache(appState: self)

/// The app's hotkey registry.
nonisolated let hotkeyRegistry = HotkeyRegistry()
/// Manager for menu bar item spacing.
let spacingManager = MenuBarItemSpacingManager()

/// Manager for app updates.
let updatesManager = UpdatesManager()

/// Model for app-wide navigation.
let navigationState = AppNavigationState()

/// The app's hotkey registry.
nonisolated let hotkeyRegistry = HotkeyRegistry()

/// The app's delegate.
private(set) weak var appDelegate: AppDelegate?

/// The window that contains the settings interface.
private(set) weak var settingsWindow: NSWindow?

/// The window that contains the permissions interface.
private(set) weak var permissionsWindow: NSWindow?

/// A Boolean value that indicates whether the "ShowOnHover" feature is prevented.
private(set) var isShowOnHoverPrevented = false

/// A Boolean value that indicates whether the application can set the
/// cursor in the background.
///
/// The default value of this property is `false`.
var setsCursorInBackground: Bool {
get { Bridging.getConnectionProperty(forKey: "SetsCursorInBackground") as? Bool ?? false }
set { Bridging.setConnectionProperty(newValue, forKey: "SetsCursorInBackground") }
}
/// Storage for internal observers.
private var cancellables = Set<AnyCancellable>()

/// A Boolean value that indicates whether the app is running as a SwiftUI preview.
let isPreview: Bool = {
Expand All @@ -74,8 +63,21 @@ final class AppState: ObservableObject {
#endif
}()

init() {
MigrationManager(appState: self).migrateAll()
/// The window that contains the settings interface.
var settingsWindow: NSWindow? {
NSApp.window(withIdentifier: Constants.settingsWindowID)
}

/// The window that contains the permissions interface.
var permissionsWindow: NSWindow? {
NSApp.window(withIdentifier: Constants.permissionsWindowID)
}

/// A Boolean value that indicates whether the application can set the cursor
/// in the background.
var setsCursorInBackground: Bool {
get { Bridging.getConnectionProperty(forKey: "SetsCursorInBackground") as? Bool ?? false }
set { Bridging.setConnectionProperty(newValue, forKey: "SetsCursorInBackground") }
}

private func configureCancellables() {
Expand All @@ -85,12 +87,12 @@ final class AppState: ObservableObject {
NSWorkspace.shared.notificationCenter
.publisher(for: NSWorkspace.activeSpaceDidChangeNotification)
.mapToVoid(),
// frontmost application change can indicate a space change from one display to
// another, which gets ignored by `NSWorkspace.activeSpaceDidChangeNotification`
// Frontmost application change can indicate a space change from one display to
// another, which gets ignored by NSWorkspace.activeSpaceDidChangeNotification.
NSWorkspace.shared
.publisher(for: \.frontmostApplication)
.mapToVoid(),
// clicking into a fullscreen space from another space is also ignored
// Clicking into a fullscreen space from another space is also ignored.
UniversalEventMonitor
.publisher(for: .leftMouseDown)
.delay(for: 0.1, scheduler: DispatchQueue.main)
Expand Down Expand Up @@ -124,6 +126,8 @@ final class AppState: ObservableObject {
navigationState.isSettingsPresented = isVisible
}
.store(in: &c)
} else {
Logger.appState.warning("No settings window!")
}

Publishers.Merge(
Expand Down Expand Up @@ -173,7 +177,6 @@ final class AppState: ObservableObject {
settingsManager.performSetup()
itemManager.performSetup()
imageCache.performSetup()
permissionsWindow?.close()
}

/// Assigns the app delegate to the app state.
Expand All @@ -185,28 +188,10 @@ final class AppState: ObservableObject {
self.appDelegate = appDelegate
}

/// Assigns the settings window to the app state.
func assignSettingsWindow(_ settingsWindow: NSWindow) {
guard self.settingsWindow == nil else {
Logger.appState.warning("Multiple attempts made to assign settings window")
return
}
self.settingsWindow = settingsWindow
}

/// Assigns the permissions window to the app state.
func assignPermissionsWindow(_ permissionsWindow: NSWindow) {
guard self.permissionsWindow == nil else {
Logger.appState.warning("Multiple attempts made to assign permissions window")
return
}
self.permissionsWindow = permissionsWindow
}

/// Activates the app and sets its activation policy to the given value.
func activate(withPolicy policy: NSApplication.ActivationPolicy) {
// store whether the app has previously activated inside an internal
// context to keep it isolated
// Store whether the app has previously activated inside an internal
// context to keep it isolated.
enum Context {
static let hasActivated = ObjectAssociation<Bool>()
}
Expand All @@ -225,7 +210,7 @@ final class AppState: ObservableObject {
} else {
Context.hasActivated[self] = true
Logger.appState.debug("First time activating app, so going through Dock")
// hack to make sure the app properly activates for the first time
// Hack to make sure the app properly activates for the first time.
NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.dock").first?.activate()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
activate()
Expand Down
15 changes: 3 additions & 12 deletions Ice/Main/IceApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,16 @@ import SwiftUI
struct IceApp: App {
@NSApplicationDelegateAdaptor var appDelegate: AppDelegate
@ObservedObject var appState = AppState()
@Environment(\.openWindow) private var openWindow

init() {
NSSplitViewItem.swizzle()
IceBarPanel.swizzle()
// Occurs before AppDelegate.applicationWillFinishLaunching(_:).
MigrationManager(appState: appState).migrateAll()
appDelegate.assignAppState(appState)
}

var body: some Scene {
SettingsWindow(appState: appState, onAppear: {
// Open the permissions window no matter what, so that we can
// reference it. We'll close it in AppDelegate if permissions
// have already been granted.
openWindow(id: Constants.permissionsWindowID)
})
PermissionsWindow(appState: appState, onContinue: {
appState.performSetup()
openWindow(id: Constants.settingsWindowID)
})
SettingsWindow(appState: appState)
PermissionsWindow(appState: appState)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ final class AppNavigationState: ObservableObject {
@Published var isAppFrontmost = false
@Published var isSettingsPresented = false
@Published var isIceBarPresented = false
@Published var isSearchPresented = false
@Published var settingsNavigationIdentifier: SettingsNavigationIdentifier = .general
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/// An identifier used for navigation in the settings interface.
enum SettingsNavigationIdentifier: String, NavigationIdentifier {
case general = "General"
case menuBarItems = "Menu Bar Items"
case menuBarLayout = "Menu Bar Layout"
case menuBarAppearance = "Menu Bar Appearance"
case hotkeys = "Hotkeys"
case advanced = "Advanced"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ extension MenuBarAppearanceConfiguration {
static let defaultConfiguration = MenuBarAppearanceConfiguration(
hasShadow: false,
hasBorder: false,
isInset: false,
isInset: true,
borderColor: .black,
borderWidth: 1,
shapeKind: .none,
Expand Down
File renamed without changes.
File renamed without changes.
8 changes: 4 additions & 4 deletions Ice/MenuBar/MenuBarItemImageCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ class MenuBarItemImageCache: ObservableObject {
return
}

if !appState.navigationState.isIceBarPresented {
if !appState.navigationState.isIceBarPresented && !appState.navigationState.isSearchPresented {
guard appState.navigationState.isAppFrontmost else {
Logger.imageCache.debug("Skipping image cache as Ice Bar not visible, app not frontmost")
return
Expand All @@ -215,8 +215,8 @@ class MenuBarItemImageCache: ObservableObject {
Logger.imageCache.debug("Skipping image cache as Ice Bar not visible, Settings not visible")
return
}
guard case .menuBarItems = appState.navigationState.settingsNavigationIdentifier else {
Logger.imageCache.debug("Skipping image cache as Ice Bar not visible, Settings visible but not on Menu Bar Items pane")
guard case .menuBarLayout = appState.navigationState.settingsNavigationIdentifier else {
Logger.imageCache.debug("Skipping image cache as Ice Bar not visible, Settings visible but not on Menu Bar Layout pane")
return
}
}
Expand All @@ -229,7 +229,7 @@ class MenuBarItemImageCache: ObservableObject {
}

var sectionsNeedingDisplay = [MenuBarSection.Name]()
if appState.navigationState.isSettingsPresented {
if appState.navigationState.isSettingsPresented || appState.navigationState.isSearchPresented {
sectionsNeedingDisplay = MenuBarSection.Name.allCases
} else if
appState.navigationState.isIceBarPresented,
Expand Down
Loading

0 comments on commit 83f5d25

Please sign in to comment.