// // KMSyncDot.swift // PDF Reader Pro // // Created by lizhe on 2024/2/29. // import Cocoa import QuartzCore class KMSyncDot: NSObject { var point: NSPoint var page: CPDFPage? private var phase: Int = 0 private var timer: Timer? private var handler: ((Bool) -> Void)? init(point: NSPoint, page: CPDFPage?, updateHandler: ((Bool) -> Void)?) { self.point = point self.page = page self.handler = updateHandler super.init() setup() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setup() { timer = Timer.scheduledTimer(timeInterval: 0.05, target: self, selector: #selector(animate(_:)), userInfo: nil, repeats: true) } deinit { timer?.invalidate() handler = nil } @objc private func finish(_ timer: Timer) { timer.invalidate() if let handler = handler { handler(true) } } @objc private func animate(_ timer: Timer) { phase += 1 if phase >= 10 { timer.invalidate() self.timer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(finish(_:)), userInfo: nil, repeats: false) } if let handler = handler { handler(false) } } func invalidate() { timer?.invalidate() } func bounds() -> NSRect { return KMRectFromCenterAndSquareSize(center: point, size: 22.0) ?? .zero } func draw(in ctx: CGContext) { ctx.saveGState() var s: CGFloat = 6.0 OSMemoryBarrier() if phase < 10 { s += 8.0 * sin(CGFloat(phase) * 0.1 * .pi) let components: [CGFloat] = [1.0, 0.3, 0.3, 1.0, 1.0, 0.0, 0.0, 1.0] let locations: [CGFloat] = [0.0, 1.0] let colorspace = CGColorSpaceCreateDeviceRGB() if let gradient = CGGradient(colorSpace: colorspace, colorComponents: components, locations: locations, count: 2) { ctx.addEllipse(in: CGRect(x: point.x - 0.5 * s, y: point.y - 0.5 * s, width: s, height: s)) ctx.clip() ctx.drawRadialGradient(gradient, startCenter: CGPoint(x: point.x, y: point.y + 0.35 * s), startRadius: 0.0, endCenter: CGPoint(x: point.x, y: point.y), endRadius: 0.5 * s, options: []) } } else { ctx.setBlendMode(.multiply) ctx.setFillColor(CGColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)) ctx.fillEllipse(in: CGRect(x: point.x - 0.5 * s, y: point.y - 0.5 * s, width: s, height: s)) } ctx.restoreGState() } }