perf: use calayer instead of nsview for lightimageview
This commit is contained in:
1 parent
3a8c0f3bee
commit
7dec9da401
5 files changed
+148
-99
No files matched your search
@@ -154,6 +154,7 @@
|
|||||||
BF0C8973EC078169170F2634 /* CliEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C8C69316EAAF6DC9AE391 /* CliEvents.swift */; };
|
BF0C8973EC078169170F2634 /* CliEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C8C69316EAAF6DC9AE391 /* CliEvents.swift */; };
|
||||||
BF0C898511686611E4D7D81E /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C8B2C057099EDABE46779 /* Button.swift */; };
|
BF0C898511686611E4D7D81E /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C8B2C057099EDABE46779 /* Button.swift */; };
|
||||||
BF0C8991399A3277A9A02FFA /* LightImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C8E63BE7394A6591E74E0 /* LightImageView.swift */; };
|
BF0C8991399A3277A9A02FFA /* LightImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C8E63BE7394A6591E74E0 /* LightImageView.swift */; };
|
||||||
|
BF0C8992399A3277A9A02FFB /* LightImageLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C8E64BE7394A6591E74E1 /* LightImageLayer.swift */; };
|
||||||
BF0C89B4B17D7843C32A102E /* Markdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C8353097840D7DB1E0E2E /* Markdown.swift */; };
|
BF0C89B4B17D7843C32A102E /* Markdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C8353097840D7DB1E0E2E /* Markdown.swift */; };
|
||||||
BF0C89C49CFBAAB4646BB8CB /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C82A8848224E76B880A83 /* InfoPlist.strings */; };
|
BF0C89C49CFBAAB4646BB8CB /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C82A8848224E76B880A83 /* InfoPlist.strings */; };
|
||||||
BF0C89F0632E0A2EC52D9168 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C87749F1A64E2D3305181 /* InfoPlist.strings */; };
|
BF0C89F0632E0A2EC52D9168 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BF0C87749F1A64E2D3305181 /* InfoPlist.strings */; };
|
||||||
@@ -547,6 +548,7 @@
|
|||||||
BF0C8E54658B4DAEA0528200 /* menubar-2@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menubar-2@2x.png"; sourceTree = "<group>"; };
|
BF0C8E54658B4DAEA0528200 /* menubar-2@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menubar-2@2x.png"; sourceTree = "<group>"; };
|
||||||
BF0C8E5D2BD5F7F93DF77DF5 /* sk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sk; path = InfoPlist.strings; sourceTree = "<group>"; };
|
BF0C8E5D2BD5F7F93DF77DF5 /* sk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sk; path = InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
BF0C8E63BE7394A6591E74E0 /* LightImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LightImageView.swift; sourceTree = "<group>"; };
|
BF0C8E63BE7394A6591E74E0 /* LightImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LightImageView.swift; sourceTree = "<group>"; };
|
||||||
|
BF0C8E64BE7394A6591E74E1 /* LightImageLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LightImageLayer.swift; sourceTree = "<group>"; };
|
||||||
BF0C8E7B087DCC97A454C0AE /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = InfoPlist.strings; sourceTree = "<group>"; };
|
BF0C8E7B087DCC97A454C0AE /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
BF0C8E94CDFB672663333546 /* lt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = lt; path = InfoPlist.strings; sourceTree = "<group>"; };
|
BF0C8E94CDFB672663333546 /* lt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = lt; path = InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
BF0C8E9D0B36A8872D5B7653 /* nb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = nb; path = InfoPlist.strings; sourceTree = "<group>"; };
|
BF0C8E9D0B36A8872D5B7653 /* nb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = nb; path = InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
@@ -1503,6 +1505,7 @@
|
|||||||
1C9617C5839903D752070CF2 /* ImageTextButtonView.swift */,
|
1C9617C5839903D752070CF2 /* ImageTextButtonView.swift */,
|
||||||
BF0C896FD4907CD85D62C124 /* WindowlessAppIndicator.swift */,
|
BF0C896FD4907CD85D62C124 /* WindowlessAppIndicator.swift */,
|
||||||
BF0C8E63BE7394A6591E74E0 /* LightImageView.swift */,
|
BF0C8E63BE7394A6591E74E0 /* LightImageView.swift */,
|
||||||
|
BF0C8E64BE7394A6591E74E1 /* LightImageLayer.swift */,
|
||||||
BF0C8DC2A355E29516DD2FAF /* CustomRecorderControlTestable.swift */,
|
BF0C8DC2A355E29516DD2FAF /* CustomRecorderControlTestable.swift */,
|
||||||
);
|
);
|
||||||
path = "generic-components";
|
path = "generic-components";
|
||||||
@@ -2540,6 +2543,7 @@
|
|||||||
BF0C8973EC078169170F2634 /* CliEvents.swift in Sources */,
|
BF0C8973EC078169170F2634 /* CliEvents.swift in Sources */,
|
||||||
BF0C8521DA3078516615C414 /* HelperExtensionsTestable.swift in Sources */,
|
BF0C8521DA3078516615C414 /* HelperExtensionsTestable.swift in Sources */,
|
||||||
BF0C8991399A3277A9A02FFA /* LightImageView.swift in Sources */,
|
BF0C8991399A3277A9A02FFA /* LightImageView.swift in Sources */,
|
||||||
|
BF0C8992399A3277A9A02FFB /* LightImageLayer.swift in Sources */,
|
||||||
BF0C8F40C2D0FF7601D926D3 /* LightweightTimer.swift in Sources */,
|
BF0C8F40C2D0FF7601D926D3 /* LightweightTimer.swift in Sources */,
|
||||||
BF0C88A9AB286CB7CDC1019E /* MainMenu.swift in Sources */,
|
BF0C88A9AB286CB7CDC1019E /* MainMenu.swift in Sources */,
|
||||||
BF0C826C7F8C5F68732FD75B /* DebugMenu.swift in Sources */,
|
BF0C826C7F8C5F68732FD75B /* DebugMenu.swift in Sources */,
|
||||||
|
|||||||
@@ -89,6 +89,22 @@ extension NSColor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension CALayer {
|
||||||
|
func centerInSuperlayer(x: Bool = false, y: Bool = false) {
|
||||||
|
guard let superlayer else { return }
|
||||||
|
if x { frame.origin.x = ((superlayer.bounds.width - frame.width) / 2).rounded() }
|
||||||
|
if y { frame.origin.y = ((superlayer.bounds.height - frame.height) / 2).rounded() }
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyShadow(_ shadow: NSShadow?) {
|
||||||
|
guard let shadow else { shadowOpacity = 0; return }
|
||||||
|
shadowColor = shadow.shadowColor?.cgColor
|
||||||
|
shadowOffset = shadow.shadowOffset
|
||||||
|
shadowRadius = shadow.shadowBlurRadius
|
||||||
|
shadowOpacity = 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension NSView {
|
extension NSView {
|
||||||
// constrain size to fittingSize
|
// constrain size to fittingSize
|
||||||
func fit() {
|
func fit() {
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import Cocoa
|
||||||
|
|
||||||
|
/// this is a lightweight CALayer which displays an image
|
||||||
|
/// it is an alternative to NSView-based image display, avoiding AppKit overhead (layout recursion, responder chain, drag-and-drop)
|
||||||
|
class LightImageLayer: CALayer {
|
||||||
|
private var outlineLayer: CAShapeLayer!
|
||||||
|
private var handRaisedLayer: CALayer!
|
||||||
|
private let withTransparencyChecks: Bool
|
||||||
|
|
||||||
|
init(withTransparencyChecks: Bool = false) {
|
||||||
|
self.withTransparencyChecks = withTransparencyChecks
|
||||||
|
super.init()
|
||||||
|
contentsGravity = .resize
|
||||||
|
magnificationFilter = .trilinear
|
||||||
|
minificationFilter = .trilinear
|
||||||
|
minificationFilterBias = 0.0
|
||||||
|
shouldRasterize = false
|
||||||
|
delegate = NoAnimationDelegate.shared
|
||||||
|
setupOutlineLayer()
|
||||||
|
setupHandRaisedLayer()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("Class only supports programmatic initialization")
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(layer: Any) {
|
||||||
|
let other = layer as? LightImageLayer
|
||||||
|
self.withTransparencyChecks = other?.withTransparencyChecks ?? false
|
||||||
|
super.init(layer: layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupOutlineLayer() {
|
||||||
|
outlineLayer = CAShapeLayer()
|
||||||
|
outlineLayer.name = "outline"
|
||||||
|
outlineLayer.fillColor = nil
|
||||||
|
outlineLayer.lineWidth = 1.0
|
||||||
|
outlineLayer.lineDashPattern = [4, 3]
|
||||||
|
outlineLayer.lineCap = .round
|
||||||
|
outlineLayer.lineJoin = .round
|
||||||
|
outlineLayer.isHidden = true
|
||||||
|
addSublayer(outlineLayer)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupHandRaisedLayer() {
|
||||||
|
guard #available(macOS 11.0, *) else { return }
|
||||||
|
handRaisedLayer = CALayer()
|
||||||
|
handRaisedLayer.contentsGravity = .resizeAspect
|
||||||
|
handRaisedLayer.isHidden = true
|
||||||
|
addSublayer(handRaisedLayer)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateOutlineLayer(_ fullyTransparent: Bool) {
|
||||||
|
outlineLayer.isHidden = !fullyTransparent
|
||||||
|
guard fullyTransparent else { return }
|
||||||
|
let inset: CGFloat = 0.5
|
||||||
|
let radius: CGFloat = 8.0
|
||||||
|
outlineLayer.frame = bounds
|
||||||
|
let rect = bounds.insetBy(dx: inset, dy: inset)
|
||||||
|
let path = CGPath(roundedRect: rect, cornerWidth: radius, cornerHeight: radius, transform: nil)
|
||||||
|
outlineLayer.path = path
|
||||||
|
outlineLayer.strokeColor = Appearance.fontColor.cgColor
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateHandRaisedLayer(_ fullyTransparent: Bool) {
|
||||||
|
guard #available(macOS 11.0, *) else { return }
|
||||||
|
handRaisedLayer.isHidden = !fullyTransparent
|
||||||
|
guard fullyTransparent else { return }
|
||||||
|
let minSize = min(Appearance.iconSize, bounds.width * 0.9, bounds.height * 0.9)
|
||||||
|
let maxSize = Appearance.iconSize * 3
|
||||||
|
let ratio = min(bounds.width / ThumbnailView.maxThumbnailWidth(), bounds.height / ThumbnailView.maxThumbnailHeight())
|
||||||
|
let pointSize = max(minSize, maxSize * ratio)
|
||||||
|
let config = NSImage.SymbolConfiguration(pointSize: pointSize, weight: .regular)
|
||||||
|
guard let image = NSImage(systemSymbolName: "hand.raised.fill", accessibilityDescription: nil)?.withSymbolConfiguration(config) else { return }
|
||||||
|
handRaisedLayer.contents = image.tinted(Appearance.fontColor)
|
||||||
|
handRaisedLayer.bounds = CGRect(x: 0, y: 0, width: pointSize, height: pointSize)
|
||||||
|
handRaisedLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateContents(_ caLayerContents: CALayerContents, _ size: NSSize) {
|
||||||
|
var fullyTransparent = false
|
||||||
|
switch caLayerContents {
|
||||||
|
case .cgImage(let image?):
|
||||||
|
contents = image
|
||||||
|
if withTransparencyChecks {
|
||||||
|
fullyTransparent = image.isFullyTransparent()
|
||||||
|
}
|
||||||
|
case .pixelBuffer(let pixelBuffer?):
|
||||||
|
contents = CVPixelBufferGetIOSurface(pixelBuffer)?.takeUnretainedValue()
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
if frame.size != size {
|
||||||
|
frame.size = size
|
||||||
|
}
|
||||||
|
if withTransparencyChecks {
|
||||||
|
updateOutlineLayer(fullyTransparent)
|
||||||
|
updateHandRaisedLayer(fullyTransparent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func releaseImage() {
|
||||||
|
contents = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,106 +1,35 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
/// this is a lightweight view which displays an image using its CALayer
|
/// thin NSView wrapper around LightImageLayer, for use where AppKit requires an NSView (contentView, Auto Layout, NSStackView)
|
||||||
/// it is an alternative to NSImageView, which doesn't have internal complexities and performance costs
|
|
||||||
class LightImageView: NSView {
|
class LightImageView: NSView {
|
||||||
private var outlineLayer: CAShapeLayer!
|
let imageLayer: LightImageLayer
|
||||||
private var handRaisedLayer: CALayer!
|
|
||||||
private let withTransparencyChecks: Bool
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
fatalError("Class only supports programmatic initialization")
|
fatalError("Class only supports programmatic initialization")
|
||||||
}
|
}
|
||||||
|
|
||||||
init(frame frameRect: NSRect = .zero, withTransparencyChecks: Bool = false) {
|
init(frame frameRect: NSRect = .zero, withTransparencyChecks: Bool = false) {
|
||||||
self.withTransparencyChecks = withTransparencyChecks
|
imageLayer = LightImageLayer(withTransparencyChecks: withTransparencyChecks)
|
||||||
super.init(frame: frameRect)
|
super.init(frame: frameRect)
|
||||||
wantsLayer = true
|
wantsLayer = true
|
||||||
// configure the layer for efficient GPU-scaling
|
layer!.addSublayer(imageLayer)
|
||||||
layer!.contentsGravity = .resize
|
|
||||||
layer!.magnificationFilter = .trilinear
|
|
||||||
layer!.minificationFilter = .trilinear
|
|
||||||
layer!.minificationFilterBias = 0.0
|
|
||||||
layer!.shouldRasterize = false
|
|
||||||
layer!.delegate = NoAnimationDelegate.shared
|
|
||||||
layerContentsRedrawPolicy = .never
|
layerContentsRedrawPolicy = .never
|
||||||
setupOutlineLayer()
|
|
||||||
setupHandRaisedLayer()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupOutlineLayer() {
|
override func layout() {
|
||||||
outlineLayer = CAShapeLayer()
|
super.layout()
|
||||||
outlineLayer.name = "outline"
|
imageLayer.frame = bounds
|
||||||
outlineLayer.fillColor = nil
|
|
||||||
outlineLayer.lineWidth = 1.0
|
|
||||||
outlineLayer.lineDashPattern = [4, 3]
|
|
||||||
outlineLayer.lineCap = .round
|
|
||||||
outlineLayer.lineJoin = .round
|
|
||||||
outlineLayer.isHidden = true
|
|
||||||
layer!.addSublayer(outlineLayer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupHandRaisedLayer() {
|
|
||||||
guard #available(macOS 11.0, *) else { return }
|
|
||||||
handRaisedLayer = CALayer()
|
|
||||||
handRaisedLayer.contentsGravity = .resizeAspect
|
|
||||||
handRaisedLayer.isHidden = true
|
|
||||||
layer!.addSublayer(handRaisedLayer)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private func updateOutlineLayer(_ fullyTransparent: Bool) {
|
|
||||||
outlineLayer.isHidden = !fullyTransparent
|
|
||||||
guard fullyTransparent else { return }
|
|
||||||
let inset: CGFloat = 0.5
|
|
||||||
let radius: CGFloat = 8.0
|
|
||||||
let bounds = outlineLayer.superlayer!.bounds
|
|
||||||
outlineLayer.frame = bounds
|
|
||||||
let rect = bounds.insetBy(dx: inset, dy: inset)
|
|
||||||
let path = CGPath(roundedRect: rect, cornerWidth: radius, cornerHeight: radius, transform: nil)
|
|
||||||
outlineLayer.path = path
|
|
||||||
outlineLayer.strokeColor = Appearance.fontColor.cgColor
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updateHandRaisedLayer(_ fullyTransparent: Bool) {
|
|
||||||
guard #available(macOS 11.0, *) else { return }
|
|
||||||
handRaisedLayer.isHidden = !fullyTransparent
|
|
||||||
guard fullyTransparent else { return }
|
|
||||||
// we want the icon to be large but not too large, and readable at small sizes
|
|
||||||
let minSize = min(Appearance.iconSize, bounds.width * 0.9, bounds.height * 0.9)
|
|
||||||
let maxSize = Appearance.iconSize * 3
|
|
||||||
let ratio = min(bounds.width / ThumbnailView.maxThumbnailWidth(), bounds.height / ThumbnailView.maxThumbnailHeight())
|
|
||||||
let pointSize = max(minSize, maxSize * ratio)
|
|
||||||
let config = NSImage.SymbolConfiguration(pointSize: pointSize, weight: .regular)
|
|
||||||
guard let image = NSImage(systemSymbolName: "hand.raised.fill", accessibilityDescription: nil)?.withSymbolConfiguration(config) else { return }
|
|
||||||
handRaisedLayer.contents = image.tinted(Appearance.fontColor)
|
|
||||||
handRaisedLayer.bounds = CGRect(x: 0, y: 0, width: pointSize, height: pointSize)
|
|
||||||
handRaisedLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func updateContents(_ caLayerContents: CALayerContents, _ size: NSSize) {
|
func updateContents(_ caLayerContents: CALayerContents, _ size: NSSize) {
|
||||||
var fullyTransparent = false
|
imageLayer.updateContents(caLayerContents, size)
|
||||||
switch caLayerContents {
|
|
||||||
case .cgImage(let image?):
|
|
||||||
layer!.contents = image
|
|
||||||
if withTransparencyChecks {
|
|
||||||
fullyTransparent = image.isFullyTransparent()
|
|
||||||
}
|
|
||||||
case .pixelBuffer(let pixelBuffer?):
|
|
||||||
layer!.contents = CVPixelBufferGetIOSurface(pixelBuffer)?.takeUnretainedValue()
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
if frame.size != size {
|
if frame.size != size {
|
||||||
frame.size = size
|
frame.size = size
|
||||||
}
|
}
|
||||||
if withTransparencyChecks {
|
|
||||||
updateOutlineLayer(fullyTransparent)
|
|
||||||
updateHandRaisedLayer(fullyTransparent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func releaseImage() {
|
func releaseImage() {
|
||||||
layer!.contents = nil
|
imageLayer.releaseImage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ class ThumbnailView: FlippedView {
|
|||||||
static let extraTextForPadding = "lmnopqrstuvw"
|
static let extraTextForPadding = "lmnopqrstuvw"
|
||||||
|
|
||||||
var window_: Window?
|
var window_: Window?
|
||||||
var thumbnail = LightImageView(withTransparencyChecks: true)
|
var thumbnail = LightImageLayer(withTransparencyChecks: true)
|
||||||
var appIcon = LightImageView()
|
var appIcon = LightImageLayer()
|
||||||
var label = ThumbnailTitleView(font: Appearance.font)
|
var label = ThumbnailTitleView(font: Appearance.font)
|
||||||
var statusIcons = StatusIconsView()
|
var statusIcons = StatusIconsView()
|
||||||
var dockLabelIcon = ThumbnailFilledFontIconView(
|
var dockLabelIcon = ThumbnailFilledFontIconView(
|
||||||
@@ -165,13 +165,13 @@ class ThumbnailView: FlippedView {
|
|||||||
|
|
||||||
private func setupSharedSubviews() {
|
private func setupSharedSubviews() {
|
||||||
let shadow = ThumbnailView.makeShadow(Appearance.imagesShadowColor)
|
let shadow = ThumbnailView.makeShadow(Appearance.imagesShadowColor)
|
||||||
thumbnail.layer!.masksToBounds = false // let thumbnail shadows show
|
thumbnail.masksToBounds = false // let thumbnail shadows show
|
||||||
thumbnail.shadow = shadow
|
thumbnail.applyShadow(shadow)
|
||||||
appIcon.shadow = shadow
|
appIcon.applyShadow(shadow)
|
||||||
dockLabelIcon.shadow = shadow
|
dockLabelIcon.shadow = shadow
|
||||||
appIcon.setSubviewAbove(dockLabelIcon)
|
layer!.addSublayer(appIcon)
|
||||||
|
addSubview(dockLabelIcon)
|
||||||
label.fixHeight()
|
label.fixHeight()
|
||||||
setSubviews([appIcon])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupStyleSpecificSubviews() {
|
private func setupStyleSpecificSubviews() {
|
||||||
@@ -181,8 +181,9 @@ class ThumbnailView: FlippedView {
|
|||||||
label.alignment = .center
|
label.alignment = .center
|
||||||
label.isHidden = true
|
label.isHidden = true
|
||||||
} else if Preferences.appearanceStyle == .thumbnails {
|
} else if Preferences.appearanceStyle == .thumbnails {
|
||||||
addSubviews([thumbnail, label, statusIcons])
|
layer!.addSublayer(thumbnail)
|
||||||
thumbnail.setSubviewAbove(windowlessAppIndicator)
|
addSubviews([label, statusIcons])
|
||||||
|
setSubviewAbove(windowlessAppIndicator)
|
||||||
} else {
|
} else {
|
||||||
setSubviewAbove(windowlessAppIndicator)
|
setSubviewAbove(windowlessAppIndicator)
|
||||||
addSubviews([label, statusIcons])
|
addSubviews([label, statusIcons])
|
||||||
@@ -260,7 +261,6 @@ class ThumbnailView: FlippedView {
|
|||||||
private func updateAppIcon(_ element: Window, _ title: String) {
|
private func updateAppIcon(_ element: Window, _ title: String) {
|
||||||
let appIconSize = ThumbnailView.iconSize()
|
let appIconSize = ThumbnailView.iconSize()
|
||||||
appIcon.updateContents(.cgImage(element.icon), appIconSize)
|
appIcon.updateContents(.cgImage(element.icon), appIconSize)
|
||||||
appIcon.setAccessibilityLabel(title)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateValues(_ element: Window, _ index: Int, _ newHeight: CGFloat) {
|
private func updateValues(_ element: Window, _ index: Int, _ newHeight: CGFloat) {
|
||||||
@@ -275,7 +275,6 @@ class ThumbnailView: FlippedView {
|
|||||||
)
|
)
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
thumbnail.toolTip = element.isWindowlessApp ? ThumbnailView.noOpenWindowToolTip : nil
|
|
||||||
if !thumbnail.isHidden {
|
if !thumbnail.isHidden {
|
||||||
if let screenshot = element.thumbnail {
|
if let screenshot = element.thumbnail {
|
||||||
let thumbnailSize = ThumbnailView.thumbnailSize(element.size, false)
|
let thumbnailSize = ThumbnailView.thumbnailSize(element.size, false)
|
||||||
@@ -285,8 +284,6 @@ class ThumbnailView: FlippedView {
|
|||||||
let thumbnailSize = ThumbnailView.thumbnailSize(element.icon?.size(), true)
|
let thumbnailSize = ThumbnailView.thumbnailSize(element.icon?.size(), true)
|
||||||
thumbnail.updateContents(.cgImage(element.icon), thumbnailSize)
|
thumbnail.updateContents(.cgImage(element.icon), thumbnailSize)
|
||||||
}
|
}
|
||||||
// for Accessibility > "speak items under the pointer"
|
|
||||||
thumbnail.setAccessibilityLabel(element.title)
|
|
||||||
}
|
}
|
||||||
let title = getAppOrAndWindowTitle()
|
let title = getAppOrAndWindowTitle()
|
||||||
let labelChanged = label.stringValue != title
|
let labelChanged = label.stringValue != title
|
||||||
@@ -342,10 +339,12 @@ class ThumbnailView: FlippedView {
|
|||||||
if Preferences.appearanceStyle == .thumbnails {
|
if Preferences.appearanceStyle == .thumbnails {
|
||||||
let hHeight = max(appIcon.frame.height, ThumbnailsView.layoutCache.labelHeight)
|
let hHeight = max(appIcon.frame.height, ThumbnailsView.layoutCache.labelHeight)
|
||||||
assignIfDifferent(&thumbnail.frame.origin, NSPoint(x: edgeInsets, y: edgeInsets + hHeight + Appearance.intraCellPadding))
|
assignIfDifferent(&thumbnail.frame.origin, NSPoint(x: edgeInsets, y: edgeInsets + hHeight + Appearance.intraCellPadding))
|
||||||
thumbnail.centerFrameInParent(x: true)
|
thumbnail.centerInSuperlayer(x: true)
|
||||||
}
|
}
|
||||||
if !windowlessAppIndicator.isHidden {
|
if !windowlessAppIndicator.isHidden {
|
||||||
if Preferences.appearanceStyle != .titles {
|
if Preferences.appearanceStyle == .thumbnails {
|
||||||
|
windowlessAppIndicator.frame.origin.x = thumbnail.frame.origin.x + ((thumbnail.frame.width - windowlessAppIndicator.frame.width) / 2).rounded()
|
||||||
|
} else if Preferences.appearanceStyle != .titles {
|
||||||
windowlessAppIndicator.centerFrameInParent(x: true)
|
windowlessAppIndicator.centerFrameInParent(x: true)
|
||||||
} else {
|
} else {
|
||||||
windowlessAppIndicator.frame.origin.x = ((appIcon.frame.width / 2) - (windowlessAppIndicator.frame.width / 2)).rounded()
|
windowlessAppIndicator.frame.origin.x = ((appIcon.frame.width / 2) - (windowlessAppIndicator.frame.width / 2)).rounded()
|
||||||
@@ -354,7 +353,7 @@ class ThumbnailView: FlippedView {
|
|||||||
if Preferences.appearanceStyle != .thumbnails {
|
if Preferences.appearanceStyle != .thumbnails {
|
||||||
windowlessAppIndicator.frame.origin.y = windowlessAppIndicator.superview!.frame.height - windowlessAppIndicator.frame.height + 5
|
windowlessAppIndicator.frame.origin.y = windowlessAppIndicator.superview!.frame.height - windowlessAppIndicator.frame.height + 5
|
||||||
} else {
|
} else {
|
||||||
windowlessAppIndicator.frame.origin.y = -5
|
windowlessAppIndicator.frame.origin.y = thumbnail.frame.origin.y - 5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// we set dockLabelIcon origin, without checking if .isHidden
|
// we set dockLabelIcon origin, without checking if .isHidden
|
||||||
@@ -432,9 +431,6 @@ class ThumbnailView: FlippedView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func observeDragAndDrop() {
|
private func observeDragAndDrop() {
|
||||||
// NSImageView instances are registered to drag-and-drop by default
|
|
||||||
thumbnail.unregisterDraggedTypes()
|
|
||||||
appIcon.unregisterDraggedTypes()
|
|
||||||
// we only handle URLs (i.e. not text, image, or other draggable things)
|
// we only handle URLs (i.e. not text, image, or other draggable things)
|
||||||
registerForDraggedTypes([NSPasteboard.PasteboardType(kUTTypeURL as String)])
|
registerForDraggedTypes([NSPasteboard.PasteboardType(kUTTypeURL as String)])
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user