Skip to content

Commit b043d6a

Browse files
Add automatic Ice Bar toggle based on screen width threshold
1 parent 11edd39 commit b043d6a

File tree

4 files changed

+93
-0
lines changed

4 files changed

+93
-0
lines changed

Ice/Settings/SettingsManagers/GeneralSettingsManager.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Ice
44
//
55

6+
import AppKit
67
import Combine
78
import Foundation
89

@@ -27,6 +28,14 @@ final class GeneralSettingsManager: ObservableObject {
2728
/// in a separate bar below the menu bar.
2829
@Published var useIceBar = false
2930

31+
/// A Boolean value that indicates whether Ice Bar should be
32+
/// automatically enabled on built-in displays.
33+
@Published var autoEnableIceBarOnBuiltInDisplay = false
34+
35+
/// The screen width threshold (in pixels) below which Ice Bar is enabled.
36+
/// Ice Bar will be enabled when screen width < threshold.
37+
@Published var iceBarDisplayWidthThreshold: Double = 3000
38+
3039
/// The location where the Ice Bar appears.
3140
@Published var iceBarLocation: IceBarLocation = .dynamic
3241

@@ -78,12 +87,16 @@ final class GeneralSettingsManager: ObservableObject {
7887
func performSetup() {
7988
loadInitialState()
8089
configureCancellables()
90+
observeScreenChanges()
91+
updateIceBarForCurrentDisplay()
8192
}
8293

8394
private func loadInitialState() {
8495
Defaults.ifPresent(key: .showIceIcon, assign: &showIceIcon)
8596
Defaults.ifPresent(key: .customIceIconIsTemplate, assign: &customIceIconIsTemplate)
8697
Defaults.ifPresent(key: .useIceBar, assign: &useIceBar)
98+
Defaults.ifPresent(key: .autoEnableIceBarOnBuiltInDisplay, assign: &autoEnableIceBarOnBuiltInDisplay)
99+
Defaults.ifPresent(key: .iceBarDisplayWidthThreshold, assign: &iceBarDisplayWidthThreshold)
87100
Defaults.ifPresent(key: .showOnClick, assign: &showOnClick)
88101
Defaults.ifPresent(key: .showOnHover, assign: &showOnHover)
89102
Defaults.ifPresent(key: .showOnScroll, assign: &showOnScroll)
@@ -156,6 +169,24 @@ final class GeneralSettingsManager: ObservableObject {
156169
}
157170
.store(in: &c)
158171

172+
$autoEnableIceBarOnBuiltInDisplay
173+
.receive(on: DispatchQueue.main)
174+
.sink { [weak self] autoEnable in
175+
Defaults.set(autoEnable, forKey: .autoEnableIceBarOnBuiltInDisplay)
176+
if autoEnable {
177+
self?.updateIceBarForCurrentDisplay()
178+
}
179+
}
180+
.store(in: &c)
181+
182+
$iceBarDisplayWidthThreshold
183+
.receive(on: DispatchQueue.main)
184+
.sink { [weak self] threshold in
185+
Defaults.set(threshold, forKey: .iceBarDisplayWidthThreshold)
186+
self?.updateIceBarForCurrentDisplay()
187+
}
188+
.store(in: &c)
189+
159190
$iceBarLocation
160191
.receive(on: DispatchQueue.main)
161192
.sink { location in
@@ -215,6 +246,32 @@ final class GeneralSettingsManager: ObservableObject {
215246

216247
cancellables = c
217248
}
249+
250+
/// Updates the Ice Bar setting based on the current display configuration.
251+
private func updateIceBarForCurrentDisplay() {
252+
guard autoEnableIceBarOnBuiltInDisplay else {
253+
return
254+
}
255+
256+
// Get the main screen (where the menu bar is)
257+
guard let mainScreen = NSScreen.main else {
258+
return
259+
}
260+
261+
// Enable Ice Bar if screen width is less than threshold
262+
let screenWidth = mainScreen.frame.width
263+
useIceBar = screenWidth < iceBarDisplayWidthThreshold
264+
}
265+
266+
/// Sets up an observer for screen configuration changes.
267+
func observeScreenChanges() {
268+
NotificationCenter.default.publisher(for: NSApplication.didChangeScreenParametersNotification)
269+
.receive(on: DispatchQueue.main)
270+
.sink { [weak self] _ in
271+
self?.updateIceBarForCurrentDisplay()
272+
}
273+
.store(in: &cancellables)
274+
}
218275
}
219276

220277
// MARK: GeneralSettingsManager: BindingExposable

Ice/Settings/SettingsPanes/GeneralSettingsPane.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,10 @@ struct GeneralSettingsPane: View {
176176
@ViewBuilder
177177
private var iceBarOptions: some View {
178178
useIceBar
179+
autoEnableIceBarToggle
180+
if manager.autoEnableIceBarOnBuiltInDisplay {
181+
widthThresholdInput
182+
}
179183
if manager.useIceBar {
180184
iceBarLocationPicker
181185
}
@@ -185,6 +189,31 @@ struct GeneralSettingsPane: View {
185189
private var useIceBar: some View {
186190
Toggle("Use Ice Bar", isOn: manager.bindings.useIceBar)
187191
.annotation("Show hidden menu bar items in a separate bar below the menu bar")
192+
.disabled(manager.autoEnableIceBarOnBuiltInDisplay)
193+
}
194+
195+
@ViewBuilder
196+
private var autoEnableIceBarToggle: some View {
197+
Toggle(isOn: manager.bindings.autoEnableIceBarOnBuiltInDisplay) {
198+
HStack {
199+
Text("Auto-enable on narrow displays")
200+
BetaBadge()
201+
}
202+
}
203+
.annotation("Automatically enable Ice Bar when screen width is below the threshold")
204+
}
205+
206+
@ViewBuilder
207+
private var widthThresholdInput: some View {
208+
HStack {
209+
Text("Width threshold:")
210+
TextField("", value: manager.bindings.iceBarDisplayWidthThreshold, format: .number.grouping(.never))
211+
.textFieldStyle(.roundedBorder)
212+
.frame(width: 80)
213+
Text("pixels")
214+
.foregroundStyle(.secondary)
215+
}
216+
.annotation("Ice Bar will be enabled when screen width is less than this value")
188217
}
189218

190219
@ViewBuilder

Ice/Utilities/Defaults.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ extension Defaults {
175175

176176
case iceBarLocation = "IceBarLocation"
177177
case iceBarPinnedLocation = "IceBarPinnedLocation"
178+
case autoEnableIceBarOnBuiltInDisplay = "AutoEnableIceBarOnBuiltInDisplay"
179+
case iceBarDisplayWidthThreshold = "IceBarDisplayWidthThreshold"
178180

179181
// MARK: Migration
180182

Ice/Utilities/Extensions.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,11 @@ extension NSScreen {
453453
)
454454
}
455455

456+
/// A Boolean value that indicates whether the screen is a built-in display (laptop screen).
457+
var isBuiltIn: Bool {
458+
CGDisplayIsBuiltin(displayID) != 0
459+
}
460+
456461
/// Returns the height of the menu bar on this screen.
457462
func getMenuBarHeight() -> CGFloat? {
458463
let menuBarWindow = WindowInfo.getMenuBarWindow(for: displayID)

0 commit comments

Comments
 (0)