fix: app-icons wouldn't show when permissions were skipped
also cached NSScreen.preferred for performance
This commit is contained in:
1 parent
f027b4795f
commit
837fa03f64
8 files changed
+141
-119
No files matched your search
@@ -55,15 +55,14 @@ class Appearance {
|
||||
}
|
||||
|
||||
private static func updateSize() {
|
||||
let screen = NSScreen.preferred()
|
||||
let isHorizontalScreen = screen.isHorizontal()
|
||||
maxWidthOnScreen = AppearanceTestable.comfortableWidth(screen.physicalSize().map { $0.width })
|
||||
let isHorizontalScreen = NSScreen.preferred.isHorizontal()
|
||||
maxWidthOnScreen = AppearanceTestable.comfortableWidth(NSScreen.preferred.physicalSize().map { $0.width })
|
||||
if currentStyle == .appIcons {
|
||||
appIconsSize()
|
||||
} else if currentStyle == .titles {
|
||||
titlesSize(isHorizontalScreen)
|
||||
} else {
|
||||
thumbnailsSize(isHorizontalScreen, screen)
|
||||
thumbnailsSize(isHorizontalScreen)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +74,7 @@ class Appearance {
|
||||
}
|
||||
}
|
||||
|
||||
private static func thumbnailsSize(_ isHorizontalScreen: Bool, _ screen: NSScreen) {
|
||||
private static func thumbnailsSize(_ isHorizontalScreen: Bool) {
|
||||
hideThumbnails = false
|
||||
windowPadding = 18
|
||||
cellCornerRadius = 10
|
||||
@@ -95,7 +94,7 @@ class Appearance {
|
||||
iconSize = 32
|
||||
fontHeight = 16
|
||||
}
|
||||
let thumbnailsPanelRatio = (screen.frame.width * maxWidthOnScreen) / (screen.frame.height * maxHeightOnScreen)
|
||||
let thumbnailsPanelRatio = (NSScreen.preferred.frame.width * maxWidthOnScreen) / (NSScreen.preferred.frame.height * maxHeightOnScreen)
|
||||
(windowMinWidthInRow, windowMaxWidthInRow) = AppearanceTestable.goodValuesForThumbnailsWidthMinMax(thumbnailsPanelRatio, rowsCount)
|
||||
if currentVisibility == .highest {
|
||||
edgeInsetsSize = 10
|
||||
|
||||
+39
-37
@@ -1,6 +1,45 @@
|
||||
import Cocoa
|
||||
|
||||
extension NSScreen {
|
||||
static var preferred: NSScreen = detectPreferred() ?? NSScreen.screens.first!
|
||||
|
||||
static func updatePreferred() {
|
||||
preferred = detectPreferred() ?? NSScreen.screens.first!
|
||||
}
|
||||
|
||||
private static func detectPreferred() -> NSScreen? {
|
||||
switch Preferences.showOnScreen {
|
||||
case .includingMouse: return withMouse()
|
||||
case .active: return NSScreen.active()
|
||||
case .includingMenubar: return NSScreen.screens.first
|
||||
}
|
||||
}
|
||||
|
||||
/// NSScreen.main docs are incorrect. It stopped returning the screen with the key window in macOS 10.9
|
||||
/// see https://stackoverflow.com/a/56268826/2249756
|
||||
/// There are a few cases where .main doesn't return the screen with the key window:
|
||||
/// * if the active screen shows a fullscreen app, it always returns screens[0]
|
||||
/// * if NSScreen.screensHaveSeparateSpaces == false, and key window is on another screen than screens[0], it still returns screens[0]
|
||||
/// we find the screen with the key window ourselves manually
|
||||
static func active() -> NSScreen? {
|
||||
if let app = Applications.find(NSWorkspace.shared.frontmostApplication?.processIdentifier) {
|
||||
if let focusedWindow = app.focusedWindow {
|
||||
return NSScreen.screens.first { focusedWindow.isOnScreen($0) }
|
||||
}
|
||||
return NSScreen.withActiveMenubar()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// there is only 1 active menubar. Other screens will show their menubar dimmed
|
||||
static func withActiveMenubar() -> NSScreen? {
|
||||
return NSScreen.screens.first { CGSCopyActiveMenuBarDisplayIdentifier(CGS_CONNECTION) == $0.uuid() }
|
||||
}
|
||||
|
||||
static func withMouse() -> NSScreen? {
|
||||
return NSScreen.screens.first { NSMouseInRect(NSEvent.mouseLocation, $0.frame, false) }
|
||||
}
|
||||
|
||||
func ratio() -> CGFloat {
|
||||
return frame.width / frame.height
|
||||
}
|
||||
@@ -41,43 +80,6 @@ extension NSScreen {
|
||||
return nil
|
||||
}
|
||||
|
||||
static func preferred() -> NSScreen {
|
||||
preferred_() ?? NSScreen.screens.first!
|
||||
}
|
||||
|
||||
private static func preferred_() -> NSScreen? {
|
||||
switch Preferences.showOnScreen {
|
||||
case .includingMouse: return withMouse()
|
||||
case .active: return NSScreen.active()
|
||||
case .includingMenubar: return NSScreen.screens.first
|
||||
}
|
||||
}
|
||||
|
||||
/// NSScreen.main docs are incorrect. It stopped returning the screen with the key window in macOS 10.9
|
||||
/// see https://stackoverflow.com/a/56268826/2249756
|
||||
/// There are a few cases where .main doesn't return the screen with the key window:
|
||||
/// * if the active screen shows a fullscreen app, it always returns screens[0]
|
||||
/// * if NSScreen.screensHaveSeparateSpaces == false, and key window is on another screen than screens[0], it still returns screens[0]
|
||||
/// we find the screen with the key window ourselves manually
|
||||
static func active() -> NSScreen? {
|
||||
if let app = Applications.find(NSWorkspace.shared.frontmostApplication?.processIdentifier) {
|
||||
if let focusedWindow = app.focusedWindow {
|
||||
return NSScreen.screens.first { focusedWindow.isOnScreen($0) }
|
||||
}
|
||||
return NSScreen.withActiveMenubar()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// there is only 1 active menubar. Other screens will show their menubar dimmed
|
||||
static func withActiveMenubar() -> NSScreen? {
|
||||
return NSScreen.screens.first { CGSCopyActiveMenuBarDisplayIdentifier(CGS_CONNECTION) == $0.uuid() }
|
||||
}
|
||||
|
||||
static func withMouse() -> NSScreen? {
|
||||
return NSScreen.screens.first { NSMouseInRect(NSEvent.mouseLocation, $0.frame, false) }
|
||||
}
|
||||
|
||||
func repositionPanel(_ window: NSWindow) {
|
||||
let screenFrame = visibleFrame
|
||||
let panelFrame = window.frame
|
||||
|
||||
@@ -7,6 +7,7 @@ class Window {
|
||||
var creationOrder = Int.zero
|
||||
var title: String!
|
||||
var thumbnail: NSImage?
|
||||
var thumbnailFullSize: NSSize?
|
||||
var icon: NSImage? { get { application.icon } }
|
||||
var shouldShowTheUser = true
|
||||
var isTabbed: Bool = false
|
||||
@@ -98,12 +99,13 @@ class Window {
|
||||
|
||||
func refreshThumbnail(_ screenshot: NSImage?) {
|
||||
thumbnail = screenshot
|
||||
thumbnailFullSize = screenshot?.size
|
||||
if App.app.appIsBeingUsed && shouldShowTheUser {
|
||||
if let index = (Windows.list.firstIndex { $0.cgWindowId == cgWindowId }) {
|
||||
let view = ThumbnailsView.recycledViews[index]
|
||||
if !view.thumbnail.isHidden {
|
||||
view.thumbnail.image = thumbnail
|
||||
let thumbnailSize = ThumbnailView.thumbnailSize(thumbnail, NSScreen.preferred(), false)
|
||||
let thumbnailSize = ThumbnailView.thumbnailSize(thumbnail, false)
|
||||
view.thumbnail.image?.size = thumbnailSize
|
||||
view.thumbnail.frame.size = thumbnailSize
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ class Windows {
|
||||
}
|
||||
}
|
||||
|
||||
static func updatesBeforeShowing(_ screen: NSScreen) -> Bool {
|
||||
static func updatesBeforeShowing() -> Bool {
|
||||
if list.count == 0 || MissionControl.state() == .showAllWindows || MissionControl.state() == .showFrontWindows { return false }
|
||||
// TODO: find a way to update space info when spaces are changed, instead of on every trigger
|
||||
// workaround: when Preferences > Mission Control > "Displays have separate Spaces" is unchecked,
|
||||
@@ -275,9 +275,9 @@ class Windows {
|
||||
// TODO: can the CGS call inside detectTabbedWindows introduce latency when WindowServer is busy?
|
||||
detectTabbedWindows(window, cgsWindowIds, visibleCgsWindowIds)
|
||||
updatesWindowSpace(window)
|
||||
refreshIfWindowShouldBeShownToTheUser(window, screen)
|
||||
refreshIfWindowShouldBeShownToTheUser(window)
|
||||
}
|
||||
refreshWhichWindowsToShowTheUser(screen)
|
||||
refreshWhichWindowsToShowTheUser()
|
||||
sort()
|
||||
if (!list.contains { $0.shouldShowTheUser }) { return false }
|
||||
return true
|
||||
@@ -337,7 +337,7 @@ class Windows {
|
||||
}
|
||||
}
|
||||
|
||||
static func refreshWhichWindowsToShowTheUser(_ screen: NSScreen) {
|
||||
static func refreshWhichWindowsToShowTheUser() {
|
||||
if Preferences.onlyShowApplications() {
|
||||
// Group windows by application and select the optimal main window
|
||||
let windowsGroupedByApp = Dictionary(grouping: list) { $0.application.pid }
|
||||
@@ -353,7 +353,7 @@ class Windows {
|
||||
}
|
||||
}
|
||||
|
||||
private static func refreshIfWindowShouldBeShownToTheUser(_ window: Window, _ screen: NSScreen) {
|
||||
private static func refreshIfWindowShouldBeShownToTheUser(_ window: Window) {
|
||||
window.shouldShowTheUser =
|
||||
!(window.application.runningApplication.bundleIdentifier.flatMap { id in
|
||||
Preferences.blacklist.contains {
|
||||
@@ -368,7 +368,7 @@ class Windows {
|
||||
!(!(Preferences.showFullscreenWindows[App.app.shortcutIndex] != .hide) && window.isFullscreen) &&
|
||||
!(!(Preferences.showMinimizedWindows[App.app.shortcutIndex] != .hide) && window.isMinimized) &&
|
||||
!(Preferences.spacesToShow[App.app.shortcutIndex] == .visible && !Spaces.visibleSpaces.contains(window.spaceId)) &&
|
||||
!(Preferences.screensToShow[App.app.shortcutIndex] == .showingAltTab && !window.isOnScreen(screen)) &&
|
||||
!(Preferences.screensToShow[App.app.shortcutIndex] == .showingAltTab && !window.isOnScreen(NSScreen.preferred)) &&
|
||||
(Preferences.showTabsAsWindows || !window.isTabbed))
|
||||
}
|
||||
|
||||
|
||||
+12
-10
@@ -145,7 +145,8 @@ class App: AppCenterApplication {
|
||||
|
||||
func showSecondaryWindow(_ window: NSWindow?) {
|
||||
if let window = window {
|
||||
NSScreen.preferred().repositionPanel(window)
|
||||
NSScreen.updatePreferred()
|
||||
NSScreen.preferred.repositionPanel(window)
|
||||
App.shared.activate(ignoringOtherApps: true)
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
// Use the center function to continue to center, the `repositionPanel` function cannot center, it may be a system bug
|
||||
@@ -201,19 +202,19 @@ class App: AppCenterApplication {
|
||||
return
|
||||
}
|
||||
guard appIsBeingUsed else { return }
|
||||
let currentScreen = NSScreen.preferred() // fix screen between steps since it could change (e.g. mouse moved to another screen)
|
||||
if !skipUpdatesBeforeShowing {
|
||||
if !Windows.updatesBeforeShowing(currentScreen) { hideUi(); return }
|
||||
if !Windows.updatesBeforeShowing() { hideUi(); return }
|
||||
}
|
||||
guard appIsBeingUsed else { return }
|
||||
Windows.updateFocusedWindowIndex()
|
||||
guard appIsBeingUsed else { return }
|
||||
thumbnailsPanel.thumbnailsView.updateItemsAndLayout(currentScreen)
|
||||
print("updateItemsAndLayout")
|
||||
thumbnailsPanel.thumbnailsView.updateItemsAndLayout()
|
||||
guard appIsBeingUsed else { return }
|
||||
thumbnailsPanel.setContentSize(thumbnailsPanel.thumbnailsView.frame.size)
|
||||
thumbnailsPanel.display()
|
||||
guard appIsBeingUsed else { return }
|
||||
currentScreen.repositionPanel(thumbnailsPanel)
|
||||
NSScreen.preferred.repositionPanel(thumbnailsPanel)
|
||||
guard appIsBeingUsed else { return }
|
||||
Windows.voiceOverWindow() // at this point ThumbnailViews are assigned to the window, and ready
|
||||
guard appIsBeingUsed else { return }
|
||||
@@ -232,16 +233,16 @@ class App: AppCenterApplication {
|
||||
}
|
||||
isFirstSummon = false
|
||||
self.shortcutIndex = shortcutIndex
|
||||
let screen = NSScreen.preferred()
|
||||
if !Windows.updatesBeforeShowing(screen) { hideUi(); return }
|
||||
NSScreen.updatePreferred()
|
||||
if !Windows.updatesBeforeShowing() { hideUi(); return }
|
||||
Windows.setInitialFocusedAndHoveredWindowIndex()
|
||||
if Preferences.windowDisplayDelay == DispatchTimeInterval.milliseconds(0) {
|
||||
buildUiAndShowPanel(screen)
|
||||
buildUiAndShowPanel()
|
||||
} else {
|
||||
delayedDisplayScheduled += 1
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Preferences.windowDisplayDelay) { () -> () in
|
||||
if self.delayedDisplayScheduled == 1 {
|
||||
self.buildUiAndShowPanel(screen)
|
||||
self.buildUiAndShowPanel()
|
||||
}
|
||||
self.delayedDisplayScheduled -= 1
|
||||
}
|
||||
@@ -252,7 +253,7 @@ class App: AppCenterApplication {
|
||||
}
|
||||
}
|
||||
|
||||
func buildUiAndShowPanel(_ screen: NSScreen = NSScreen.preferred()) {
|
||||
func buildUiAndShowPanel() {
|
||||
guard appIsBeingUsed else { return }
|
||||
Appearance.update()
|
||||
guard appIsBeingUsed else { return }
|
||||
@@ -299,6 +300,7 @@ extension App: NSApplicationDelegate {
|
||||
SystemPermissions.ensurePermissionsAreGranted { [weak self] in
|
||||
guard let self = self else { return }
|
||||
BackgroundWork.start()
|
||||
NSScreen.updatePreferred()
|
||||
Appearance.update()
|
||||
Menubar.initialize()
|
||||
self.loadMainMenuXib()
|
||||
|
||||
@@ -95,11 +95,11 @@ class ThumbnailView: FlippedView {
|
||||
observeDragAndDrop()
|
||||
}
|
||||
|
||||
func updateRecycledCellWithNewContent(_ element: Window, _ index: Int, _ newHeight: CGFloat, _ screen: NSScreen) {
|
||||
func updateRecycledCellWithNewContent(_ element: Window, _ index: Int, _ newHeight: CGFloat) {
|
||||
window_ = element
|
||||
updateValues(element, index, newHeight, screen)
|
||||
updateSizes(newHeight, screen)
|
||||
updatePositions(newHeight, screen)
|
||||
updateValues(element, index, newHeight)
|
||||
updateSizes(newHeight)
|
||||
updatePositions(newHeight)
|
||||
label.toolTip = label.cell!.cellSize.width >= label.frame.size.width ? label.stringValue : nil
|
||||
}
|
||||
|
||||
@@ -298,7 +298,36 @@ class ThumbnailView: FlippedView {
|
||||
view.label.toolTip = view.label.cell!.cellSize.width >= view.label.frame.size.width ? view.label.stringValue : nil
|
||||
}
|
||||
|
||||
private func updateValues(_ element: Window, _ index: Int, _ newHeight: CGFloat, _ screen: NSScreen) {
|
||||
private func updateThumbnail(_ element: Window) {
|
||||
// we need to reset the original size, otherwise it may be too small and not get resized
|
||||
// this can happen when switching from one screen to another for example
|
||||
if let screenshot = element.thumbnail, let thumbnailFullSize = element.thumbnailFullSize {
|
||||
screenshot.size = thumbnailFullSize
|
||||
}
|
||||
if !thumbnail.isHidden && thumbnail != element.thumbnail {
|
||||
if let screenshot = element.thumbnail {
|
||||
thumbnail.image = screenshot
|
||||
} else if let appIconFallback = element.icon {
|
||||
// if no thumbnail, show appIcon instead
|
||||
// .icon is always 32x32 by default. We scale it so it can be "thumbnail-ed" down
|
||||
appIconFallback.size = NSSize(width: 1024, height: 1024)
|
||||
thumbnail.image = appIconFallback
|
||||
}
|
||||
// for Accessibility > "speak items under the pointer"
|
||||
thumbnail.setAccessibilityLabel(element.title)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateAppIcon(_ element: Window, _ title: String) -> Bool {
|
||||
let appIconChanged = appIcon.image != element.icon
|
||||
if appIconChanged {
|
||||
appIcon.image = element.icon
|
||||
appIcon.setAccessibilityLabel(title)
|
||||
}
|
||||
return appIconChanged
|
||||
}
|
||||
|
||||
private func updateValues(_ element: Window, _ index: Int, _ newHeight: CGFloat) {
|
||||
assignIfDifferent(&thumbnail.isHidden, Appearance.hideThumbnails || element.isWindowlessApp)
|
||||
assignIfDifferent(&windowlessIcon.isHidden, !element.isWindowlessApp || Appearance.hideThumbnails)
|
||||
assignIfDifferent(&windowlessAppIndicator.isHidden, !element.isWindowlessApp)
|
||||
@@ -310,11 +339,7 @@ class ThumbnailView: FlippedView {
|
||||
NSScreen.screens.count < 2 || Preferences.screensToShow[App.app.shortcutIndex] == .showingAltTab
|
||||
)
|
||||
))
|
||||
if !thumbnail.isHidden {
|
||||
thumbnail.image = element.thumbnail
|
||||
// for Accessibility > "speak items under the pointer"
|
||||
thumbnail.setAccessibilityLabel(element.title)
|
||||
}
|
||||
updateThumbnail(element)
|
||||
let title = getAppOrAndWindowTitle()
|
||||
let labelChanged = label.stringValue != title
|
||||
if labelChanged {
|
||||
@@ -331,11 +356,7 @@ class ThumbnailView: FlippedView {
|
||||
spaceIcon.toolTip = String(format: NSLocalizedString("Window is on Space %d", comment: ""), element.spaceIndex)
|
||||
}
|
||||
}
|
||||
let appIconChanged = appIcon.image != element.icon
|
||||
if appIconChanged {
|
||||
appIcon.image = element.icon
|
||||
appIcon.setAccessibilityLabel(title)
|
||||
}
|
||||
let appIconChanged = updateAppIcon(element, title)
|
||||
let dockLabelChanged = updateDockLabelIcon(element.dockLabel)
|
||||
if appIconChanged || dockLabelChanged {
|
||||
setAccessibilityHelp(getAccessibilityHelp(element.application.runningApplication.localizedName, element.dockLabel))
|
||||
@@ -349,19 +370,19 @@ class ThumbnailView: FlippedView {
|
||||
mouseMovedCallback = { () -> Void in Windows.updateFocusedAndHoveredWindowIndex(index, true) }
|
||||
}
|
||||
|
||||
private func updateSizes(_ newHeight: CGFloat, _ screen: NSScreen) {
|
||||
private func updateSizes(_ newHeight: CGFloat) {
|
||||
if Preferences.appearanceStyle == .thumbnails {
|
||||
if !thumbnail.isHidden {
|
||||
let thumbnailSize = ThumbnailView.thumbnailSize(thumbnail.image, screen, false)
|
||||
let thumbnailSize = ThumbnailView.thumbnailSize(thumbnail.image, false)
|
||||
thumbnail.setSize(thumbnailSize)
|
||||
}
|
||||
if !windowlessIcon.isHidden {
|
||||
let windowlessIconSize = ThumbnailView.thumbnailSize(windowlessIcon.image, screen, true)
|
||||
let windowlessIconSize = ThumbnailView.thumbnailSize(windowlessIcon.image, true)
|
||||
windowlessIcon.setSize(windowlessIconSize)
|
||||
}
|
||||
}
|
||||
setFrameWidthHeight(screen, newHeight)
|
||||
let appIconSize = ThumbnailView.iconSize(screen)
|
||||
setFrameWidthHeight(newHeight)
|
||||
let appIconSize = ThumbnailView.iconSize()
|
||||
appIcon.setSize(appIconSize)
|
||||
if Preferences.appearanceStyle == .appIcons {
|
||||
vStackView.frame.size = NSSize(width: frame.width, height: appIcon.frame.height + Appearance.edgeInsetsSize * 2)
|
||||
@@ -378,7 +399,7 @@ class ThumbnailView: FlippedView {
|
||||
}
|
||||
}
|
||||
|
||||
private func updatePositions(_ newHeight: CGFloat, _ screen: NSScreen) {
|
||||
private func updatePositions(_ newHeight: CGFloat) {
|
||||
hStackView.frame.origin = NSPoint(x: Appearance.edgeInsetsSize, y: Appearance.edgeInsetsSize)
|
||||
if Preferences.appearanceStyle != .appIcons {
|
||||
appIcon.frame.origin.x = App.shared.userInterfaceLayoutDirection == .leftToRight
|
||||
@@ -426,7 +447,7 @@ class ThumbnailView: FlippedView {
|
||||
windowlessAppIndicator.frame.origin.y = windowlessAppIndicator.superview!.frame.height - windowlessAppIndicator.frame.height
|
||||
}
|
||||
if !dockLabelIcon.isHidden {
|
||||
let iconSize = ThumbnailView.iconSize(screen)
|
||||
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)
|
||||
@@ -449,18 +470,18 @@ class ThumbnailView: FlippedView {
|
||||
return windowTitle ?? ""
|
||||
}
|
||||
|
||||
private func setFrameWidthHeight(_ screen: NSScreen, _ newHeight: CGFloat) {
|
||||
private func setFrameWidthHeight(_ newHeight: CGFloat) {
|
||||
var contentWidth = CGFloat(0)
|
||||
if Preferences.appearanceStyle == .thumbnails {
|
||||
// Preferred to the width of the image, and the minimum width may be set to be large.
|
||||
contentWidth = (!windowlessIcon.isHidden ? windowlessIcon : thumbnail).frame.size.width
|
||||
} else if Preferences.appearanceStyle == .titles {
|
||||
contentWidth = ThumbnailView.maxThumbnailWidth(screen) - Appearance.edgeInsetsSize * 2
|
||||
contentWidth = ThumbnailView.maxThumbnailWidth() - Appearance.edgeInsetsSize * 2
|
||||
} else {
|
||||
contentWidth = Appearance.iconSize
|
||||
}
|
||||
let frameWidth = (contentWidth + Appearance.edgeInsetsSize * 2).rounded()
|
||||
let widthMin = ThumbnailView.minThumbnailWidth(screen)
|
||||
let widthMin = ThumbnailView.minThumbnailWidth()
|
||||
let width = max(frameWidth, widthMin).rounded()
|
||||
assignIfDifferent(&frame.size.width, width)
|
||||
assignIfDifferent(&frame.size.height, newHeight)
|
||||
@@ -489,7 +510,7 @@ class ThumbnailView: FlippedView {
|
||||
}
|
||||
|
||||
static func dockLabelLabelSize() -> CGFloat {
|
||||
let size = (ThumbnailView.iconSize(NSScreen.preferred()).width * 0.43).rounded()
|
||||
let size = (ThumbnailView.iconSize().width * 0.43).rounded()
|
||||
// label should have a minimum size for readability
|
||||
return max(size, 13)
|
||||
}
|
||||
@@ -505,33 +526,28 @@ class ThumbnailView: FlippedView {
|
||||
return shadow
|
||||
}
|
||||
|
||||
static func maxThumbnailWidth(_ screen: NSScreen) -> CGFloat {
|
||||
return ThumbnailsPanel.maxThumbnailsWidth(screen) * Appearance.windowMaxWidthInRow - Appearance.interCellPadding * 2
|
||||
static func maxThumbnailWidth() -> CGFloat {
|
||||
return ThumbnailsPanel.maxThumbnailsWidth() * Appearance.windowMaxWidthInRow - Appearance.interCellPadding * 2
|
||||
}
|
||||
|
||||
static func minThumbnailWidth(_ screen: NSScreen) -> CGFloat {
|
||||
return ThumbnailsPanel.maxThumbnailsWidth(screen) * Appearance.windowMinWidthInRow - Appearance.interCellPadding * 2
|
||||
static func minThumbnailWidth() -> CGFloat {
|
||||
return ThumbnailsPanel.maxThumbnailsWidth() * Appearance.windowMinWidthInRow - Appearance.interCellPadding * 2
|
||||
}
|
||||
|
||||
/// The maximum height that a thumbnail can be drawn
|
||||
///
|
||||
/// maxThumbnailsHeight = maxThumbnailHeight * rowCount + interCellPadding * (rowCount - 1)
|
||||
///
|
||||
/// maxThumbnailHeight = (maxThumbnailsHeight - interCellPadding * (rowCount - 1)) / rowCount
|
||||
///
|
||||
/// - Parameter screen:
|
||||
/// - Returns:
|
||||
static func maxThumbnailHeight(_ screen: NSScreen) -> CGFloat {
|
||||
return (ThumbnailsPanel.maxThumbnailsHeight(screen) - Appearance.interCellPadding) / Appearance.rowsCount - Appearance.interCellPadding
|
||||
static func maxThumbnailHeight() -> CGFloat {
|
||||
return ((ThumbnailsPanel.maxThumbnailsHeight() - Appearance.interCellPadding) / Appearance.rowsCount - Appearance.interCellPadding).rounded()
|
||||
}
|
||||
|
||||
static func thumbnailSize(_ image: NSImage?, _ screen: NSScreen, _ isWindowlessApp: Bool) -> NSSize {
|
||||
static func thumbnailSize(_ image: NSImage?, _ isWindowlessApp: Bool) -> NSSize {
|
||||
guard let image = image else { return NSSize(width: 0, height: 0) }
|
||||
let thumbnailHeightMax = ThumbnailView.maxThumbnailHeight(screen)
|
||||
let thumbnailHeightMax = ThumbnailView.maxThumbnailHeight()
|
||||
- Appearance.edgeInsetsSize * 2
|
||||
- Appearance.intraCellPadding
|
||||
- Appearance.iconSize
|
||||
let thumbnailWidthMax = ThumbnailView.maxThumbnailWidth(screen)
|
||||
let thumbnailWidthMax = ThumbnailView.maxThumbnailWidth()
|
||||
- Appearance.edgeInsetsSize * 2
|
||||
// don't stretch very small windows; keep them 1:1 in the switcher
|
||||
if !isWindowlessApp && image.size.width < thumbnailWidthMax && image.size.height < thumbnailHeightMax {
|
||||
@@ -559,9 +575,9 @@ class ThumbnailView: FlippedView {
|
||||
return NSSize(width: width.rounded(), height: height.rounded())
|
||||
}
|
||||
|
||||
static func iconSize(_ screen: NSScreen) -> NSSize {
|
||||
static func iconSize() -> NSSize {
|
||||
if Preferences.appearanceStyle == .appIcons {
|
||||
let widthMin = ThumbnailView.minThumbnailWidth(screen)
|
||||
let widthMin = ThumbnailView.minThumbnailWidth()
|
||||
let contentWidth = Appearance.iconSize
|
||||
let frameWidth = contentWidth + Appearance.edgeInsetsSize * 2
|
||||
let width = max(frameWidth, widthMin).rounded()
|
||||
@@ -573,12 +589,12 @@ class ThumbnailView: FlippedView {
|
||||
return NSSize(width: Appearance.iconSize, height: Appearance.iconSize)
|
||||
}
|
||||
|
||||
static func height(_ screen: NSScreen, _ labelHeight: CGFloat) -> CGFloat {
|
||||
static func height(_ labelHeight: CGFloat) -> CGFloat {
|
||||
if Preferences.appearanceStyle == .titles {
|
||||
return max(ThumbnailView.iconSize(screen).height, labelHeight) + Appearance.edgeInsetsSize * 2
|
||||
return max(ThumbnailView.iconSize().height, labelHeight) + Appearance.edgeInsetsSize * 2
|
||||
} else if Preferences.appearanceStyle == .appIcons {
|
||||
return ThumbnailView.iconSize(screen).height + Appearance.edgeInsetsSize * 2 + Appearance.intraCellPadding * 2 + labelHeight
|
||||
return ThumbnailView.iconSize().height + Appearance.edgeInsetsSize * 2 + Appearance.intraCellPadding * 2 + labelHeight
|
||||
}
|
||||
return ThumbnailView.maxThumbnailHeight(screen).rounded(.down)
|
||||
return ThumbnailView.maxThumbnailHeight()
|
||||
}
|
||||
}
|
||||
@@ -44,12 +44,12 @@ class ThumbnailsPanel: NSPanel {
|
||||
thumbnailsView.scrollView.flashScrollers()
|
||||
}
|
||||
|
||||
static func maxThumbnailsWidth(_ screen: NSScreen) -> CGFloat {
|
||||
return screen.frame.width * Appearance.maxWidthOnScreen - Appearance.windowPadding * 2
|
||||
static func maxThumbnailsWidth() -> CGFloat {
|
||||
return (NSScreen.preferred.frame.width * Appearance.maxWidthOnScreen - Appearance.windowPadding * 2).rounded()
|
||||
}
|
||||
|
||||
static func maxThumbnailsHeight(_ screen: NSScreen) -> CGFloat {
|
||||
return screen.frame.height * Appearance.maxHeightOnScreen - Appearance.windowPadding * 2
|
||||
static func maxThumbnailsHeight() -> CGFloat {
|
||||
return (NSScreen.preferred.frame.height * Appearance.maxHeightOnScreen - Appearance.windowPadding * 2).rounded()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ class ThumbnailsView: NSVisualEffectView {
|
||||
func reset() {
|
||||
// it would be nicer to remove this whole "reset" logic, and instead update each component to check Appearance properties before showing
|
||||
// Maybe in some Appkit willDraw() function that triggers before drawing it
|
||||
NSScreen.updatePreferred()
|
||||
Appearance.update()
|
||||
material = Appearance.material
|
||||
for i in 0..<ThumbnailsView.recycledViews.count {
|
||||
@@ -101,10 +102,10 @@ class ThumbnailsView: NSVisualEffectView {
|
||||
}
|
||||
}
|
||||
|
||||
func updateItemsAndLayout(_ screen: NSScreen) {
|
||||
let widthMax = ThumbnailsPanel.maxThumbnailsWidth(screen).rounded()
|
||||
if let (maxX, maxY, labelHeight) = layoutThumbnailViews(screen, widthMax) {
|
||||
layoutParentViews(screen, maxX, widthMax, maxY, labelHeight)
|
||||
func updateItemsAndLayout() {
|
||||
let widthMax = ThumbnailsPanel.maxThumbnailsWidth().rounded()
|
||||
if let (maxX, maxY, labelHeight) = layoutThumbnailViews(widthMax) {
|
||||
layoutParentViews(maxX, widthMax, maxY, labelHeight)
|
||||
if Preferences.alignThumbnails == .center {
|
||||
centerRows(maxX)
|
||||
}
|
||||
@@ -120,9 +121,9 @@ class ThumbnailsView: NSVisualEffectView {
|
||||
}
|
||||
}
|
||||
|
||||
private func layoutThumbnailViews(_ screen: NSScreen, _ widthMax: CGFloat) -> (CGFloat, CGFloat, CGFloat)? {
|
||||
private func layoutThumbnailViews(_ widthMax: CGFloat) -> (CGFloat, CGFloat, CGFloat)? {
|
||||
let labelHeight = ThumbnailsView.recycledViews.first!.label.cell!.cellSize.height
|
||||
let height = ThumbnailView.height(screen, labelHeight)
|
||||
let height = ThumbnailView.height(labelHeight)
|
||||
let isLeftToRight = App.shared.userInterfaceLayoutDirection == .leftToRight
|
||||
let startingX = isLeftToRight ? Appearance.interCellPadding : widthMax - Appearance.interCellPadding
|
||||
var currentX = startingX
|
||||
@@ -136,7 +137,7 @@ class ThumbnailsView: NSVisualEffectView {
|
||||
guard App.app.appIsBeingUsed else { return nil }
|
||||
guard window.shouldShowTheUser else { continue }
|
||||
let view = ThumbnailsView.recycledViews[index]
|
||||
view.updateRecycledCellWithNewContent(window, index, height, screen)
|
||||
view.updateRecycledCellWithNewContent(window, index, height)
|
||||
let width = view.frame.size.width
|
||||
let projectedX = projectedWidth(currentX, width).rounded(.down)
|
||||
if needNewLine(projectedX, widthMax) {
|
||||
@@ -177,8 +178,8 @@ class ThumbnailsView: NSVisualEffectView {
|
||||
App.shared.userInterfaceLayoutDirection == .leftToRight ? currentX : currentX - width
|
||||
}
|
||||
|
||||
private func layoutParentViews(_ screen: NSScreen, _ maxX: CGFloat, _ widthMax: CGFloat, _ maxY: CGFloat, _ labelHeight: CGFloat) {
|
||||
let heightMax = ThumbnailsPanel.maxThumbnailsHeight(screen).rounded()
|
||||
private func layoutParentViews(_ maxX: CGFloat, _ widthMax: CGFloat, _ maxY: CGFloat, _ labelHeight: CGFloat) {
|
||||
let heightMax = ThumbnailsPanel.maxThumbnailsHeight()
|
||||
ThumbnailsView.thumbnailsWidth = min(maxX, widthMax)
|
||||
ThumbnailsView.thumbnailsHeight = min(maxY, heightMax)
|
||||
let frameWidth = ThumbnailsView.thumbnailsWidth + Appearance.windowPadding * 2
|
||||
|
||||
Reference in new issue
Block a user