feat: show indicator for fullscreen windows
Also: * preference to show or not fullscreen windows * new indicator for hidden windows
This commit is contained in:
1 parent
2674c8f8db
commit
0138cd17b1
14 files changed
+42
-25
No files matched your search
Binary file not shown.
@@ -67,6 +67,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Fade out animation:" = "Fade out animation:";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Fullscreen" = "Fullscreen";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"General" = "General";
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
set -exu
|
||||
|
||||
pod install
|
||||
scripts/extract_l10n_strings.sh
|
||||
scripts/import_l10n_strings_from_poeditor.sh
|
||||
brew install jq
|
||||
scripts/update_contributors.sh
|
||||
scripts/import_l10n_strings_from_poeditor.sh
|
||||
scripts/extract_l10n_strings.sh
|
||||
pod install
|
||||
|
||||
git status
|
||||
git --no-pager diff
|
||||
|
||||
@@ -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=""
|
||||
+1
-1
@@ -3,8 +3,8 @@
|
||||
set -exu
|
||||
|
||||
env | sort
|
||||
|
||||
npm ci
|
||||
brew install jq
|
||||
|
||||
npx commitlint-travis
|
||||
scripts/ensure_generated_files_are_up_to_date.sh
|
||||
|
||||
@@ -49,13 +49,6 @@ extension AXUIElement {
|
||||
return attribute(kAXWindowsAttribute, [AXUIElement].self)
|
||||
}
|
||||
|
||||
func isTabbed(_ app: AXUIElement, _ spaceId: CGSSpaceID) -> Bool {
|
||||
// we can only detect tabs for windows on the current space, as AXUIElement.windows() only reports current space windows
|
||||
// also, windows that start in fullscreen will have the wrong spaceID at that point in time, so we check if they are fullscreen too
|
||||
return spaceId == Spaces.currentSpaceId && !isFullScreen() &&
|
||||
app.windows()?.first { $0 == self } == nil
|
||||
}
|
||||
|
||||
func isMinimized() -> Bool {
|
||||
return attribute(kAXMinimizedAttribute, Bool.self) == true
|
||||
}
|
||||
|
||||
@@ -78,8 +78,10 @@ class DebugProfile {
|
||||
|
||||
private static func appWindow(_ window: Window) -> String {
|
||||
return listLevel3([
|
||||
("isFullscreen", String(window.isFullscreen)),
|
||||
("isMinimized", String(window.isMinimized)),
|
||||
("isHidden", String(window.isHidden)),
|
||||
("isTabbed", String(window.isTabbed)),
|
||||
("isOnAllSpaces", String(window.isOnAllSpaces)),
|
||||
("spaceId", String(window.spaceId)),
|
||||
("spaceIndex", String(window.spaceIndex)),
|
||||
|
||||
@@ -22,6 +22,7 @@ class Preferences {
|
||||
"hideShowAppShortcut": "H",
|
||||
"arrowKeysEnabled": "true",
|
||||
"mouseHoverEnabled": "true",
|
||||
"showFullscreenWindows": "true",
|
||||
"showMinimizedWindows": "true",
|
||||
"showHiddenWindows": "true",
|
||||
"showTabsAsWindows": "false",
|
||||
@@ -63,6 +64,7 @@ class Preferences {
|
||||
static var hideShowAppShortcut: String { defaults.string("hideShowAppShortcut") }
|
||||
static var arrowKeysEnabled: Bool { defaults.bool("arrowKeysEnabled") }
|
||||
static var mouseHoverEnabled: Bool { defaults.bool("mouseHoverEnabled") }
|
||||
static var showFullscreenWindows: Bool { defaults.bool("showFullscreenWindows") }
|
||||
static var showMinimizedWindows: Bool { defaults.bool("showMinimizedWindows") }
|
||||
static var showHiddenWindows: Bool { defaults.bool("showHiddenWindows") }
|
||||
static var showTabsAsWindows: Bool { defaults.bool("showTabsAsWindows") }
|
||||
|
||||
+14
-5
@@ -2,12 +2,13 @@ import Cocoa
|
||||
|
||||
class Window {
|
||||
var cgWindowId: CGWindowID
|
||||
var title: String
|
||||
var title: String = ""
|
||||
var thumbnail: NSImage?
|
||||
var icon: NSImage?
|
||||
var shouldShowTheUser = true
|
||||
var isTabbed: Bool
|
||||
var isTabbed: Bool = false
|
||||
var isHidden: Bool
|
||||
var isFullscreen: Bool
|
||||
var isMinimized: Bool
|
||||
var isOnAllSpaces: Bool
|
||||
var spaceId: CGSSpaceID
|
||||
@@ -33,11 +34,12 @@ class Window {
|
||||
self.spaceId = Spaces.currentSpaceId
|
||||
self.spaceIndex = Spaces.currentSpaceIndex
|
||||
self.icon = application.runningApplication.icon
|
||||
self.isTabbed = axUiElement.isTabbed(application.axUiElement!, spaceId)
|
||||
self.isHidden = application.runningApplication.isHidden
|
||||
self.isFullscreen = axUiElement.isFullScreen()
|
||||
self.isMinimized = axUiElement.isMinimized()
|
||||
self.isOnAllSpaces = false
|
||||
self.title = Window.bestEffortTitle(axUiElement, cgWindowId, application)
|
||||
self.title = bestEffortTitle()
|
||||
self.isTabbed = getIsTabbed()
|
||||
debugPrint("Adding window", cgWindowId, title, application.runningApplication.bundleIdentifier ?? "nil", Spaces.currentSpaceId, Spaces.currentSpaceIndex)
|
||||
observeEvents()
|
||||
}
|
||||
@@ -56,6 +58,13 @@ class Window {
|
||||
thumbnail = NSImage(cgImage: cgImage, size: NSSize(width: cgImage.width, height: cgImage.height))
|
||||
}
|
||||
|
||||
func getIsTabbed() -> Bool {
|
||||
// we can only detect tabs for windows on the current space, as AXUIElement.windows() only reports current space windows
|
||||
// also, windows that start in fullscreen will have the wrong spaceID at that point in time, so we check if they are fullscreen too
|
||||
return spaceId == Spaces.currentSpaceId && !isFullscreen &&
|
||||
application.axUiElement!.windows()?.first { $0 == axUiElement } == nil
|
||||
}
|
||||
|
||||
func close() {
|
||||
DispatchQueues.accessibilityCommands.async { [weak self] in
|
||||
self?.axUiElement.closeWindow()
|
||||
@@ -121,7 +130,7 @@ class Window {
|
||||
}
|
||||
|
||||
// for some windows (e.g. Slack), the AX API doesn't return a title; we try CG API; finally we resort to the app name
|
||||
static func bestEffortTitle(_ axUiElement: AXUIElement, _ cgWindowId: CGWindowID, _ application: Application) -> String {
|
||||
func bestEffortTitle() -> String {
|
||||
if let axTitle = axUiElement.title(), !axTitle.isEmpty {
|
||||
return axTitle
|
||||
}
|
||||
|
||||
@@ -96,7 +96,9 @@ class Windows {
|
||||
}
|
||||
|
||||
static func refreshIfWindowShouldBeShownToTheUser(_ window: Window, _ screen: NSScreen) {
|
||||
window.shouldShowTheUser = !(!Preferences.showMinimizedWindows && window.isMinimized) &&
|
||||
window.shouldShowTheUser =
|
||||
!(!Preferences.showFullscreenWindows && window.isFullscreen) &&
|
||||
!(!Preferences.showMinimizedWindows && window.isMinimized) &&
|
||||
!(!Preferences.showHiddenWindows && window.isHidden) &&
|
||||
!(Preferences.appsToShow == .active && window.application.runningApplication != NSWorkspace.shared.frontmostApplication) &&
|
||||
!(Preferences.spacesToShow == .active && window.spaceId != Spaces.currentSpaceId) &&
|
||||
|
||||
@@ -20,11 +20,11 @@ func axObserverCallback(observer: AXObserver, element: AXUIElement, notification
|
||||
}
|
||||
|
||||
private func focusedUiElementChanged(_ element: AXUIElement, _ applicationPointer: UnsafeMutableRawPointer?) {
|
||||
// this event is the only opportunity we have to check if a window became a tab, or a tab became a window
|
||||
let application = Unmanaged<Application>.fromOpaque(applicationPointer!).takeUnretainedValue()
|
||||
Windows.list.forEach {
|
||||
if $0.application == application {
|
||||
$0.isTabbed = $0.axUiElement.isTabbed(application.axUiElement!, $0.spaceId)
|
||||
// this event is the only opportunity we have to check if a window became a tab, or a tab became a window
|
||||
$0.isTabbed = $0.getIsTabbed()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,5 +96,6 @@ private func windowTitleChanged(_ element: AXUIElement) {
|
||||
private func windowResized(_ element: AXUIElement) {
|
||||
guard let index = Windows.list.firstIndexThatMatches(element) else { return }
|
||||
let window = Windows.list[index]
|
||||
window.isFullscreen = window.axUiElement.isFullScreen()
|
||||
App.app.refreshOpenUi([window])
|
||||
}
|
||||
@@ -3,8 +3,9 @@ import Cocoa
|
||||
// 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 {
|
||||
static let sfSymbolCircledPlusSign = ""
|
||||
static let sfSymbolCircledMinusSign = ""
|
||||
static let sfSymbolCircledDotSign = ""
|
||||
static let sfSymbolCircledSlashSign = ""
|
||||
static let sfSymbolCircledNumber0 = ""
|
||||
static let sfSymbolCircledNumber10 = ""
|
||||
static let sfSymbolCircledStart = ""
|
||||
|
||||
@@ -5,8 +5,9 @@ class ThumbnailView: NSStackView {
|
||||
var thumbnail = NSImageView()
|
||||
var appIcon = NSImageView()
|
||||
var label = ThumbnailTitleView(Preferences.fontHeight)
|
||||
var fullscreenIcon = ThumbnailFontIconView(ThumbnailFontIconView.sfSymbolCircledPlusSign, Preferences.fontIconSize, .white)
|
||||
var minimizedIcon = ThumbnailFontIconView(ThumbnailFontIconView.sfSymbolCircledMinusSign, Preferences.fontIconSize, .white)
|
||||
var hiddenIcon = ThumbnailFontIconView(ThumbnailFontIconView.sfSymbolCircledDotSign, Preferences.fontIconSize, .white)
|
||||
var hiddenIcon = ThumbnailFontIconView(ThumbnailFontIconView.sfSymbolCircledSlashSign, Preferences.fontIconSize, .white)
|
||||
var spaceIcon = ThumbnailFontIconView(ThumbnailFontIconView.sfSymbolCircledNumber0, Preferences.fontIconSize, .white)
|
||||
var hStackView: NSStackView!
|
||||
var mouseDownCallback: (() -> Void)!
|
||||
@@ -32,7 +33,7 @@ class ThumbnailView: NSStackView {
|
||||
let shadow = ThumbnailView.makeShadow(.gray)
|
||||
thumbnail.shadow = shadow
|
||||
appIcon.shadow = shadow
|
||||
hStackView = NSStackView(views: [appIcon, label, hiddenIcon, minimizedIcon, spaceIcon])
|
||||
hStackView = NSStackView(views: [appIcon, label, hiddenIcon, fullscreenIcon, minimizedIcon, spaceIcon])
|
||||
hStackView.spacing = Preferences.intraCellPadding
|
||||
setViews([hStackView, thumbnail], in: .leading)
|
||||
}
|
||||
@@ -78,6 +79,7 @@ class ThumbnailView: NSStackView {
|
||||
label.string = element.title
|
||||
}
|
||||
assignIfDifferent(&hiddenIcon.isHidden, !element.isHidden)
|
||||
assignIfDifferent(&fullscreenIcon.isHidden, !element.isFullscreen)
|
||||
assignIfDifferent(&minimizedIcon.isHidden, !element.isMinimized)
|
||||
assignIfDifferent(&spaceIcon.isHidden, Spaces.isSingleSpace || Preferences.hideSpaceNumberLabels)
|
||||
if !spaceIcon.isHidden {
|
||||
@@ -89,7 +91,7 @@ class ThumbnailView: NSStackView {
|
||||
}
|
||||
assignIfDifferent(&frame.size.width, max(thumbnail.frame.size.width + Preferences.intraCellPadding * 2, ThumbnailView.widthMin(screen)))
|
||||
assignIfDifferent(&frame.size.height, newHeight)
|
||||
let fontIconWidth = CGFloat([minimizedIcon, hiddenIcon, spaceIcon].filter { !$0.isHidden }.count) * (Preferences.fontIconSize + Preferences.intraCellPadding)
|
||||
let fontIconWidth = CGFloat([fullscreenIcon, minimizedIcon, hiddenIcon, spaceIcon].filter { !$0.isHidden }.count) * (Preferences.fontIconSize + Preferences.intraCellPadding)
|
||||
assignIfDifferent(&label.textContainer!.size.width, frame.width - Preferences.iconSize - Preferences.intraCellPadding * 3 - fontIconWidth)
|
||||
assignIfDifferent(&subviews.first!.frame.size, frame.size)
|
||||
self.mouseDownCallback = { () -> Void in App.app.focusSelectedWindow(element) }
|
||||
|
||||
@@ -43,11 +43,12 @@ class GeneralTab: NSViewController, PreferencePane {
|
||||
let appsToShow = dropdown("appsToShow", AppsToShowPreference.allCases)
|
||||
let spacesToShow = dropdown("spacesToShow", SpacesToShowPreference.allCases)
|
||||
let screensToShow = dropdown("screensToShow", ScreensToShowPreference.allCases)
|
||||
let showFullscreenWindows = StackView(LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Fullscreen", comment: ""), "showFullscreenWindows", labelPosition: .right))
|
||||
let showMinimizedWindows = StackView(LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Minimized", comment: ""), "showMinimizedWindows", labelPosition: .right))
|
||||
let showHiddenWindows = StackView(LabelAndControl.makeLabelWithCheckbox(NSLocalizedString("Hidden", comment: ""), "showHiddenWindows", labelPosition: .right))
|
||||
let shortcuts = StackView([nextWindowShortcut, previousWindowShortcut, cancelShortcut, closeWindowShortcut, minDeminWindowShortcut, quitAppShortcut, hideShowAppShortcut].map { (view: [NSView]) in StackView(view) }, .vertical)
|
||||
let toShowDropdowns = StackView([appsToShow, spacesToShow, screensToShow], .vertical)
|
||||
let toShowCheckboxes = StackView([showMinimizedWindows, showHiddenWindows], .vertical)
|
||||
let toShowCheckboxes = StackView([showMinimizedWindows, showHiddenWindows, showFullscreenWindows], .vertical)
|
||||
let toShowExplanations = LabelAndControl.makeLabel(NSLocalizedString("Show the following windows:", comment: ""))
|
||||
let toShow = StackView([toShowDropdowns, toShowCheckboxes], .vertical)
|
||||
|
||||
|
||||
Reference in new issue
Block a user