feat: new preference to hide thumbnails (closes #384)
This commit is contained in:
1 parent
df3010a3c8
commit
877c93c8a2
9 files changed
+46
-37
No files matched your search
@@ -64,9 +64,9 @@ class Application: NSObject {
|
||||
|
||||
func addAndObserveWindows() {
|
||||
if runningApplication.isFinishedLaunching && runningApplication.activationPolicy != .prohibited && axUiElement == nil {
|
||||
axUiElement = AXUIElementCreateApplication(runningApplication.processIdentifier)
|
||||
AXObserverCreate(runningApplication.processIdentifier, axObserverCallback, &axObserver)
|
||||
debugPrint("Adding app", runningApplication.processIdentifier, runningApplication.bundleIdentifier ?? "nil")
|
||||
axUiElement = AXUIElementCreateApplication(pid)
|
||||
AXObserverCreate(pid, axObserverCallback, &axObserver)
|
||||
debugPrint("Adding app", pid, runningApplication.bundleIdentifier ?? "nil")
|
||||
observeEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ class Preferences {
|
||||
"shortcutStyle": "0",
|
||||
"hideAppBadges": "false",
|
||||
"hideWindowlessApps": "false",
|
||||
"hideThumbnails": "false",
|
||||
]
|
||||
|
||||
// constant values
|
||||
@@ -69,7 +70,6 @@ class Preferences {
|
||||
static var windowPadding: CGFloat { 18 }
|
||||
static var interCellPadding: CGFloat { 5 }
|
||||
static var intraCellPadding: CGFloat { 5 }
|
||||
static var fontIconSize: CGFloat { 20 }
|
||||
|
||||
// persisted values
|
||||
static var maxScreenUsage: CGFloat { defaults.cgfloat("maxScreenUsage") / CGFloat(100) }
|
||||
@@ -100,6 +100,7 @@ class Preferences {
|
||||
static var hideStatusIcons: Bool { defaults.bool("hideStatusIcons") }
|
||||
static var hideAppBadges: Bool { defaults.bool("hideAppBadges") }
|
||||
static var hideWindowlessApps: Bool { defaults.bool("hideWindowlessApps") }
|
||||
static var hideThumbnails: Bool { defaults.bool("hideThumbnails") }
|
||||
static var startAtLogin: Bool { defaults.bool("startAtLogin") }
|
||||
static var dontShowBlacklist: [String] { blacklistStringToArray(defaults.string("dontShowBlacklist")) }
|
||||
static var disableShortcutsBlacklist: [String] { blacklistStringToArray(defaults.string("disableShortcutsBlacklist")) }
|
||||
|
||||
@@ -152,7 +152,7 @@ class Window {
|
||||
}
|
||||
|
||||
func focus() {
|
||||
if application.runningApplication.processIdentifier == ProcessInfo.processInfo.processIdentifier {
|
||||
if application.pid == ProcessInfo.processInfo.processIdentifier {
|
||||
App.app.showSecondaryWindow(App.app.window(withWindowNumber: Int(cgWindowId)))
|
||||
} else if isWindowlessApp {
|
||||
if let bundleID = application.runningApplication.bundleIdentifier {
|
||||
|
||||
@@ -87,6 +87,7 @@ class Windows {
|
||||
}
|
||||
|
||||
static func refreshFirstFewThumbnailsSync() {
|
||||
if Preferences.hideThumbnails { return }
|
||||
list.filter { $0.shouldShowTheUser }
|
||||
.prefix(criticalFirstThumbnails)
|
||||
.forEachAsync { window in window.refreshThumbnail() }
|
||||
@@ -94,6 +95,7 @@ class Windows {
|
||||
|
||||
static func refreshThumbnailsAsync(_ screen: NSScreen, _ currentIndex: Int = criticalFirstThumbnails) {
|
||||
guard App.app.appIsBeingUsed else { return }
|
||||
if Preferences.hideThumbnails { return }
|
||||
BackgroundWork.mainQueueConcurrentWorkQueue.async {
|
||||
if currentIndex < list.count {
|
||||
let window = list[currentIndex]
|
||||
|
||||
@@ -27,11 +27,11 @@ class FontIcon: BaseLabel {
|
||||
// Font icon using SF Symbols from the SF Pro font from Apple
|
||||
// see https://developer.apple.com/design/human-interface-guidelines/sf-symbols/overview/
|
||||
class ThumbnailFontIconView: ThumbnailTitleView {
|
||||
convenience init(_ symbol: Symbols, _ size: CGFloat = Preferences.fontIconSize, _ color: NSColor = .white, _ shadow: NSShadow? = ThumbnailView.makeShadow(.darkGray)) {
|
||||
// This helps SF symbols display vertically centered and not clipped at the bottom
|
||||
self.init(size, 3, shadow: shadow)
|
||||
convenience init(_ symbol: Symbols, _ size: CGFloat = Preferences.fontHeight, _ color: NSColor = .white, _ shadow: NSShadow? = ThumbnailView.makeShadow(.darkGray)) {
|
||||
self.init(size, shadow)
|
||||
string = symbol.rawValue
|
||||
font = NSFont(name: "SF Pro Text", size: size)
|
||||
// This helps SF symbols display vertically centered and not clipped at the top
|
||||
font = NSFont(name: "SF Pro Text", size: (size * 0.85).rounded())
|
||||
textColor = color
|
||||
// This helps SF symbols not be clipped on the right
|
||||
widthAnchor.constraint(equalToConstant: size * 1.15).isActive = true
|
||||
@@ -62,15 +62,13 @@ class ThumbnailFontIconView: ThumbnailTitleView {
|
||||
}
|
||||
|
||||
class ThumbnailFilledFontIconView: NSView {
|
||||
convenience init(_ thumbnailFontIconView: ThumbnailFontIconView, _ backgroundColor: NSColor, _ offset: Bool = false) {
|
||||
convenience init(_ thumbnailFontIconView: ThumbnailFontIconView, _ backgroundColor: NSColor) {
|
||||
self.init(frame: .zero)
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
let backgroundView = ThumbnailFontIconView(.filledCircled, thumbnailFontIconView.font!.pointSize - (offset ? 2 : 0), backgroundColor)
|
||||
let backgroundView = ThumbnailFontIconView(.filledCircled, 14 - 2, backgroundColor, nil)
|
||||
addSubview(backgroundView)
|
||||
addSubview(thumbnailFontIconView, positioned: .above, relativeTo: nil)
|
||||
if offset {
|
||||
backgroundView.leftAnchor.constraint(equalTo: backgroundView.superview!.leftAnchor, constant: 1).isActive = true
|
||||
}
|
||||
backgroundView.frame.origin = CGPoint(x: backgroundView.frame.origin.x + 1, y: backgroundView.frame.origin.y + 1)
|
||||
fit(thumbnailFontIconView.fittingSize.width, thumbnailFontIconView.fittingSize.height)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
import Cocoa
|
||||
|
||||
class ThumbnailTitleView: BaseLabel {
|
||||
var magicOffset = CGFloat(0)
|
||||
|
||||
convenience init(_ size: CGFloat, _ magicOffset: CGFloat = 0, shadow: NSShadow? = ThumbnailView.makeShadow(.darkGray)) {
|
||||
convenience init(_ size: CGFloat, _ shadow: NSShadow? = ThumbnailView.makeShadow(.darkGray)) {
|
||||
let textStorage = NSTextStorage()
|
||||
let layoutManager = NSLayoutManager()
|
||||
textStorage.addLayoutManager(layoutManager)
|
||||
@@ -13,18 +11,17 @@ class ThumbnailTitleView: BaseLabel {
|
||||
layoutManager.addTextContainer(textContainer)
|
||||
self.init(NSRect.zero, textContainer)
|
||||
font = Preferences.font
|
||||
self.magicOffset = magicOffset
|
||||
textColor = Preferences.fontColor
|
||||
self.shadow = shadow
|
||||
defaultParagraphStyle = makeParagraphStyle(size)
|
||||
heightAnchor.constraint(equalToConstant: size + magicOffset).isActive = true
|
||||
heightAnchor.constraint(equalToConstant: size).isActive = true
|
||||
}
|
||||
|
||||
private func makeParagraphStyle(_ size: CGFloat) -> NSMutableParagraphStyle {
|
||||
let paragraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
|
||||
paragraphStyle.lineBreakMode = getTruncationMode()
|
||||
paragraphStyle.maximumLineHeight = size + magicOffset
|
||||
paragraphStyle.minimumLineHeight = size + magicOffset
|
||||
paragraphStyle.maximumLineHeight = size
|
||||
paragraphStyle.minimumLineHeight = size
|
||||
paragraphStyle.allowsDefaultTighteningForTruncation = false
|
||||
return paragraphStyle
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ class ThumbnailView: NSStackView {
|
||||
var minimizedIcon = ThumbnailFontIconView(.circledMinusSign)
|
||||
var hiddenIcon = ThumbnailFontIconView(.circledSlashSign)
|
||||
var spaceIcon = ThumbnailFontIconView(.circledNumber0)
|
||||
var dockLabelIcon = ThumbnailFilledFontIconView(ThumbnailFontIconView(.filledCircledNumber0, (Preferences.fontIconSize * 0.7).rounded(), NSColor(srgbRed: 1, green: 0.30, blue: 0.25, alpha: 1), nil), NSColor.white, true)
|
||||
var dockLabelIcon = ThumbnailFilledFontIconView(ThumbnailFontIconView(.filledCircledNumber0, 14, NSColor(srgbRed: 1, green: 0.30, blue: 0.25, alpha: 1), nil), NSColor.white)
|
||||
var closeIcon = WindowControlView("close", 16)
|
||||
var minimizeIcon = WindowControlView("minimize", 16)
|
||||
var maximizeIcon = WindowControlView("fullscreen", 16)
|
||||
@@ -36,12 +36,10 @@ class ThumbnailView: NSStackView {
|
||||
layer!.borderWidth = Preferences.cellBorderWidth
|
||||
edgeInsets = NSEdgeInsets(top: Preferences.intraCellPadding, left: Preferences.intraCellPadding, bottom: Preferences.intraCellPadding, right: Preferences.intraCellPadding)
|
||||
orientation = .vertical
|
||||
spacing = Preferences.intraCellPadding
|
||||
let shadow = ThumbnailView.makeShadow(.gray)
|
||||
thumbnail.shadow = shadow
|
||||
appIcon.shadow = shadow
|
||||
hStackView = NSStackView(views: [appIcon, label, hiddenIcon, fullscreenIcon, minimizedIcon, spaceIcon])
|
||||
hStackView.spacing = Preferences.intraCellPadding
|
||||
setViews([hStackView, thumbnail], in: .leading)
|
||||
addWindowControls()
|
||||
addDockLabelIcon()
|
||||
@@ -70,7 +68,7 @@ class ThumbnailView: NSStackView {
|
||||
if let shouldShowWindowControls = shouldShowWindowControls_ {
|
||||
self.shouldShowWindowControls = shouldShowWindowControls
|
||||
}
|
||||
let shouldShow = shouldShowWindowControls && isHighlighted && !Preferences.hideColoredCircles && !window_!.isWindowlessApp
|
||||
let shouldShow = shouldShowWindowControls && isHighlighted && !Preferences.hideColoredCircles && !window_!.isWindowlessApp && !Preferences.hideThumbnails
|
||||
if isShowingWindowControls != shouldShow {
|
||||
isShowingWindowControls = shouldShow
|
||||
[closeIcon, minimizeIcon, maximizeIcon].forEach { $0.isHidden = !shouldShow }
|
||||
@@ -101,14 +99,19 @@ class ThumbnailView: NSStackView {
|
||||
|
||||
func updateRecycledCellWithNewContent(_ element: Window, _ index: Int, _ newHeight: CGFloat, _ screen: NSScreen) {
|
||||
window_ = element
|
||||
thumbnail.image = element.thumbnail
|
||||
if let image = thumbnail.image {
|
||||
image.size = element.thumbnailFullSize!
|
||||
assignIfDifferent(&thumbnail.isHidden, Preferences.hideThumbnails)
|
||||
if !Preferences.hideThumbnails {
|
||||
thumbnail.image = element.thumbnail
|
||||
if let image = thumbnail.image {
|
||||
image.size = element.thumbnailFullSize!
|
||||
}
|
||||
let (thumbnailWidth, thumbnailHeight) = ThumbnailView.thumbnailSize(element.thumbnail, screen)
|
||||
let thumbnailSize = NSSize(width: thumbnailWidth.rounded(), height: thumbnailHeight.rounded())
|
||||
thumbnail.image?.size = thumbnailSize
|
||||
thumbnail.frame.size = thumbnailSize
|
||||
}
|
||||
let (thumbnailWidth, thumbnailHeight) = ThumbnailView.thumbnailSize(element.thumbnail, screen)
|
||||
let thumbnailSize = NSSize(width: thumbnailWidth.rounded(), height: thumbnailHeight.rounded())
|
||||
thumbnail.image?.size = thumbnailSize
|
||||
thumbnail.frame.size = thumbnailSize
|
||||
assignIfDifferent(&spacing, Preferences.hideThumbnails ? 0 : Preferences.intraCellPadding)
|
||||
assignIfDifferent(&hStackView.spacing, Preferences.fontHeight == 0 ? 0 : Preferences.intraCellPadding)
|
||||
if appIcon.image != element.icon {
|
||||
appIcon.image = element.icon
|
||||
let appIconSize = NSSize(width: Preferences.iconSize, height: Preferences.iconSize)
|
||||
@@ -142,11 +145,11 @@ class ThumbnailView: NSStackView {
|
||||
}
|
||||
dockLabelIcon.setFrameOrigin(NSPoint(x: appIcon.frame.maxX - dockLabelIcon.fittingSize.width - 1, y: appIcon.frame.maxY - dockLabelIcon.fittingSize.height + 4))
|
||||
}
|
||||
assignIfDifferent(&frame.size.width, max(thumbnail.frame.size.width + Preferences.intraCellPadding * 2, ThumbnailView.widthMin(screen)))
|
||||
assignIfDifferent(&frame.size.width, max((Preferences.hideThumbnails ? hStackView.fittingSize.width : thumbnail.frame.size.width) + Preferences.intraCellPadding * 2, ThumbnailView.widthMin(screen)))
|
||||
assignIfDifferent(&frame.size.height, newHeight)
|
||||
let fontIconWidth = CGFloat([fullscreenIcon, minimizedIcon, hiddenIcon, spaceIcon].filter { !$0.isHidden }.count) * (Preferences.fontIconSize + Preferences.intraCellPadding)
|
||||
let fontIconWidth = CGFloat([fullscreenIcon, minimizedIcon, hiddenIcon, spaceIcon].filter { !$0.isHidden }.count) * (Preferences.fontHeight + Preferences.intraCellPadding)
|
||||
assignIfDifferent(&label.textContainer!.size.width, frame.width - Preferences.iconSize - Preferences.intraCellPadding * 3 - fontIconWidth)
|
||||
assignIfDifferent(&windowlessIcon.isHidden, !element.isWindowlessApp)
|
||||
assignIfDifferent(&windowlessIcon.isHidden, !element.isWindowlessApp || Preferences.hideThumbnails)
|
||||
if element.isWindowlessApp {
|
||||
let maxWidth = (ThumbnailView.widthMin(screen) - Preferences.intraCellPadding * 2).rounded()
|
||||
let maxHeight = ((ThumbnailView.height(screen) - hStackView.fittingSize.height) - Preferences.intraCellPadding * 2).rounded()
|
||||
|
||||
@@ -51,8 +51,15 @@ class ThumbnailsView: NSVisualEffectView {
|
||||
}
|
||||
}
|
||||
|
||||
func rowHeight(_ screen: NSScreen) -> CGFloat {
|
||||
if Preferences.hideThumbnails {
|
||||
return max(Preferences.iconSize, Preferences.fontHeight + 3) + Preferences.intraCellPadding * 2
|
||||
}
|
||||
return ThumbnailView.height(screen).rounded(.down)
|
||||
}
|
||||
|
||||
private func layoutThumbnailViews(_ screen: NSScreen, _ widthMax: CGFloat) -> (CGFloat, CGFloat)? {
|
||||
let height = ThumbnailView.height(screen).rounded(.down)
|
||||
let height = rowHeight(screen)
|
||||
let isLeftToRight = App.shared.userInterfaceLayoutDirection == .leftToRight
|
||||
let startingX = isLeftToRight ? Preferences.interCellPadding : widthMax - Preferences.interCellPadding
|
||||
var currentX = startingX
|
||||
|
||||
@@ -9,7 +9,7 @@ class AppearanceTab {
|
||||
LabelAndControl.makeLabelWithSlider(NSLocalizedString("Rows of windows:", comment: ""), "rowsCount", 1, 20, 20, true),
|
||||
LabelAndControl.makeLabelWithSlider(NSLocalizedString("Min windows per row:", comment: ""), "minCellsPerRow", 1, 20, 20, true),
|
||||
LabelAndControl.makeLabelWithSlider(NSLocalizedString("Max windows per row:", comment: ""), "maxCellsPerRow", 1, 40, 20, true),
|
||||
LabelAndControl.makeLabelWithSlider(NSLocalizedString("Window app icon size:", comment: ""), "iconSize", 0, 64, 11, false, "px"),
|
||||
LabelAndControl.makeLabelWithSlider(NSLocalizedString("Window app icon size:", comment: ""), "iconSize", 0, 128, 11, false, "px"),
|
||||
LabelAndControl.makeLabelWithSlider(NSLocalizedString("Window title font size:", comment: ""), "fontHeight", 0, 64, 11, false, "px"),
|
||||
LabelAndControl.makeLabelWithDropdown(NSLocalizedString("Window title truncation:", comment: ""), "titleTruncation", TitleTruncationPreference.allCases),
|
||||
LabelAndControl.makeLabelWithDropdown(NSLocalizedString("Show on:", comment: ""), "showOnScreen", ShowOnScreenPreference.allCases),
|
||||
@@ -21,6 +21,7 @@ class AppearanceTab {
|
||||
LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Hide colored circles on mouse hover:", comment: ""), "hideColoredCircles"),
|
||||
LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Hide app badges:", comment: ""), "hideAppBadges"),
|
||||
LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Hide apps with no open window:", comment: ""), "hideWindowlessApps"),
|
||||
LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Hide window thumbnails:", comment: ""), "hideThumbnails"),
|
||||
])
|
||||
grid.column(at: 0).xPlacement = .trailing
|
||||
grid.rowAlignment = .lastBaseline
|
||||
|
||||
Reference in new issue
Block a user