Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ice 0.11.0 #349

Merged
merged 69 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
9a167d7
Menu bar search (#253)
jordanbaird Jul 19, 2024
b113399
Merge branch 'main' into 0.11.0
jordanbaird Jul 19, 2024
8c25f9c
Merge branch 'main' into 0.11.0
jordanbaird Jul 20, 2024
70d6622
Merge branch 'main' into 0.11.0
jordanbaird Jul 23, 2024
e6338f2
Menu bar spacing (#267)
jordanbaird Jul 25, 2024
2a80178
Merge branch 'main' into 0.11.0
jordanbaird Jul 25, 2024
eb2876b
Add setting to customize delay for temporarily shown items
jordanbaird Jul 28, 2024
41518ae
Merge branch 'main' into 0.11.0
jordanbaird Jul 28, 2024
7342293
Merge branch 'temp-shown-item-delay' into 0.11.0
jordanbaird Jul 28, 2024
c489c4b
Gardening
jordanbaird Jul 28, 2024
5e0bab1
fix: change copy for 'Use template image' in settings to 'Apply syste…
dhruvikn Jul 29, 2024
50c81d3
Merge pull request #277 from dhruvikn/fix/use-template-image-copy-change
jordanbaird Jul 29, 2024
ccdeaa8
Merge branch 'main' into 0.11.0
jordanbaird Jul 29, 2024
7f5689e
Merge branch 'main' into 0.11.0
jordanbaird Jul 29, 2024
d086394
Rearrange sections
jordanbaird Jul 29, 2024
2ff3645
Merge branch 'main' into 0.11.0
jordanbaird Jul 30, 2024
318e038
Merge branch 'main' into 0.11.0
jordanbaird Jul 31, 2024
3f4bb20
Merge branch 'main' into 0.11.0
jordanbaird Jul 31, 2024
0583e12
Merge branch 'main' into 0.11.0
jordanbaird Aug 1, 2024
d2f272e
Update search panel background
jordanbaird Aug 1, 2024
c6fa9cd
Merge branch 'main' into 0.11.0
jordanbaird Aug 2, 2024
acf5970
Merge branch 'main' into 0.11.0
jordanbaird Aug 3, 2024
b6bdc04
Merge branch 'main' into 0.11.0
jordanbaird Aug 3, 2024
db12c44
Changes to Menu Item Spacing
stonerl Aug 8, 2024
4fb79b0
MenuBarSearchPanel set frame size
stonerl Aug 9, 2024
cbf0b9d
Merge pull request #306 from stonerl/menu-bar-search-panel
jordanbaird Aug 9, 2024
fb492fd
Address PR change request
stonerl Aug 9, 2024
927c35d
MenuBarSearchPanel set frame size
stonerl Aug 9, 2024
c2d0926
Merge branch '0.11.0' into menu-bar-spacing
stonerl Aug 9, 2024
ab99241
Fix concurrency errors
jordanbaird Aug 9, 2024
c6ea134
Merge pull request #305 from stonerl/menu-bar-spacing
jordanbaird Aug 10, 2024
c1e09c7
Merge branch 'main' into 0.11.0
jordanbaird Aug 10, 2024
eca2087
Misc menu bar search changes
jordanbaird Aug 10, 2024
a7745a4
Revert form formatting
jordanbaird Aug 11, 2024
1c7a9d1
Merge branch 'main' into 0.11.0
jordanbaird Aug 11, 2024
3c752f8
Merge branch 'main' into 0.11.0
jordanbaird Aug 15, 2024
d3ef87c
Merge branch 'main' into 0.11.0
jordanbaird Aug 22, 2024
1097d29
Update project.pbxproj
jordanbaird Aug 22, 2024
1680341
Bump version and build numbers
jordanbaird Aug 22, 2024
590f71c
Merge branch 'main' into 0.11.0
jordanbaird Aug 24, 2024
f1e1e5b
Merge branch 'main' into 0.11.0
jordanbaird Aug 25, 2024
925382e
Merge branch 'main' into 0.11.0
jordanbaird Aug 29, 2024
5711174
Merge branch 'main' into 0.11.0
jordanbaird Sep 1, 2024
c4cdc52
Bump version and build numbers
jordanbaird Sep 1, 2024
ffaf666
Merge branch 'main' into 0.11.0
jordanbaird Sep 6, 2024
fcea4ec
Don't use floating window
jordanbaird Sep 7, 2024
88d7904
Fix key press modifiers
jordanbaird Sep 7, 2024
8bcba23
Fix content margins for SectionedList on macOS 15
jordanbaird Sep 9, 2024
f13fd69
Implement GroupedForm
jordanbaird Sep 9, 2024
0e2ccc6
Revert "Implement GroupedForm"
jordanbaird Sep 9, 2024
9bf6762
New UI (#346)
jordanbaird Sep 11, 2024
733beb6
Cleanup
jordanbaird Sep 11, 2024
6b53346
Picker and menu tweaks
jordanbaird Sep 12, 2024
4746e8b
Use inset menu bar shape by default
jordanbaird Sep 12, 2024
38826a9
Bump version and build numbers
jordanbaird Sep 12, 2024
4b67f7e
IcePicker and IceMenu changes
jordanbaird Sep 13, 2024
37f4092
Restructure files
jordanbaird Sep 13, 2024
44ec8a9
Rename "Menu Bar Items" settings pane to "Menu Bar Layout"
jordanbaird Sep 14, 2024
8808f87
Fix menu bar search item selection
jordanbaird Sep 15, 2024
3c9be16
Stroke border around layout bars
jordanbaird Sep 15, 2024
021ac07
Menu bar search panel tweaks
jordanbaird Sep 15, 2024
a189a96
Menu bar item spacing fixes
jordanbaird Sep 15, 2024
6366dfd
Reorganize project files
jordanbaird Sep 15, 2024
8600f5a
Convert project groups to new Xcode 16 folders
jordanbaird Sep 15, 2024
b94bf26
Remove auto-generated groups
jordanbaird Sep 15, 2024
b1a5bf7
Update MenuBarItemSpacingManager.swift
jordanbaird Sep 15, 2024
386833f
Add beta badge to menu bar item spacing setting
jordanbaird Sep 15, 2024
8e7618b
Update GeneralSettingsPane.swift
jordanbaird Sep 16, 2024
c6fd3c1
Bump version and build numbers
jordanbaird Sep 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
Loading