diff --git a/src/api-wrappers/HelperExtensions.swift b/src/api-wrappers/HelperExtensions.swift index d3f4b2e3..02f18658 100644 --- a/src/api-wrappers/HelperExtensions.swift +++ b/src/api-wrappers/HelperExtensions.swift @@ -193,15 +193,6 @@ extension DispatchQoS { } } -extension NSTextField { - func setWidth(_ width: CGFloat) { - frame.size.width = width - // TODO: NSTextField does some internal magic, and ends up with constraints. We need to add our own to force its size - // I wish there was a better way to only set the frame.size - addOrUpdateConstraint(widthAnchor, width) - } -} - extension NSImage { func appIconFixedSize(_ size: NSSize) -> CGImage? { var rect = NSRect(origin: .zero, size: size) diff --git a/src/logic/Appearance.swift b/src/logic/Appearance.swift index a559d55c..bb43e0b8 100644 --- a/src/logic/Appearance.swift +++ b/src/logic/Appearance.swift @@ -37,7 +37,7 @@ class Appearance { // derived static var font: NSFont { if #available(macOS 26.0, *) { - return NSFont.systemFont(ofSize: fontHeight, weight: .semibold) + return NSFont.systemFont(ofSize: fontHeight, weight: currentStyle == .appIcons ? .semibold : .medium) } return NSFont.systemFont(ofSize: fontHeight) } @@ -155,7 +155,6 @@ class Appearance { windowCornerRadius = 23 cellCornerRadius = 10 edgeInsetsSize = 7 - maxWidthOnScreen = isHorizontalScreen ? 0.6 : 0.8 windowMinWidthInRow = 0.6 windowMaxWidthInRow = 0.9 rowsCount = 1 diff --git a/src/ui/main-window/ThumbnailTitleView.swift b/src/ui/main-window/ThumbnailTitleView.swift index 49f0f4da..d1cbe41c 100644 --- a/src/ui/main-window/ThumbnailTitleView.swift +++ b/src/ui/main-window/ThumbnailTitleView.swift @@ -5,6 +5,8 @@ class ThumbnailTitleView: NSTextField { self.init(labelWithString: "") self.font = font textColor = Appearance.fontColor + // drawsBackground = true + // backgroundColor = .red allowsDefaultTighteningForTruncation = false translatesAutoresizingMaskIntoConstraints = false } @@ -13,6 +15,19 @@ class ThumbnailTitleView: NSTextField { heightAnchor.constraint(equalToConstant: fittingSize.height).isActive = true } + func setWidth(_ width: CGFloat) { + frame.size.width = width + // TODO: NSTextField does some internal magic, and ends up with constraints. + // we can't use addOrUpdateConstraint for some reason, otherwise it will only actually apply after the UI is shown twice + // i tried everything and ended up removing all constraints then adding a fresh one. This seems to work + let toRemove = constraints.filter { + ($0.firstItem as? NSView) === self && $0.firstAttribute == .width || + ($0.secondItem as? NSView) === self && $0.secondAttribute == .width + } + toRemove.forEach { removeConstraint($0) } + widthAnchor.constraint(equalToConstant: width).isActive = true + } + override func mouseMoved(with event: NSEvent) { // no-op here prevents tooltips from disappearing on mouseMoved } diff --git a/src/ui/main-window/ThumbnailView.swift b/src/ui/main-window/ThumbnailView.swift index 81692edd..92598b4b 100644 --- a/src/ui/main-window/ThumbnailView.swift +++ b/src/ui/main-window/ThumbnailView.swift @@ -2,6 +2,8 @@ import Cocoa class ThumbnailView: FlippedView { static let noOpenWindowToolTip = NSLocalizedString("App is running but has no open window", comment: "") + // when calculating the width of a nstextfield, somehow we need to add this suffix to get the correct width + static let extraTextForPadding = "lmnopqrstuvw" var window_: Window? var thumbnail = LightImageView() var windowlessIcon = LightImageView() @@ -276,20 +278,19 @@ 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 - Appearance.intraCellPadding * 4 + return maxLabelWidth - Appearance.intraCellPadding * 2 } private func updateAppIconsLabelFrame(_ view: ThumbnailView) { let viewWidth = view.frame.width - let labelWidth = view.label.fittingSize.width + let labelWidth = view.label.cell!.cellSize.width let maxAllowedLabelWidth = getMaxAllowedLabelWidth(view) let effectiveLabelWidth = max(min(labelWidth, maxAllowedLabelWidth), viewWidth) - var leftOffset = CGFloat(0) - var effectiveLabelWidth = max(min(labelWidth, maxAllowedLabelWidth), viewWidth) + - (view.isFirstInRow && view.isLastInRow ? 4 : (view.isFirstInRow || view.isLastInRow ? 2 : 0)) * Appearance.intraCellPadding var leftOffset = -Appearance.intraCellPadding * 2 if view.isFirstInRow && view.isLastInRow { leftOffset = -Appearance.intraCellPadding * 2 - effectiveLabelWidth -= Appearance.intraCellPadding * 4 + } else if view.isLastInRow { leftOffset = max(0, effectiveLabelWidth - viewWidth + Appearance.intraCellPadding * 2) } else if !view.isFirstInRow && !view.isLastInRow { @@ -547,6 +548,28 @@ class ThumbnailView: FlippedView { return ThumbnailsPanel.maxThumbnailsWidth() * Appearance.windowMaxWidthInRow - Appearance.interCellPadding * 2 } + static func widthOfComfortableReadability() -> CGFloat? { + let labTitleView = ThumbnailTitleView(font: Appearance.font) + labTitleView.stringValue = "abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz-abcdefghijk" + extraTextForPadding + Logger.error(labTitleView.font) + return labTitleView.fittingSize.width + } + + static func widthOfLongestTitle() -> CGFloat? { + let labTitleView = ThumbnailTitleView(font: Appearance.font) + var maxWidth = CGFloat(0) + for window in Windows.list { + guard window.shouldShowTheUser else { continue } + labTitleView.stringValue = window.title + extraTextForPadding + let width = labTitleView.fittingSize.width + if width > maxWidth { + maxWidth = width + } + } + guard maxWidth > 0 else { return nil } + return maxWidth + } + static func minThumbnailWidth() -> CGFloat { return ThumbnailsPanel.maxThumbnailsWidth() * Appearance.windowMinWidthInRow - Appearance.interCellPadding * 2 } diff --git a/src/ui/main-window/ThumbnailsPanel.swift b/src/ui/main-window/ThumbnailsPanel.swift index b150bf6d..6abaf434 100644 --- a/src/ui/main-window/ThumbnailsPanel.swift +++ b/src/ui/main-window/ThumbnailsPanel.swift @@ -61,6 +61,17 @@ class ThumbnailsPanel: NSPanel { } static func maxThumbnailsWidth() -> CGFloat { + if Preferences.appearanceStyle == .titles, + let readableWidth = ThumbnailView.widthOfComfortableReadability(), + let widthOfLongestTitle = ThumbnailView.widthOfLongestTitle() { + return ( + min( + NSScreen.preferred.frame.width * Appearance.maxWidthOnScreen, + readableWidth + Appearance.intraCellPadding * 2 + Appearance.appIconLabelSpacing + Appearance.iconSize, + widthOfLongestTitle + Appearance.intraCellPadding * 2 + Appearance.appIconLabelSpacing + Appearance.iconSize + ) - Appearance.windowPadding * 2 + ).rounded() + } return (NSScreen.preferred.frame.width * Appearance.maxWidthOnScreen - Appearance.windowPadding * 2).rounded() }