// // KMAnimatedBorderlessWindow.swift // PDF Reader Pro // // Created by tangchao on 2023/11/23. // import Cocoa @objcMembers class KMAnimatedBorderlessWindow: NSPanel { var defaultAlphaValue: CGFloat = 0 var autoHideTimeInterval: TimeInterval = 0 var fadeInDuration: TimeInterval { get { return FADE_IN_DURATION } } var fadeOutDuration: TimeInterval { get { return FADE_OUT_DURATION } } var backgroundImage: NSImage? { get { if let _ = self.contentView?.responds(to: NSSelectorFromString("image")) { return (self.contentView as? NSImageView)?.image } return nil } set { var imageView: NSImageView? if let data = self.contentView?.responds(to: NSSelectorFromString("setImage:")), data { imageView = self.contentView as? NSImageView } else if newValue != nil { imageView = NSImageView() imageView?.isEditable = false imageView?.imageFrameStyle = .none imageView?.imageScaling = .scaleProportionallyDown self.contentView = imageView } imageView?.image = newValue } } private let ALPHA_VALUE = 1.0 private let FADE_IN_DURATION = 0.3 private let FADE_OUT_DURATION = 1.0 private let AUTO_HIDE_TIME_INTERVAL = 0.0 deinit { KMPrint("KMAnimatedBorderlessWindow deinit.") self.stopAnimation() } convenience init(contentRect: NSRect) { self.init(contentRect: contentRect, styleMask: [.borderless], backing: .buffered, defer: false) self.defaultAlphaValue = ALPHA_VALUE self.autoHideTimeInterval = AUTO_HIDE_TIME_INTERVAL self.backgroundColor = .clear self.isOpaque = false self.alphaValue = self.defaultAlphaValue self.isReleasedWhenClosed = false self.hidesOnDeactivate = false if self.responds(to: NSSelectorFromString("setAnimationBehavior:")) { self.animationBehavior = .none } } override var canBecomeKey: Bool { return false } override var canBecomeMain: Bool { return false } override func accessibilityIsIgnored() -> Bool { return true } // MARK: - Public Methods public func fadeIn() { self.stopAnimation() if self.isVisible == false { self.alphaValue = 0.0 } super.orderFront(self) if UserDefaults.standard.bool(forKey: SKDisableAnimationsKey) { self.alphaValue = self.defaultAlphaValue } else { NSAnimationContext.runAnimationGroup { context in context.duration = self.fadeInDuration self.animator().alphaValue = self.defaultAlphaValue } } self._fadeOutAfterTimeout() } @objc public func fadeOut() { self.stopAnimation() self.alphaValue = self.defaultAlphaValue if UserDefaults.standard.bool(forKey: SKDisableAnimationsKey) { self.remove() } else { NSAnimationContext.runAnimationGroup { context in context.duration = self.fadeOutDuration self.animator().alphaValue = 0.0 } // don't put this in the completionHandler, because we want to be able to stop this using stopAnimation self.perform(#selector(remove), with: nil, afterDelay: self.fadeOutDuration) } } @objc public func remove() { self.orderOut(nil) } override func orderOut(_ sender: Any?) { self.stopAnimation() super.orderOut(sender) self.alphaValue = self.defaultAlphaValue } override func orderFront(_ sender: Any?) { self.stopAnimation() self.alphaValue = self.defaultAlphaValue super.orderFront(sender) self._fadeOutAfterTimeout() } override func orderFrontRegardless() { self.stopAnimation() self.alphaValue = self.defaultAlphaValue super.orderFrontRegardless() self._fadeOutAfterTimeout() } public func stopAnimation() { Self.cancelPreviousPerformRequests(withTarget: self, selector: #selector(fadeOut), object: nil) Self.cancelPreviousPerformRequests(withTarget: self, selector: #selector(remove), object: nil) } } // MARK: - Private Methods extension KMAnimatedBorderlessWindow { private func _fadeOutAfterTimeout() { if self.autoHideTimeInterval > 0.0 { self.perform(#selector(fadeOut), with: nil, afterDelay: self.autoHideTimeInterval) } } }