From a96f654948c0d5b1b75eb031327454635b519973 Mon Sep 17 00:00:00 2001 From: Abdul Rehman Date: Mon, 20 Apr 2026 17:58:34 +0500 Subject: [PATCH] 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 --- MonitorControl/Support/CustomHUD.swift | 21 ++++++++++----- MonitorControl/Support/SliderHandler.swift | 30 ++++++++++++++-------- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/MonitorControl/Support/CustomHUD.swift b/MonitorControl/Support/CustomHUD.swift index 7a18777..b2ea56d 100644 --- a/MonitorControl/Support/CustomHUD.swift +++ b/MonitorControl/Support/CustomHUD.swift @@ -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 ) @@ -96,6 +96,13 @@ 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 @@ -103,6 +110,8 @@ class CustomHUDManager { 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)) @@ -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) @@ -161,7 +170,7 @@ class CustomHUDManager { label.alignment = .right containerView.addSubview(label) - return containerView + return rootView } private var fadeTimers: [CGDirectDisplayID: Timer] = [:] @@ -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) } } } diff --git a/MonitorControl/Support/SliderHandler.swift b/MonitorControl/Support/SliderHandler.swift index 1c6679a..c14c7d3 100644 --- a/MonitorControl/Support/SliderHandler.swift +++ b/MonitorControl/Support/SliderHandler.swift @@ -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 @@ -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]) @@ -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" @@ -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)