Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
Prev Previous commit
Next Next commit
UI: Refine Custom HUD and Menu Slider aesthetics
- Make Custom HUD background transparent to avoid square opaque corners
- Update custom HUD icon and slider colors for better macOS integration
- Adjust Menu Slider icon padding and layout constraints
  • Loading branch information
arsarsars1 committed Apr 20, 2026
commit a96f654948c0d5b1b75eb031327454635b519973
21 changes: 15 additions & 6 deletions MonitorControl/Support/CustomHUD.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class CustomHUDManager {
private func createHUDWindow(displayID: CGDirectDisplayID) -> NSWindow {
let window = NSPanel(
contentRect: NSRect(x: 0, y: 0, width: 260, height: 56),
styleMask: [.nonactivatingPanel, .hudWindow],
styleMask: [.nonactivatingPanel],
backing: .buffered,
defer: false
)
Expand Down Expand Up @@ -96,13 +96,22 @@ class CustomHUDManager {
private func createHUDContentView(type: HUDType, value: Float, maxValue: Float) -> NSView {
let w: CGFloat = 260
let h: CGFloat = 56

// Wrap the visual effect view in a transparent root view.
// This ensures the NSWindow doesn't force a square opaque background on the corners.
let rootView = NSView(frame: NSRect(x: 0, y: 0, width: w, height: h))
rootView.wantsLayer = true
rootView.layer?.backgroundColor = NSColor.clear.cgColor

let containerView = NSVisualEffectView(frame: NSRect(x: 0, y: 0, width: w, height: h))
containerView.material = .hudWindow
containerView.blendingMode = .behindWindow
containerView.state = .active
containerView.wantsLayer = true
containerView.layer?.cornerRadius = h / 2
containerView.layer?.masksToBounds = true

rootView.addSubview(containerView)

let iconSize: CGFloat = 26
let iconView = NSImageView(frame: NSRect(x: 18, y: (h - iconSize) / 2, width: iconSize, height: iconSize))
Expand Down Expand Up @@ -149,7 +158,7 @@ class CustomHUDManager {
let normalizedValue = CGFloat(min(max(value / maxValue, 0), 1))
let progressFill = NSView(frame: NSRect(x: barX, y: barY, width: max(barH, barW * normalizedValue), height: barH))
progressFill.wantsLayer = true
progressFill.layer?.backgroundColor = NSColor.white.cgColor
progressFill.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.9).cgColor
progressFill.layer?.cornerRadius = barH / 2
containerView.addSubview(progressFill)

Expand All @@ -161,7 +170,7 @@ class CustomHUDManager {
label.alignment = .right
containerView.addSubview(label)

return containerView
return rootView
}

private var fadeTimers: [CGDirectDisplayID: Timer] = [:]
Expand Down Expand Up @@ -229,9 +238,9 @@ enum HUDType {

var iconNSColor: NSColor {
switch self {
case .brightness: return .systemYellow
case .volume, .volumeMuted: return .systemBlue
case .contrast: return .systemGray
case .brightness: return .white.withAlphaComponent(0.9)
case .volume, .volumeMuted: return .white.withAlphaComponent(0.9)
case .contrast: return .white.withAlphaComponent(0.8)
}
}
}
30 changes: 20 additions & 10 deletions MonitorControl/Support/SliderHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class SliderHandler {
let offsetY: CGFloat = -1.5

let tickMarkKnobExtraInset: CGFloat = 4
let tickMarkKnobExtraRadiusMultiplier: CGFloat = 0.25
let tickMarkKnobExtraRadiusMultiplier: CGFloat = 0.75

var numOfTickmarks: Int = 0
var isHighlightDisplayItems: Bool = false
Expand Down Expand Up @@ -226,7 +226,7 @@ class SliderHandler {
if #available(macOS 12.0, *) {
let paletteColor: NSColor
switch command {
case .brightness: paletteColor = .systemYellow
case .brightness: paletteColor = .labelColor.withAlphaComponent(0.85)
default: paletteColor = .labelColor.withAlphaComponent(0.72)
}
let palette = NSImage.SymbolConfiguration(paletteColors: [paletteColor])
Expand All @@ -247,13 +247,23 @@ class SliderHandler {
}
self.slider = slider
if !DEBUG_MACOS10, #available(macOS 11.0, *) {
slider.frame.size.width = 196
let iconSize: CGFloat = 18
let iconPadding: CGFloat = 10
slider.frame.size.width = 180
var sliderFrame = slider.frame
sliderFrame.size.height = max(sliderFrame.size.height, 22)
slider.frame = sliderFrame
slider.frame.origin = NSPoint(x: 15, y: 8)
let view = NSView(frame: NSRect(x: 0, y: 0, width: slider.frame.width + 30 + (showPercent ? 38 : 0), height: slider.frame.height + 42))

// Horizontal layout: Icon then Slider
let iconX: CGFloat = 15
let sliderX = iconX + iconSize + iconPadding
slider.frame.origin = NSPoint(x: sliderX, y: 8)

let viewWidth = sliderX + slider.frame.width + 15 + (showPercent ? 38 : 0)
let viewHeight = slider.frame.height + 16
let view = NSView(frame: NSRect(x: 0, y: 0, width: viewWidth, height: viewHeight))
view.frame.origin = NSPoint(x: 12, y: 0)

var iconName = "circle.dashed"
switch command {
case .audioSpeakerVolume: iconName = "speaker.wave.2.fill"
Expand All @@ -262,23 +272,23 @@ class SliderHandler {
default: break
}
let icon = SliderHandler.ClickThroughImageView()
let iconSize: CGFloat = 18
icon.image = SliderHandler.menuSymbolImage(named: iconName, pointSize: iconSize, command: command)
icon.imageScaling = .scaleProportionallyDown
if #available(macOS 12.0, *) {
icon.contentTintColor = nil
} else {
icon.contentTintColor = command == .brightness ? NSColor.systemYellow : NSColor.labelColor.withAlphaComponent(0.72)
icon.contentTintColor = .labelColor.withAlphaComponent(0.72)
}
// Position icon at horizontal left (start), 8px above slider
icon.frame = NSRect(x: slider.frame.origin.x, y: slider.frame.origin.y + slider.frame.height + 8, width: iconSize, height: iconSize)

// Position icon to the left of the slider, vertically centered
icon.frame = NSRect(x: iconX, y: slider.frame.origin.y + (slider.frame.height - iconSize) / 2, width: iconSize, height: iconSize)
icon.imageAlignment = .alignCenter
icon.configureForMenuSymbol()
view.addSubview(slider)
view.addSubview(icon)
self.icon = icon
if showPercent {
let percentageBox = NSTextField(frame: NSRect(x: 15 + slider.frame.size.width - 2, y: slider.frame.origin.y + (slider.frame.height - 12) / 2, width: 40, height: 12))
let percentageBox = NSTextField(frame: NSRect(x: sliderX + slider.frame.size.width + 2, y: slider.frame.origin.y + (slider.frame.height - 12) / 2, width: 40, height: 12))
self.setupPercentageBox(percentageBox)
self.percentageBox = percentageBox
view.addSubview(percentageBox)
Expand Down