diff --git a/src/api-wrappers/HelperExtensions.swift b/src/api-wrappers/HelperExtensions.swift index fa608d0e..293e144a 100644 --- a/src/api-wrappers/HelperExtensions.swift +++ b/src/api-wrappers/HelperExtensions.swift @@ -203,27 +203,9 @@ extension NSTextField { } extension NSImage { - func cgImage(maxSize: NSSize) -> CGImage { - // some images like NSRunningApp.icon are from icns. They hosts multiple representations and it's hard to know the highest resolution - // by setting a maxSize, the returned CGImage will be the biggest it can under that maxSize - let ctx = CGContext( - data: nil, - width: Int(maxSize.width), - height: Int(maxSize.height), - bitsPerComponent: 8, - bytesPerRow: 0, - space: CGColorSpaceCreateDeviceRGB(), - bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue - )! - NSGraphicsContext.current = NSGraphicsContext(cgContext: ctx, flipped: false) - self.draw( - in: CGRect(origin: .zero, size: maxSize), - from: .zero, - operation: .copy, - fraction: 1.0 - ) - NSGraphicsContext.current = nil - return ctx.makeImage()! + func appIconFixedSize(_ size: NSSize) -> CGImage? { + var rect = NSRect(origin: .zero, size: size) + return cgImage(forProposedRect: &rect, context: nil, hints: nil) } // NSImage(named) caches/reuses NSImage objects; we force separate instances of images by using copy() diff --git a/src/logic/Appearance.swift b/src/logic/Appearance.swift index e133ef64..c9a82100 100644 --- a/src/logic/Appearance.swift +++ b/src/logic/Appearance.swift @@ -3,16 +3,17 @@ import Cocoa class Appearance { // size static var hideThumbnails = Bool(false) - static var windowPadding = CGFloat(18) - static var windowCornerRadius = CGFloat(23) - static var cellCornerRadius = CGFloat(10) - static var edgeInsetsSize = CGFloat(5) - static var maxWidthOnScreen = CGFloat(0.8) - static var rowsCount = CGFloat(0) - static var iconSize = CGFloat(0) - static var fontHeight = CGFloat(0) - static var windowMinWidthInRow = CGFloat(0) - static var windowMaxWidthInRow = CGFloat(0) + static var windowPadding = CGFloat(1000) + static var windowCornerRadius = CGFloat(1000) + static var cellCornerRadius = CGFloat(1000) + static var edgeInsetsSize = CGFloat(1000) + static var maxWidthOnScreen = CGFloat(1000) + static var rowsCount = CGFloat(1000) + static var iconSize = CGFloat(1000) + static var fontHeight = CGFloat(1000) + static var windowMinWidthInRow = CGFloat(1000) + static var windowMaxWidthInRow = CGFloat(1000) + // size: constants static let maxHeightOnScreen = CGFloat(0.8) static let interCellPadding = CGFloat(1) @@ -20,40 +21,36 @@ class Appearance { static let appIconLabelSpacing = CGFloat(2) // theme - static var fontColor = NSColor.white - static var indicatedIconShadowColor: NSColor? = .darkGray - static var titleShadowColor: NSColor? = .darkGray - static var highlightMaterial = NSVisualEffectView.Material.selection - static var material = NSVisualEffectView.Material.dark - static var imageShadowColor: NSColor? = .gray // for icon, thumbnail and windowless images - static var highlightFocusedAlphaValue = 1.0 - static var highlightHoveredAlphaValue = 0.8 - static var highlightFocusedBackgroundColor = NSColor.black.withAlphaComponent(0.5) - static var highlightHoveredBackgroundColor = NSColor.black.withAlphaComponent(0.3) - static var highlightFocusedBorderColor = NSColor.clear - static var highlightHoveredBorderColor = NSColor.clear - static var highlightBorderShadowColor = NSColor.clear - static var highlightBorderWidth = CGFloat(0) - static var enablePanelShadow = false + static var fontColor = NSColor.red + static var imagesShadowColor = NSColor.red // for icon, thumbnail and windowless images + static var material = NSVisualEffectView.Material.ultraDark + static var highlightBorderWidth = CGFloat(3) + + // theme: constants + static let enablePanelShadow = true + static let highlightFocusedBackgroundColor = NSColor.systemAccentColor.withAlphaComponent(0.2) + static let highlightHoveredBackgroundColor = NSColor.systemAccentColor.withAlphaComponent(0.1) + static let highlightFocusedBorderColor = NSColor.systemAccentColor + static let highlightHoveredBorderColor = NSColor.systemAccentColor.withAlphaComponent(0.7) + // derived static var font: NSFont { if #available(macOS 26.0, *) { - return NSFont.systemFont(ofSize: fontHeight, weight: .medium) + return NSFont.systemFont(ofSize: fontHeight, weight: .semibold) } return NSFont.systemFont(ofSize: fontHeight) } private static var currentStyle: AppearanceStylePreference { Preferences.appearanceStyle } private static var currentSize: AppearanceSizePreference { Preferences.appearanceSize } - private static var currentTheme: AppearanceThemePreference { + static var currentTheme: AppearanceThemePreference { if Preferences.appearanceTheme == .system { return NSAppearance.current.getThemeName() } else { return Preferences.appearanceTheme } } - private static var currentVisibility: AppearanceVisibilityPreference { Preferences.appearanceVisibility } static func update() { updateSize() @@ -78,10 +75,11 @@ class Appearance { } else { lightTheme() } - // for Liquid Glass, we don't want a shadow around the panel - if #available(macOS 26.0, *), currentStyle == .appIcons && LiquidGlassEffectView.canUsePrivateLiquidGlassLook() { - enablePanelShadow = false - } + highlightBorderWidth = currentStyle == .titles ? 2 : 3 + // // for Liquid Glass, we don't want a shadow around the panel + // if #available(macOS 26.0, *), currentStyle == .appIcons && LiquidGlassEffectView.canUsePrivateLiquidGlassLook() { + // enablePanelShadow = false + // } } private static func thumbnailsSize(_ isHorizontalScreen: Bool) { @@ -90,7 +88,6 @@ class Appearance { windowCornerRadius = 23 cellCornerRadius = 10 edgeInsetsSize = 12 - maxWidthOnScreen = 0.8 if #available(macOS 26.0, *) { windowPadding = 28 windowCornerRadius = 43 @@ -99,23 +96,19 @@ class Appearance { switch currentSize { case .small: rowsCount = isHorizontalScreen ? 5 : 8 - iconSize = 20 - fontHeight = 12 + iconSize = 16 + fontHeight = 13 case .medium: rowsCount = isHorizontalScreen ? 4 : 7 - iconSize = 30 - fontHeight = 13 + iconSize = 26 + fontHeight = 14 case .large: rowsCount = isHorizontalScreen ? 3 : 6 - iconSize = 32 + iconSize = 28 fontHeight = 16 } let thumbnailsPanelRatio = (NSScreen.preferred.frame.width * maxWidthOnScreen) / (NSScreen.preferred.frame.height * maxHeightOnScreen) (windowMinWidthInRow, windowMaxWidthInRow) = AppearanceTestable.goodValuesForThumbnailsWidthMinMax(thumbnailsPanelRatio, rowsCount) - if currentVisibility == .highest { - edgeInsetsSize = 10 - cellCornerRadius = 12 - } } private static func appIconsSize() { @@ -127,32 +120,31 @@ class Appearance { if #available(macOS 26.0, *) { edgeInsetsSize = 6 } - maxWidthOnScreen = 0.8 windowMinWidthInRow = 0.04 windowMaxWidthInRow = 0.3 rowsCount = 1 switch currentSize { case .small: - iconSize = 88 + iconSize = 70 fontHeight = 13 if #available(macOS 26.0, *) { windowCornerRadius = 50 - cellCornerRadius = 28 + cellCornerRadius = 24 } case .medium: - iconSize = 128 - fontHeight = 15 + iconSize = 110 + fontHeight = 14 if #available(macOS 26.0, *) { windowCornerRadius = 55 - cellCornerRadius = 38 + cellCornerRadius = 35 } case .large: windowPadding = 28 - iconSize = 168 - fontHeight = 17 + iconSize = 150 + fontHeight = 16 if #available(macOS 26.0, *) { windowCornerRadius = 75 - cellCornerRadius = 48 + cellCornerRadius = 45 } } } @@ -169,102 +161,26 @@ class Appearance { rowsCount = 1 switch currentSize { case .small: - iconSize = 20 + iconSize = 18 fontHeight = 13 case .medium: - iconSize = 26 + iconSize = 24 fontHeight = 14 case .large: - iconSize = 32 + iconSize = 22 fontHeight = 16 } } private static func lightTheme() { fontColor = .black.withAlphaComponent(0.8) - indicatedIconShadowColor = nil - titleShadowColor = nil - highlightMaterial = .mediumLight - imageShadowColor = .lightGray.withAlphaComponent(0.4) - switch currentVisibility { - case .normal: - material = .light - highlightFocusedBackgroundColor = .lightGray.withAlphaComponent(0.7) - highlightHoveredBackgroundColor = .lightGray.withAlphaComponent(0.5) - enablePanelShadow = false - highlightFocusedAlphaValue = 1.0 - highlightHoveredAlphaValue = 0.8 - highlightFocusedBorderColor = NSColor.clear - highlightHoveredBorderColor = NSColor.clear - highlightBorderShadowColor = NSColor.clear - highlightBorderWidth = 0 - case .high: - material = .mediumLight - highlightFocusedBackgroundColor = .lightGray.withAlphaComponent(0.7) - highlightHoveredBackgroundColor = .lightGray.withAlphaComponent(0.5) - enablePanelShadow = true - highlightFocusedAlphaValue = 1.0 - highlightHoveredAlphaValue = 0.8 - highlightFocusedBorderColor = .lightGray.withAlphaComponent(0.9) - highlightHoveredBorderColor = .lightGray.withAlphaComponent(0.8) - highlightBorderShadowColor = .black.withAlphaComponent(0.5) - highlightBorderWidth = 1 - case .highest: - material = .mediumLight - highlightFocusedBackgroundColor = .lightGray.withAlphaComponent(0.4) - highlightHoveredBackgroundColor = .lightGray.withAlphaComponent(0.3) - enablePanelShadow = true - highlightFocusedAlphaValue = 0.4 - highlightHoveredAlphaValue = 0.2 - highlightFocusedBorderColor = NSColor.systemAccentColor - highlightHoveredBorderColor = NSColor.systemAccentColor.withAlphaComponent(0.8) - highlightBorderShadowColor = .black.withAlphaComponent(0.5) - highlightBorderWidth = currentStyle == .titles ? 2 : 4 - } + imagesShadowColor = .gray.withAlphaComponent(0.8) + material = .mediumLight } private static func darkTheme() { - fontColor = .white.withAlphaComponent(0.9) - indicatedIconShadowColor = .darkGray - titleShadowColor = .darkGray - highlightMaterial = .ultraDark - switch currentVisibility { - case .normal: - material = .dark - imageShadowColor = .gray.withAlphaComponent(0.8) - highlightFocusedBackgroundColor = .black.withAlphaComponent(0.6) - highlightHoveredBackgroundColor = .black.withAlphaComponent(0.5) - enablePanelShadow = false - highlightFocusedAlphaValue = 1.0 - highlightHoveredAlphaValue = 0.8 - highlightFocusedBorderColor = NSColor.clear - highlightHoveredBorderColor = NSColor.clear - highlightBorderShadowColor = NSColor.clear - highlightBorderWidth = 0 - case .high: - material = .ultraDark - imageShadowColor = .gray.withAlphaComponent(0.4) - highlightFocusedBackgroundColor = .gray.withAlphaComponent(0.6) - highlightHoveredBackgroundColor = .gray.withAlphaComponent(0.4) - enablePanelShadow = true - highlightFocusedAlphaValue = 1.0 - highlightHoveredAlphaValue = 0.8 - highlightFocusedBorderColor = .gray.withAlphaComponent(0.8) - highlightHoveredBorderColor = .gray.withAlphaComponent(0.7) - highlightBorderShadowColor = .white.withAlphaComponent(0.5) - highlightBorderWidth = 1 - case .highest: - material = .ultraDark - imageShadowColor = .gray.withAlphaComponent(0.4) - highlightFocusedBackgroundColor = .black.withAlphaComponent(0.4) - highlightHoveredBackgroundColor = .black.withAlphaComponent(0.2) - enablePanelShadow = true - highlightFocusedAlphaValue = 0.4 - highlightHoveredAlphaValue = 0.2 - highlightFocusedBorderColor = NSColor.systemAccentColor - highlightHoveredBorderColor = NSColor.systemAccentColor.withAlphaComponent(0.8) - highlightBorderShadowColor = .white.withAlphaComponent(0.5) - highlightBorderWidth = currentStyle == .titles ? 2 : 4 - } + fontColor = .white.withAlphaComponent(0.85) + imagesShadowColor = .gray.withAlphaComponent(0.8) + material = .dark } } diff --git a/src/logic/Application.swift b/src/logic/Application.swift index 339b2bd2..8a8ef70a 100644 --- a/src/logic/Application.swift +++ b/src/logic/Application.swift @@ -33,13 +33,13 @@ class Application: NSObject { static func appIconWithoutPadding(_ icon: NSImage?) -> CGImage? { guard let icon else { return nil } // we can render the icon quite big (e.g. windowless app icon), so we store it high-res - let iconSize = CGFloat(1024) + let iconWidth = CGFloat(1024) // NSRunningApplication.icon returns icons with padding; we remove it manually let paddingToRemove = CGFloat(84) - let croppedSize = iconSize - paddingToRemove * 2 + let croppedSize = iconWidth - paddingToRemove * 2 return icon - .cgImage(maxSize: NSSize(width: iconSize, height: iconSize)) - .cropping(to: CGRect(x: paddingToRemove, y: paddingToRemove, width: croppedSize, height: croppedSize).integral)! + .appIconFixedSize(NSSize(width: iconWidth, height: iconWidth))? + .cropping(to: CGRect(x: paddingToRemove, y: paddingToRemove, width: croppedSize, height: croppedSize).integral) } init(_ runningApplication: NSRunningApplication) { diff --git a/src/logic/MacroPreferences.swift b/src/logic/MacroPreferences.swift index 0ed30f60..1d7fda7a 100644 --- a/src/logic/MacroPreferences.swift +++ b/src/logic/MacroPreferences.swift @@ -484,28 +484,6 @@ enum AppearanceThemePreference: CaseIterable, SfSymbolMacroPreference { } } -enum AppearanceVisibilityPreference: CaseIterable, SfSymbolMacroPreference { - case normal - case high - case highest - - var localizedString: LocalizedString { - switch self { - case .normal: return NSLocalizedString("Normal", comment: "") - case .high: return NSLocalizedString("High", comment: "") - case .highest: return NSLocalizedString("Highest", comment: "") - } - } - - var symbolName: String { - switch self { - case .normal: return "eye" - case .high: return "eyeglasses" - case .highest: return "binoculars.fill" - } - } -} - enum UpdatePolicyPreference: CaseIterable, MacroPreference { case manual case autoCheck diff --git a/src/logic/Preferences.swift b/src/logic/Preferences.swift index de5f4900..6ead5706 100644 --- a/src/logic/Preferences.swift +++ b/src/logic/Preferences.swift @@ -50,7 +50,6 @@ class Preferences { "appearanceStyle": AppearanceStylePreference.thumbnails.indexAsString, "appearanceSize": AppearanceSizePreference.medium.indexAsString, "appearanceTheme": AppearanceThemePreference.system.indexAsString, - "appearanceVisibility": AppearanceVisibilityPreference.normal.indexAsString, "theme": ThemePreference.macOs.indexAsString, "showOnScreen": ShowOnScreenPreference.active.indexAsString, "titleTruncation": TitleTruncationPreference.end.indexAsString, @@ -129,7 +128,6 @@ class Preferences { static var appearanceStyle: AppearanceStylePreference { CachedUserDefaults.macroPref("appearanceStyle", AppearanceStylePreference.allCases) } static var appearanceSize: AppearanceSizePreference { CachedUserDefaults.macroPref("appearanceSize", AppearanceSizePreference.allCases) } static var appearanceTheme: AppearanceThemePreference { CachedUserDefaults.macroPref("appearanceTheme", AppearanceThemePreference.allCases) } - static var appearanceVisibility: AppearanceVisibilityPreference { CachedUserDefaults.macroPref("appearanceVisibility", AppearanceVisibilityPreference.allCases) } // periphery:ignore static var theme: ThemePreference { ThemePreference.macOs/*CachedUserDefaults.macroPref("theme", ThemePreference.allCases)*/ } static var showOnScreen: ShowOnScreenPreference { CachedUserDefaults.macroPref("showOnScreen", ShowOnScreenPreference.allCases) } diff --git a/src/ui/main-window/ThumbnailFontIconView.swift b/src/ui/main-window/ThumbnailFontIconView.swift index 6f9cbc44..29967087 100644 --- a/src/ui/main-window/ThumbnailFontIconView.swift +++ b/src/ui/main-window/ThumbnailFontIconView.swift @@ -25,11 +25,9 @@ class ThumbnailFontIconView: ThumbnailTitleView { }() var initialAttributedString: NSMutableAttributedString! - convenience init(symbol: Symbols, tooltip: String? = nil, size: CGFloat = Appearance.fontHeight, - color: NSColor = Appearance.fontColor, - shadow: NSShadow? = ThumbnailView.makeShadow(Appearance.indicatedIconShadowColor)) { + convenience init(symbol: Symbols, tooltip: String? = nil, size: CGFloat = Appearance.fontHeight, color: NSColor = Appearance.fontColor) { // This helps SF symbols display vertically centered and not clipped at the top - self.init(shadow: shadow, font: NSFont(name: "SF Pro Text", size: (size * 0.85).rounded())!) + self.init(font: NSFont(name: "SF Pro Text", size: (size * 0.85).rounded())!) initialAttributedString = NSMutableAttributedString(string: symbol.rawValue, attributes: [.paragraphStyle: ThumbnailFontIconView.paragraphStyle]) attributedStringValue = initialAttributedString textColor = color @@ -72,7 +70,7 @@ class ThumbnailFilledFontIconView: NSView { convenience init(_ thumbnailFontIconView: ThumbnailFontIconView, backgroundColor: NSColor, size: CGFloat) { self.init(frame: .zero) translatesAutoresizingMaskIntoConstraints = false - let backgroundView = ThumbnailFontIconView(symbol: .filledCircled, size: size - 4, color: backgroundColor, shadow: nil) + let backgroundView = ThumbnailFontIconView(symbol: .filledCircled, size: size - 3, color: backgroundColor) addSubview(backgroundView) addSubview(thumbnailFontIconView, positioned: .above, relativeTo: nil) backgroundView.centerXAnchor.constraint(equalTo: thumbnailFontIconView.centerXAnchor).isActive = true diff --git a/src/ui/main-window/ThumbnailTitleView.swift b/src/ui/main-window/ThumbnailTitleView.swift index d05055c3..62877717 100644 --- a/src/ui/main-window/ThumbnailTitleView.swift +++ b/src/ui/main-window/ThumbnailTitleView.swift @@ -1,11 +1,10 @@ import Cocoa class ThumbnailTitleView: NSTextField { - convenience init(shadow: NSShadow?, font: NSFont) { + convenience init(font: NSFont) { self.init(labelWithString: "") self.font = font textColor = Appearance.fontColor - self.shadow = shadow allowsDefaultTighteningForTruncation = false translatesAutoresizingMaskIntoConstraints = false } diff --git a/src/ui/main-window/ThumbnailView.swift b/src/ui/main-window/ThumbnailView.swift index 9021812d..845e3bda 100644 --- a/src/ui/main-window/ThumbnailView.swift +++ b/src/ui/main-window/ThumbnailView.swift @@ -7,13 +7,13 @@ class ThumbnailView: FlippedView { var windowlessIcon = LightImageView() let thumbnailContainer = FlippedView() var appIcon = LightImageView() - var label = ThumbnailTitleView(shadow: ThumbnailView.makeShadow(Appearance.titleShadowColor), font: Appearance.font) + var label = ThumbnailTitleView(font: Appearance.font) var fullscreenIcon = ThumbnailFontIconView(symbol: .circledPlusSign, tooltip: NSLocalizedString("Window is fullscreen", comment: "")) var minimizedIcon = ThumbnailFontIconView(symbol: .circledMinusSign, tooltip: NSLocalizedString("Window is minimized", comment: "")) var hiddenIcon = ThumbnailFontIconView(symbol: .circledSlashSign, tooltip: NSLocalizedString("App is hidden", comment: "")) var spaceIcon = ThumbnailFontIconView(symbol: .circledNumber0) var dockLabelIcon = ThumbnailFilledFontIconView( - ThumbnailFontIconView(symbol: .filledCircledNumber0, size: dockLabelLabelSize(), color: NSColor(srgbRed: 1, green: 0.30, blue: 0.25, alpha: 1), shadow: nil), + ThumbnailFontIconView(symbol: .filledCircledNumber0, size: dockLabelLabelSize(), color: NSColor(srgbRed: 1, green: 0.30, blue: 0.25, alpha: 1)), backgroundColor: NSColor.white, size: dockLabelLabelSize()) var quitIcon = TrafficLightButton(.quit, NSLocalizedString("Quit app", comment: "")) var closeIcon = TrafficLightButton(.close, NSLocalizedString("Close window", comment: "")) @@ -134,7 +134,6 @@ class ThumbnailView: FlippedView { let isHovered = indexInRecycledViews == Windows.hoveredWindowIndex setBackground(isFocused: isFocused, isHovered: isHovered) setBorder(isFocused: isFocused, isHovered: isHovered) - setShadow(isFocused: isFocused, isHovered: isHovered) if Preferences.appearanceStyle == .appIcons { label.isHidden = !(isFocused || isHovered) updateAppIconsLabel(isFocused: isFocused, isHovered: isHovered) @@ -181,12 +180,13 @@ class ThumbnailView: FlippedView { } private func setupSharedSubviews() { - let shadow = ThumbnailView.makeShadow(Appearance.imageShadowColor) + let shadow = ThumbnailView.makeShadow(Appearance.imagesShadowColor) thumbnailContainer.wantsLayer = true thumbnailContainer.layer!.masksToBounds = false // let thumbnail shadows show thumbnail.shadow = shadow windowlessIcon.shadow = shadow appIcon.shadow = shadow + dockLabelIcon.shadow = shadow windowlessIcon.translatesAutoresizingMaskIntoConstraints = false windowlessIcon.toolTip = ThumbnailView.noOpenWindowToolTip appIcon.translatesAutoresizingMaskIntoConstraints = false @@ -250,15 +250,6 @@ class ThumbnailView: FlippedView { } } - private func setShadow(isFocused: Bool, isHovered: Bool) { - if (isFocused || isHovered) && Appearance.highlightBorderShadowColor != .clear { - vStackView.layer!.shadowColor = Appearance.highlightBorderShadowColor.cgColor - vStackView.layer!.shadowOpacity = 0.25 - vStackView.layer!.shadowOffset = .zero - vStackView.layer!.shadowRadius = 1 - } - } - private func updateAppIconsLabel(isFocused: Bool, isHovered: Bool) { let focusedView = ThumbnailsView.recycledViews[Windows.focusedWindowIndex] var hoveredView: ThumbnailView? = nil @@ -285,19 +276,22 @@ class ThumbnailView: FlippedView { let availableRightWidth = view.isLastInRow ? 0 : CGFloat(view.numberOfViewsInRow - 1 - view.indexInRow) * viewWidth let totalWidth = availableLeftWidth + availableRightWidth + viewWidth let maxLabelWidth = min(totalWidth, maxAllowedWidth) - return maxLabelWidth + return maxLabelWidth - Appearance.intraCellPadding * 4 } private func updateAppIconsLabelFrame(_ view: ThumbnailView) { let viewWidth = view.frame.width - let labelWidth = view.label.cell!.cellSize.width + let labelWidth = view.label.fittingSize.width let maxAllowedLabelWidth = getMaxAllowedLabelWidth(view) let effectiveLabelWidth = max(min(labelWidth, maxAllowedLabelWidth), viewWidth) var leftOffset = CGFloat(0) + var effectiveLabelWidth = max(min(labelWidth, maxAllowedLabelWidth), viewWidth) + var leftOffset = -Appearance.intraCellPadding * 2 if view.isFirstInRow && view.isLastInRow { - leftOffset = 0 + leftOffset = -Appearance.intraCellPadding * 2 + effectiveLabelWidth -= Appearance.intraCellPadding * 4 } else if view.isLastInRow { - leftOffset = max(0, effectiveLabelWidth - viewWidth) + leftOffset = max(0, effectiveLabelWidth - viewWidth + Appearance.intraCellPadding * 2) } else if !view.isFirstInRow && !view.isLastInRow { let halfNeededOffset = max(0, (effectiveLabelWidth - viewWidth) / 2) let availableLeftWidth = view.isFirstInRow ? 0 : CGFloat(view.indexInRow) * viewWidth @@ -445,11 +439,36 @@ class ThumbnailView: FlippedView { windowlessAppIndicator.frame.origin.y = windowlessAppIndicator.superview!.frame.height - windowlessAppIndicator.frame.height + 5 } // we set dockLabelIcon origin, without checking if .isHidden - // This is because its updated async. We needed it positioned correctly always - let iconSize = ThumbnailView.iconSize() - dockLabelIcon.frame.origin.x = ((iconSize.width * 0.8) - (dockLabelIcon.fittingSize.width / 2)).rounded() - dockLabelIcon.frame.origin.y = ((iconSize.height * 0.8) - (dockLabelIcon.fittingSize.height / 2)).rounded() - - (Preferences.appearanceSize == .small ? 2 : 0) + // This is because its updated async. We need it positioned correctly always + let (offsetX, offsetY) = dockLabelOffset() + dockLabelIcon.frame.origin.x = appIcon.frame.maxX - (dockLabelIcon.fittingSize.width * offsetX).rounded() + dockLabelIcon.frame.origin.y = appIcon.frame.maxY - (dockLabelIcon.fittingSize.height * offsetY).rounded() + } + + /// positioning the dock label is messy because it's an NSTextField so it's visual size doesn't match what we can through APIs + // TODO: remove this; find a better way + private func dockLabelOffset() -> (CGFloat, CGFloat) { + var offsetX = 0.6 + if Preferences.appearanceStyle == .appIcons { + if Preferences.appearanceSize == .small { + offsetX = 0.82 + } else if Preferences.appearanceSize == .medium { + offsetX = 0.86 + } else { // .large + offsetX = 0.92 + } + } + var offsetY = offsetX + if Preferences.appearanceStyle == .appIcons { + if Preferences.appearanceSize == .small { + offsetY = 0.88 + } else if Preferences.appearanceSize == .medium { + offsetY = 0.90 + } else { // .large + offsetY = 0.92 + } + } + return (offsetX, offsetY) } private func indicatorsSpace() -> CGFloat { diff --git a/src/ui/main-window/ThumbnailsPanel.swift b/src/ui/main-window/ThumbnailsPanel.swift index f39cc627..07b5ddf9 100644 --- a/src/ui/main-window/ThumbnailsPanel.swift +++ b/src/ui/main-window/ThumbnailsPanel.swift @@ -10,7 +10,6 @@ class ThumbnailsPanel: NSPanel { isFloatingPanel = true animationBehavior = .none hidesOnDeactivate = false - hasShadow = Appearance.enablePanelShadow titleVisibility = .hidden backgroundColor = .clear contentView! = thumbnailsView.contentView @@ -23,6 +22,12 @@ class ThumbnailsPanel: NSPanel { setAccessibilitySubrole(.unknown) // for VoiceOver setAccessibilityLabel(App.name) + updateAppearance() + } + + func updateAppearance() { + hasShadow = Appearance.enablePanelShadow + appearance = NSAppearance(named: Appearance.currentTheme == .dark ? .vibrantDark : .vibrantLight) } override func orderOut(_ sender: Any?) { @@ -37,7 +42,7 @@ class ThumbnailsPanel: NSPanel { } func show() { - hasShadow = Appearance.enablePanelShadow + updateAppearance() alphaValue = 1 makeKeyAndOrderFront(nil) MouseEvents.toggle(true) diff --git a/src/ui/main-window/ThumbnailsPanelBackgroundView.swift b/src/ui/main-window/ThumbnailsPanelBackgroundView.swift index 4123d662..d77c9e0d 100644 --- a/src/ui/main-window/ThumbnailsPanelBackgroundView.swift +++ b/src/ui/main-window/ThumbnailsPanelBackgroundView.swift @@ -8,10 +8,14 @@ class LiquidGlassEffectView: NSGlassEffectView, EffectView { return method != nil } - convenience init(_: Int?) { + convenience init(_ clear: Bool) { self.init() - style = .clear - safeSetVariant(3) + if clear { + style = .clear + safeSetVariant(3) + } else { + style = .regular + } updateAppearance() wantsLayer = true // without this, there are weird shadows around the corners @@ -70,16 +74,19 @@ protocol EffectView: NSView { } func makeAppropriateEffectView() -> EffectView { - if #available(macOS 26.0, *), Preferences.appearanceStyle == .appIcons, LiquidGlassEffectView.canUsePrivateLiquidGlassLook() { - Logger.error("liquid") - return LiquidGlassEffectView(nil) - } else { - if #available(macOS 26.0, *), Preferences.appearanceStyle == .appIcons { + if #available(macOS 26.0, *) { + if Preferences.appearanceStyle == .appIcons { + if LiquidGlassEffectView.canUsePrivateLiquidGlassLook() { + Logger.debug("Using LiquidGlassEffectView(true)") + return LiquidGlassEffectView(true) + } let os = ProcessInfo.processInfo.operatingSystemVersion let version = "\(os.majorVersion).\(os.minorVersion).\(os.patchVersion)" Logger.error("Private API set_variant is no longer available. macOS version: \(version))") } - Logger.error("frosted") - return FrostedGlassEffectView(nil) + Logger.debug("Using LiquidGlassEffectView(false)") + return LiquidGlassEffectView(false) } + Logger.debug("Using FrostedGlassEffectView(nil)") + return FrostedGlassEffectView(nil) } diff --git a/src/ui/preferences-window/LabelAndControl.swift b/src/ui/preferences-window/LabelAndControl.swift index e0577063..0a8bb88c 100644 --- a/src/ui/preferences-window/LabelAndControl.swift +++ b/src/ui/preferences-window/LabelAndControl.swift @@ -133,7 +133,7 @@ class LabelAndControl: NSObject { onClick: EventClosure? = nil, onMouseEntered: EventClosure? = nil, onMouseExited: EventClosure? = nil) -> ClickHoverImageView { - let imageView = ThumbnailFontIconView(symbol: .circledInfo, size: size, color: .labelColor, shadow: nil) + let imageView = ThumbnailFontIconView(symbol: .circledInfo, size: size, color: .labelColor) let view = ClickHoverImageView(infoCircle: imageView) view.onClick = onClick view.onMouseEntered = onMouseEntered @@ -225,7 +225,7 @@ class LabelAndControl: NSObject { } } - static func makeSegmentedControl(_ rawName: String, _ macroPreferences: [MacroPreference], extraAction: ActionClosure? = nil, segmentWidth: CGFloat = -1) -> NSSegmentedControl { + static func makeSegmentedControl(_ rawName: String, _ macroPreferences: [MacroPreference], segmentWidth: CGFloat = -1, extraAction: ActionClosure? = nil) -> NSSegmentedControl { let button = NSSegmentedControl(labels: macroPreferences.map { $0.localizedString }, trackingMode: .selectOne, target: nil, action: nil) @@ -313,7 +313,7 @@ class LabelAndControl: NSObject { } // some preferences require re-creating some components if (!(senderControl is NSSlider) || (NSEvent.pressedMouseButtons & (1 << 0)) == 0) && - (["appearanceStyle", "appearanceSize", "appearanceTheme", "appearanceVisibility", "showOnScreen", "showAppsOrWindows"].contains { (pref: String) -> Bool in + (["appearanceStyle", "appearanceSize", "appearanceTheme", "showOnScreen", "showAppsOrWindows"].contains { (pref: String) -> Bool in pref == senderControl.identifier!.rawValue }) { (App.shared as! App).resetPreferencesDependentComponents() diff --git a/src/ui/preferences-window/tabs/appearance/AppearanceTab.swift b/src/ui/preferences-window/tabs/appearance/AppearanceTab.swift index 61ec59ee..5be59ac5 100644 --- a/src/ui/preferences-window/tabs/appearance/AppearanceTab.swift +++ b/src/ui/preferences-window/tabs/appearance/AppearanceTab.swift @@ -397,8 +397,6 @@ class AppearanceTab: NSObject { rightViews: [LabelAndControl.makeSegmentedControl("appearanceSize", AppearanceSizePreference.allCases, segmentWidth: 100)]) table.addRow(leftText: NSLocalizedString("Theme", comment: ""), rightViews: [LabelAndControl.makeSegmentedControl("appearanceTheme", AppearanceThemePreference.allCases, segmentWidth: 100)]) - table.addRow(leftText: NSLocalizedString("Visibility", comment: ""), - rightViews: [LabelAndControl.makeSegmentedControl("appearanceVisibility", AppearanceVisibilityPreference.allCases, segmentWidth: 100)]) table.addRow(rightViews: customizeStyleButton) table.fit() return table