diff --git a/resources/SF-Pro-Text-Regular.otf b/resources/SF-Pro-Text-Regular.otf index ba724c23..010702f5 100644 Binary files a/resources/SF-Pro-Text-Regular.otf and b/resources/SF-Pro-Text-Regular.otf differ diff --git a/resources/l10n/Localizable.strings b/resources/l10n/Localizable.strings index fe4e3830..8e745a4c 100644 --- a/resources/l10n/Localizable.strings +++ b/resources/l10n/Localizable.strings @@ -127,6 +127,9 @@ /* No comment provided by engineer. */ "Hidden" = "Hidden"; +/* No comment provided by engineer. */ +"Hide app badges:" = "Hide app badges:"; + /* No comment provided by engineer. */ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/resources/l10n/ar.lproj/Localizable.strings b/resources/l10n/ar.lproj/Localizable.strings index b8814d1c..c7360744 100644 --- a/resources/l10n/ar.lproj/Localizable.strings +++ b/resources/l10n/ar.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "مخفي"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/resources/l10n/de.lproj/Localizable.strings b/resources/l10n/de.lproj/Localizable.strings index 98bf2bf8..fa489bca 100644 --- a/resources/l10n/de.lproj/Localizable.strings +++ b/resources/l10n/de.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "Versteckt"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Bunte Kreise beim Markieren mit Maus verbergen:"; diff --git a/resources/l10n/en.lproj/Localizable.strings b/resources/l10n/en.lproj/Localizable.strings index 5d218bb3..da845f56 100644 --- a/resources/l10n/en.lproj/Localizable.strings +++ b/resources/l10n/en.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "Hidden"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/resources/l10n/es.lproj/Localizable.strings b/resources/l10n/es.lproj/Localizable.strings index 83df2495..3df3cc1a 100644 --- a/resources/l10n/es.lproj/Localizable.strings +++ b/resources/l10n/es.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "Ocultas"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/resources/l10n/fi.lproj/Localizable.strings b/resources/l10n/fi.lproj/Localizable.strings index 6d574600..4167f76d 100644 --- a/resources/l10n/fi.lproj/Localizable.strings +++ b/resources/l10n/fi.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "Piilossa"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/resources/l10n/fr.lproj/Localizable.strings b/resources/l10n/fr.lproj/Localizable.strings index 21fe68cf..c021431d 100644 --- a/resources/l10n/fr.lproj/Localizable.strings +++ b/resources/l10n/fr.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "Cachées"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/resources/l10n/hu.lproj/Localizable.strings b/resources/l10n/hu.lproj/Localizable.strings index cfef894d..cc31ce76 100644 --- a/resources/l10n/hu.lproj/Localizable.strings +++ b/resources/l10n/hu.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "Rejtett"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/resources/l10n/id.lproj/Localizable.strings b/resources/l10n/id.lproj/Localizable.strings index 99f171d6..20404d9d 100644 --- a/resources/l10n/id.lproj/Localizable.strings +++ b/resources/l10n/id.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "Tersembunyi"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/resources/l10n/it.lproj/Localizable.strings b/resources/l10n/it.lproj/Localizable.strings index 939de267..c847bf1f 100644 --- a/resources/l10n/it.lproj/Localizable.strings +++ b/resources/l10n/it.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "Nascosto"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/resources/l10n/ja.lproj/Localizable.strings b/resources/l10n/ja.lproj/Localizable.strings index 7a2842f7..f0a149fa 100644 --- a/resources/l10n/ja.lproj/Localizable.strings +++ b/resources/l10n/ja.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "ウィンドウをしまう"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/resources/l10n/ko.lproj/Localizable.strings b/resources/l10n/ko.lproj/Localizable.strings index ec37057c..89d6f2b0 100644 --- a/resources/l10n/ko.lproj/Localizable.strings +++ b/resources/l10n/ko.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "숨겨진 화면"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "마우스를 올릴 때 색칠된 원들 숨기기:"; diff --git a/resources/l10n/lb.lproj/Localizable.strings b/resources/l10n/lb.lproj/Localizable.strings index cace0bda..25515041 100644 --- a/resources/l10n/lb.lproj/Localizable.strings +++ b/resources/l10n/lb.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "Verstoppen"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/resources/l10n/nl.lproj/Localizable.strings b/resources/l10n/nl.lproj/Localizable.strings index db54861e..388ae313 100644 --- a/resources/l10n/nl.lproj/Localizable.strings +++ b/resources/l10n/nl.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "Verborgen"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/resources/l10n/pt.lproj/Localizable.strings b/resources/l10n/pt.lproj/Localizable.strings index df4f4bfd..323e953c 100644 --- a/resources/l10n/pt.lproj/Localizable.strings +++ b/resources/l10n/pt.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "Escondido"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Esconder círculos coloridos onde o rato passar:"; diff --git a/resources/l10n/ru.lproj/Localizable.strings b/resources/l10n/ru.lproj/Localizable.strings index e61e9555..3e631bf0 100644 --- a/resources/l10n/ru.lproj/Localizable.strings +++ b/resources/l10n/ru.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "Скрытые"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Скрыть цветные круги при наведении мышью:"; diff --git a/resources/l10n/tr.lproj/Localizable.strings b/resources/l10n/tr.lproj/Localizable.strings index 42223845..2b51bb77 100644 --- a/resources/l10n/tr.lproj/Localizable.strings +++ b/resources/l10n/tr.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "Gizli"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/resources/l10n/zh-CN.lproj/Localizable.strings b/resources/l10n/zh-CN.lproj/Localizable.strings index a174e24a..176d76b5 100644 --- a/resources/l10n/zh-CN.lproj/Localizable.strings +++ b/resources/l10n/zh-CN.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "隐藏"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "鼠标悬停时隐藏彩色圆圈:"; diff --git a/resources/l10n/zh-TW.lproj/Localizable.strings b/resources/l10n/zh-TW.lproj/Localizable.strings index 9a9fecc2..8be6efd8 100644 --- a/resources/l10n/zh-TW.lproj/Localizable.strings +++ b/resources/l10n/zh-TW.lproj/Localizable.strings @@ -128,6 +128,9 @@ /*No comment provided by engineer.*/ "Hidden" = "隱藏"; +/*No comment provided by engineer.*/ +"Hide app badges:" = "Hide app badges:"; + /*No comment provided by engineer.*/ "Hide colored circles on mouse hover:" = "Hide colored circles on mouse hover:"; diff --git a/scripts/subset_fonticon.sh b/scripts/subset_fonticon.sh index e2fb67ad..42c5b168 100755 --- a/scripts/subset_fonticon.sh +++ b/scripts/subset_fonticon.sh @@ -4,4 +4,4 @@ set -exu "$(pipenv --venv)"/bin/pyftsubset resources/SF-Pro-Text-Regular-Full.otf \ --output-file=resources/SF-Pro-Text-Regular.otf \ - --text="􀀁􀁑􀁏􀁍􀁎􀁌􀕧􀕬􀀸􀀺􀀼􀀾􀁀􀁂􀁄􀑱􀁆􀁈􀁊􀑳􀓵􀓶􀓷􀓸􀓹􀓺􀓻􀓼􀓽􀓾􀓿􀔀􀔁􀔂􀔃􀔄􀔅􀔆􀔇􀔈􀔉􀘠􀚗􀚙􀚛􀚝􀚟􀚡􀚣􀚥􀚧􀚩􀚫􀚭􀚯􀚱􀚳􀚵􀚷􀚹􀚻" + --text="􀀁􀁑􀁏􀁍􀁎􀁌􀕧􀕬􀀸􀀺􀀼􀀾􀁀􀁂􀁄􀑱􀁆􀁈􀁊􀑳􀓵􀓶􀓷􀓸􀓹􀓺􀓻􀓼􀓽􀓾􀓿􀔀􀔁􀔂􀔃􀔄􀔅􀔆􀔇􀔈􀔉􀀹􀀻􀀽􀀿􀁁􀘘􀁃􀁅􀑲􀁇􀁉􀁋􀑴􀔔􀔕􀔖􀔗􀔘􀔙􀔚􀔛􀔜􀔝􀔞􀔟􀔠􀔡􀔢􀔣􀔤􀔥􀔦􀔧􀔨􀕭" diff --git a/src/api-wrappers/AXUIElement.swift b/src/api-wrappers/AXUIElement.swift index 72061d46..a4b3eba8 100644 --- a/src/api-wrappers/AXUIElement.swift +++ b/src/api-wrappers/AXUIElement.swift @@ -116,6 +116,10 @@ extension AXUIElement { return try attribute(kAXParentAttribute, AXUIElement.self) } + func children() throws -> [AXUIElement]? { + return try attribute(kAXChildrenAttribute, [AXUIElement].self) + } + func windows() throws -> [AXUIElement]? { return try attribute(kAXWindowsAttribute, [AXUIElement].self) } @@ -140,6 +144,10 @@ extension AXUIElement { return try attribute(kAXSubroleAttribute, String.self) } + func appIsRunning() throws -> Bool? { + return try attribute(kAXIsApplicationRunningAttribute, Bool.self) + } + func closeButton() throws -> AXUIElement? { return try attribute(kAXCloseButtonAttribute, AXUIElement.self) } diff --git a/src/api-wrappers/PrivateApis.swift b/src/api-wrappers/PrivateApis.swift index 90319f2c..3d69ac60 100644 --- a/src/api-wrappers/PrivateApis.swift +++ b/src/api-wrappers/PrivateApis.swift @@ -121,8 +121,9 @@ func CGSGetWindowLevel(_ cid: CGSConnectionID, _ wid: CGWindowID, _ level: inout @_silgen_name("SLSRequestScreenCaptureAccess") @discardableResult func SLSRequestScreenCaptureAccess() -> UInt8 -// for some reason, this attribute is missing from AXAttributeConstants +// for some reason, these attributes are missing from AXAttributeConstants let kAXFullscreenAttribute = "AXFullScreen" +let kAXStatusLabelAttribute = "AXStatusLabel" // enables/disables a symbolic hotkeys. These are system shortcuts such as command+tab or Spotlight // it is possible to find all the existing hotkey IDs by using CGSGetSymbolicHotKeyValue on the first few hundred numbers diff --git a/src/logic/Application.swift b/src/logic/Application.swift index beb98a6d..ae36083f 100644 --- a/src/logic/Application.swift +++ b/src/logic/Application.swift @@ -7,6 +7,7 @@ class Application: NSObject { var axUiElement: AXUIElement? var axObserver: AXObserver? var isReallyFinishedLaunching = false + var dockLabel: String? static func notifications(_ app: NSRunningApplication) -> [String] { var n = [ diff --git a/src/logic/Applications.swift b/src/logic/Applications.swift index 5cfa6036..469a8c9e 100644 --- a/src/logic/Applications.swift +++ b/src/logic/Applications.swift @@ -73,6 +73,21 @@ class Applications { App.app.refreshOpenUi() } + static func refreshBadges() { + retryAxCallUntilTimeout { + let axDock = AXUIElementCreateApplication(Applications.list.first { $0.runningApplication.bundleIdentifier == "com.apple.dock" }!.runningApplication.processIdentifier) + let axList = try axDock.children()!.first { try $0.role() == "AXList" }! + let axAppDockItem = try axList.children()!.filter { try $0.subrole() == "AXApplicationDockItem" && ($0.appIsRunning() ?? false) } + try Applications.list.forEach { app in + if app.runningApplication.activationPolicy == .regular, + let bundleId = app.runningApplication.bundleIdentifier, + let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: bundleId) { + app.dockLabel = try axAppDockItem.first { try $0.attribute(kAXURLAttribute, URL.self) == url }?.attribute(kAXStatusLabelAttribute, String.self) + } + } + } + } + private static func isActualApplication(_ app: NSRunningApplication) -> Bool { return (app.activationPolicy != .prohibited || isNotXpc(app)) && notAltTab(app) } diff --git a/src/logic/Preferences.swift b/src/logic/Preferences.swift index 2fbf7d76..9ef46d2e 100644 --- a/src/logic/Preferences.swift +++ b/src/logic/Preferences.swift @@ -58,6 +58,7 @@ class Preferences { "minCellsPerRow": defaultsDependingOnScreenRatio_["minCellsPerRow"]!, "maxCellsPerRow": defaultsDependingOnScreenRatio_["maxCellsPerRow"]!, "shortcutStyle": "0", + "hideAppBadges": "false", ] // constant values @@ -96,6 +97,7 @@ class Preferences { static var fadeOutAnimation: Bool { defaults.bool("fadeOutAnimation") } static var hideSpaceNumberLabels: Bool { defaults.bool("hideSpaceNumberLabels") } static var hideStatusIcons: Bool { defaults.bool("hideStatusIcons") } + static var hideAppBadges: Bool { defaults.bool("hideAppBadges") } static var startAtLogin: Bool { defaults.bool("startAtLogin") } static var dontShowBlacklist: [String] { blacklistStringToArray(defaults.string("dontShowBlacklist")) } static var disableShortcutsBlacklist: [String] { blacklistStringToArray(defaults.string("disableShortcutsBlacklist")) } diff --git a/src/logic/Window.swift b/src/logic/Window.swift index d6244720..1b9e4f69 100644 --- a/src/logic/Window.swift +++ b/src/logic/Window.swift @@ -4,10 +4,11 @@ class Window { var cgWindowId: CGWindowID var title: String! var thumbnail: NSImage? - var icon: NSImage? + var icon: NSImage? { get { application.runningApplication.icon } } var shouldShowTheUser = true var isTabbed: Bool = false - var isHidden: Bool + var isHidden: Bool { get { application.runningApplication.isHidden } } + var dockLabel: Int? { get { application.dockLabel.flatMap { Int($0) } } } var isFullscreen: Bool var isMinimized: Bool var isOnAllSpaces: Bool @@ -35,8 +36,6 @@ class Window { self.cgWindowId = wid self.spaceId = Spaces.currentSpaceId self.spaceIndex = Spaces.currentSpaceIndex - self.icon = application.runningApplication.icon - self.isHidden = application.runningApplication.isHidden self.isFullscreen = isFullscreen self.isMinimized = isMinimized self.isOnAllSpaces = false diff --git a/src/logic/events/AccessibilityEvents.swift b/src/logic/events/AccessibilityEvents.swift index 95c1906a..68c2f817 100644 --- a/src/logic/events/AccessibilityEvents.swift +++ b/src/logic/events/AccessibilityEvents.swift @@ -86,11 +86,7 @@ private func applicationHiddenOrShown(_ element: AXUIElement, _ pid: pid_t, _ ty DispatchQueue.main.async { let windows = Windows.list.filter { // for AXUIElement of apps, CFEqual or == don't work; looks like a Cocoa bug - let isFromApp = $0.application.runningApplication.processIdentifier == pid - if isFromApp { - $0.isHidden = type == kAXApplicationHiddenNotification - } - return isFromApp + return $0.application.runningApplication.processIdentifier == pid } App.app.refreshOpenUi(windows) } diff --git a/src/ui/App.swift b/src/ui/App.swift index f9a6b00e..a684d8d5 100644 --- a/src/ui/App.swift +++ b/src/ui/App.swift @@ -270,6 +270,8 @@ class App: AppCenterApplication, NSApplicationDelegate { guard appIsBeingUsed else { return } Windows.refreshAllThumbnails() guard appIsBeingUsed else { return } + Applications.refreshBadges() + guard appIsBeingUsed else { return } refreshOpenUi() guard appIsBeingUsed else { return } thumbnailsPanel.show() diff --git a/src/ui/main-window/ThumbnailFontIconView.swift b/src/ui/main-window/ThumbnailFontIconView.swift index 44c88573..a45f4072 100644 --- a/src/ui/main-window/ThumbnailFontIconView.swift +++ b/src/ui/main-window/ThumbnailFontIconView.swift @@ -7,22 +7,21 @@ enum Symbols: String { case circledNumber0 = "􀀸" case circledNumber10 = "􀓵" case circledStar = "􀕬" + case filledCircledStar = "􀕭" case filledCircled = "􀀁" case filledCircledMultiplySign = "􀁑" case filledCircledMinusSign = "􀁏" case filledCircledPlusSign = "􀁍" + case filledCircledNumber0 = "􀀹" + case filledCircledNumber10 = "􀔔" } // 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, _ isBackground: Bool = false) { + 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 - if isBackground { - self.init(size, 3, shadow: nil) - } else { - self.init(size, 3) - } + self.init(size, 3, shadow: shadow) string = symbol.rawValue font = NSFont(name: "SF Pro Text", size: size) textColor = color @@ -31,32 +30,39 @@ class ThumbnailFontIconView: ThumbnailTitleView { } // number should be in the interval [0-50] - func setNumber(_ number: UInt32) { - let (baseCharacter, offset) = baseCharacterAndOffset(number) - assignIfDifferent(&string, String(UnicodeScalar(baseCharacter.unicodeScalars.first!.value + offset)!)) + func setNumber(_ number: Int, _ filled: Bool) { + let (baseCharacter, offset) = baseCharacterAndOffset(number, filled) + assignIfDifferent(&string, String(UnicodeScalar(Int(baseCharacter.unicodeScalars.first!.value) + offset)!)) + } + + private func baseCharacterAndOffset(_ number: Int, _ filled: Bool) -> (String, Int) { + if number <= 9 { + // numbers alternate between empty and full circles; we skip the full circles + return ((filled ? Symbols.filledCircledNumber0 : Symbols.circledNumber0).rawValue, number * 2) + } else { + return ((filled ? Symbols.filledCircledNumber10 : Symbols.circledNumber10).rawValue, number - 10) + } } func setStar() { assignIfDifferent(&string, Symbols.circledStar.rawValue) } - private func baseCharacterAndOffset(_ number: UInt32) -> (String, UInt32) { - if number <= 9 { - // numbers alternate between empty and full circles; we skip the full circles - return (Symbols.circledNumber0.rawValue, number * UInt32(2)) - } else { - return (Symbols.circledNumber10.rawValue, number - 10) - } + func setFilledStar() { + assignIfDifferent(&string, Symbols.filledCircledStar.rawValue) } } class ThumbnailFilledFontIconView: NSView { - convenience init(_ thumbnailFontIconView: ThumbnailFontIconView, _ backgroundColor: NSColor) { + convenience init(_ thumbnailFontIconView: ThumbnailFontIconView, _ backgroundColor: NSColor, _ offset: Bool = false) { self.init(frame: .zero) translatesAutoresizingMaskIntoConstraints = false - let backgroundView = ThumbnailFontIconView(.filledCircled, thumbnailFontIconView.font!.pointSize, backgroundColor, true) + let backgroundView = ThumbnailFontIconView(.filledCircled, thumbnailFontIconView.font!.pointSize - (offset ? 2 : 0), backgroundColor) addSubview(backgroundView) addSubview(thumbnailFontIconView, positioned: .above, relativeTo: nil) - fit(backgroundView.fittingSize.width, backgroundView.fittingSize.height) + if offset { + backgroundView.leftAnchor.constraint(equalTo: backgroundView.superview!.leftAnchor, constant: 1).isActive = true + } + fit(thumbnailFontIconView.fittingSize.width, thumbnailFontIconView.fittingSize.height) } } diff --git a/src/ui/main-window/ThumbnailView.swift b/src/ui/main-window/ThumbnailView.swift index 73c54c1d..bfd8d070 100644 --- a/src/ui/main-window/ThumbnailView.swift +++ b/src/ui/main-window/ThumbnailView.swift @@ -9,9 +9,10 @@ class ThumbnailView: NSStackView { var minimizedIcon = ThumbnailFontIconView(.circledMinusSign) var hiddenIcon = ThumbnailFontIconView(.circledSlashSign) var spaceIcon = ThumbnailFontIconView(.circledNumber0) - var closeIcon = ThumbnailFilledFontIconView(ThumbnailFontIconView(.filledCircledMultiplySign, Preferences.fontIconSize, NSColor(srgbRed: 1, green: 0.35, blue: 0.32, alpha: 1)), NSColor(srgbRed: 0.64, green: 0.03, blue: 0.02, alpha: 1)) - var minimizeIcon = ThumbnailFilledFontIconView(ThumbnailFontIconView(.filledCircledMinusSign, Preferences.fontIconSize, NSColor(srgbRed: 0.91, green: 0.75, blue: 0.16, alpha: 1)), NSColor(srgbRed: 0.71, green: 0.55, blue: 0.09, alpha: 1)) - var maximizeIcon = ThumbnailFilledFontIconView(ThumbnailFontIconView(.filledCircledPlusSign, Preferences.fontIconSize, NSColor(srgbRed: 0.32, green: 0.76, blue: 0.17, alpha: 1)), NSColor(srgbRed: 0.04, green: 0.39, blue: 0.02, alpha: 1)) + 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 closeIcon = ThumbnailFilledFontIconView(ThumbnailFontIconView(.filledCircledMultiplySign, Preferences.fontIconSize, NSColor(srgbRed: 1, green: 0.35, blue: 0.32, alpha: 1), nil), NSColor(srgbRed: 0.64, green: 0.03, blue: 0.02, alpha: 1)) + var minimizeIcon = ThumbnailFilledFontIconView(ThumbnailFontIconView(.filledCircledMinusSign, Preferences.fontIconSize, NSColor(srgbRed: 0.91, green: 0.75, blue: 0.16, alpha: 1), nil), NSColor(srgbRed: 0.71, green: 0.55, blue: 0.09, alpha: 1)) + var maximizeIcon = ThumbnailFilledFontIconView(ThumbnailFontIconView(.filledCircledPlusSign, Preferences.fontIconSize, NSColor(srgbRed: 0.32, green: 0.76, blue: 0.17, alpha: 1), nil), NSColor(srgbRed: 0.04, green: 0.39, blue: 0.02, alpha: 1)) var hStackView: NSStackView! var mouseUpCallback: (() -> Void)! var mouseMovedCallback: (() -> Void)! @@ -42,9 +43,16 @@ class ThumbnailView: NSStackView { hStackView.spacing = Preferences.intraCellPadding setViews([hStackView, thumbnail], in: .leading) addWindowControls() + addDockLabelIcon() } - func addWindowControls() { + private func addDockLabelIcon() { + appIcon.addSubview(dockLabelIcon, positioned: .above, relativeTo: nil) + dockLabelIcon.topAnchor.constraint(equalTo: appIcon.topAnchor, constant: -4).isActive = true + dockLabelIcon.rightAnchor.constraint(equalTo: appIcon.rightAnchor, constant: 1).isActive = true + } + + private func addWindowControls() { thumbnail.addSubview(closeIcon, positioned: .above, relativeTo: nil) thumbnail.addSubview(minimizeIcon, positioned: .above, relativeTo: nil) thumbnail.addSubview(maximizeIcon, positioned: .above, relativeTo: nil) @@ -117,10 +125,19 @@ class ThumbnailView: NSStackView { assignIfDifferent(&minimizedIcon.isHidden, !element.isMinimized || Preferences.hideStatusIcons) assignIfDifferent(&spaceIcon.isHidden, Spaces.isSingleSpace || Preferences.hideSpaceNumberLabels) if !spaceIcon.isHidden { - if element.isOnAllSpaces { + if element.spaceIndex > 30 || element.isOnAllSpaces { spaceIcon.setStar() } else { - spaceIcon.setNumber(UInt32(element.spaceIndex)) + spaceIcon.setNumber(element.spaceIndex, false) + } + } + assignIfDifferent(&dockLabelIcon.isHidden, element.dockLabel == nil || Preferences.hideAppBadges || Preferences.iconSize == 0) + if !dockLabelIcon.isHidden, let dockLabel = element.dockLabel { + let view = dockLabelIcon.subviews[1] as! ThumbnailFontIconView + if dockLabel > 30 { + view.setFilledStar() + } else { + view.setNumber(dockLabel, true) } } assignIfDifferent(&frame.size.width, max(thumbnail.frame.size.width + Preferences.intraCellPadding * 2, ThumbnailView.widthMin(screen))) diff --git a/src/ui/preferences-window/tabs/AppearanceTab.swift b/src/ui/preferences-window/tabs/AppearanceTab.swift index 4e064c10..a91ae4aa 100644 --- a/src/ui/preferences-window/tabs/AppearanceTab.swift +++ b/src/ui/preferences-window/tabs/AppearanceTab.swift @@ -24,6 +24,7 @@ class AppearanceTab: NSViewController, PreferencePane { LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Hide status icons:", comment: ""), "hideStatusIcons"), LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Show standard tabs as windows:", comment: ""), "showTabsAsWindows"), LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Hide colored circles on mouse hover:", comment: ""), "hideColoredCircles"), + LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Hide app badges:", comment: ""), "hideAppBadges"), ]) grid.column(at: 0).xPlacement = .trailing grid.rowAlignment = .lastBaseline