From 8250bc70b7ee1e17ef3fb291806c9a3b9a95dac6 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 10:36:20 -0500 Subject: [PATCH] Copy functionality! --- Clocker/Clocker/ca.lproj/Localizable.strings | 2 + Clocker/Clocker/de.lproj/Localizable.strings | 2 + Clocker/Clocker/en.lproj/Localizable.strings | 1 + Clocker/Clocker/es.lproj/Localizable.strings | 2 + Clocker/Clocker/fr.lproj/Localizable.strings | 2 + Clocker/Clocker/hi.lproj/Localizable.strings | 2 + Clocker/Clocker/it.lproj/Localizable.strings | 2 + Clocker/Clocker/ja.lproj/Localizable.strings | 2 + Clocker/Clocker/ko.lproj/Localizable.strings | 2 + Clocker/Clocker/nl.lproj/Localizable.strings | 2 + .../Clocker/pt-BR.lproj/Localizable.strings | 1 + Clocker/Clocker/ru.lproj/Localizable.strings | 2 + .../Clocker/zh-Hans.lproj/Localizable.strings | 2 + .../Clocker/zh-Hant.lproj/Localizable.strings | 1 + Clocker/Panel/UI/TimezoneCellView.swift | 2 + Clocker/Panel/UI/Toasty.swift | 164 ++++++++++++++++++ 16 files changed, 191 insertions(+) create mode 100644 Clocker/Panel/UI/Toasty.swift diff --git a/Clocker/Clocker/ca.lproj/Localizable.strings b/Clocker/Clocker/ca.lproj/Localizable.strings index 48dbfd8..eb4fd3b 100644 --- a/Clocker/Clocker/ca.lproj/Localizable.strings +++ b/Clocker/Clocker/ca.lproj/Localizable.strings @@ -159,3 +159,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/de.lproj/Localizable.strings b/Clocker/Clocker/de.lproj/Localizable.strings index a4e3131..6269afd 100644 --- a/Clocker/Clocker/de.lproj/Localizable.strings +++ b/Clocker/Clocker/de.lproj/Localizable.strings @@ -159,3 +159,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/en.lproj/Localizable.strings b/Clocker/Clocker/en.lproj/Localizable.strings index 8acf703..f2b6950 100644 --- a/Clocker/Clocker/en.lproj/Localizable.strings +++ b/Clocker/Clocker/en.lproj/Localizable.strings @@ -162,3 +162,4 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/es.lproj/Localizable.strings b/Clocker/Clocker/es.lproj/Localizable.strings index 249fe28..47d6fa9 100644 --- a/Clocker/Clocker/es.lproj/Localizable.strings +++ b/Clocker/Clocker/es.lproj/Localizable.strings @@ -161,3 +161,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/fr.lproj/Localizable.strings b/Clocker/Clocker/fr.lproj/Localizable.strings index f5c60db..e823d0b 100644 --- a/Clocker/Clocker/fr.lproj/Localizable.strings +++ b/Clocker/Clocker/fr.lproj/Localizable.strings @@ -160,3 +160,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/hi.lproj/Localizable.strings b/Clocker/Clocker/hi.lproj/Localizable.strings index 7dd021c..451dfc2 100644 --- a/Clocker/Clocker/hi.lproj/Localizable.strings +++ b/Clocker/Clocker/hi.lproj/Localizable.strings @@ -155,3 +155,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/it.lproj/Localizable.strings b/Clocker/Clocker/it.lproj/Localizable.strings index b9a182e..04adcc3 100644 --- a/Clocker/Clocker/it.lproj/Localizable.strings +++ b/Clocker/Clocker/it.lproj/Localizable.strings @@ -160,3 +160,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/ja.lproj/Localizable.strings b/Clocker/Clocker/ja.lproj/Localizable.strings index f7e4229..53cdfea 100644 --- a/Clocker/Clocker/ja.lproj/Localizable.strings +++ b/Clocker/Clocker/ja.lproj/Localizable.strings @@ -159,3 +159,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/ko.lproj/Localizable.strings b/Clocker/Clocker/ko.lproj/Localizable.strings index 2c49183..50ba241 100644 --- a/Clocker/Clocker/ko.lproj/Localizable.strings +++ b/Clocker/Clocker/ko.lproj/Localizable.strings @@ -162,3 +162,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/nl.lproj/Localizable.strings b/Clocker/Clocker/nl.lproj/Localizable.strings index 48dbfd8..eb4fd3b 100644 --- a/Clocker/Clocker/nl.lproj/Localizable.strings +++ b/Clocker/Clocker/nl.lproj/Localizable.strings @@ -159,3 +159,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/pt-BR.lproj/Localizable.strings b/Clocker/Clocker/pt-BR.lproj/Localizable.strings index 0a4e6e0..ac6f642 100644 --- a/Clocker/Clocker/pt-BR.lproj/Localizable.strings +++ b/Clocker/Clocker/pt-BR.lproj/Localizable.strings @@ -161,3 +161,4 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/ru.lproj/Localizable.strings b/Clocker/Clocker/ru.lproj/Localizable.strings index de3f64e..08b025d 100644 --- a/Clocker/Clocker/ru.lproj/Localizable.strings +++ b/Clocker/Clocker/ru.lproj/Localizable.strings @@ -152,3 +152,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/zh-Hans.lproj/Localizable.strings b/Clocker/Clocker/zh-Hans.lproj/Localizable.strings index e705d48..81dd154 100644 --- a/Clocker/Clocker/zh-Hans.lproj/Localizable.strings +++ b/Clocker/Clocker/zh-Hans.lproj/Localizable.strings @@ -153,3 +153,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/zh-Hant.lproj/Localizable.strings b/Clocker/Clocker/zh-Hant.lproj/Localizable.strings index 5874828..23d4d48 100644 --- a/Clocker/Clocker/zh-Hant.lproj/Localizable.strings +++ b/Clocker/Clocker/zh-Hant.lproj/Localizable.strings @@ -161,3 +161,4 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Panel/UI/TimezoneCellView.swift b/Clocker/Panel/UI/TimezoneCellView.swift index 0d8254a..2eaf28c 100644 --- a/Clocker/Panel/UI/TimezoneCellView.swift +++ b/Clocker/Panel/UI/TimezoneCellView.swift @@ -205,6 +205,8 @@ class TimezoneCellView: NSTableCellView { pasteboard.declareTypes([.string], owner: nil) pasteboard.setString(clipboardCopy, forType: .string) + self.window?.contentView?.makeToast("Copied to Clipboard".localized()) + window?.endEditing(for: nil) } diff --git a/Clocker/Panel/UI/Toasty.swift b/Clocker/Panel/UI/Toasty.swift new file mode 100644 index 0000000..86455fe --- /dev/null +++ b/Clocker/Panel/UI/Toasty.swift @@ -0,0 +1,164 @@ +// Copyright © 2015 Abhishek Banthia + +import Foundation +import AppKit + +extension CGRect { + static func center(of layer: CALayer) -> CGPoint { + let parentSize = layer.frame.size + return CGPoint(x: parentSize.width / 2, y: parentSize.height / 2) + } + static func center(of parent: NSView) -> CGPoint { + let parentSize = parent.frame.size + return CGPoint(x: parentSize.width / 2, y: parentSize.height / 6) + } +} + +extension String { + func size(with fontSize: CGFloat) -> CGSize { + let attr: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: NSFont.systemFont(ofSize: fontSize)] + let size = NSString(string: self).size(withAttributes: attr) + return size + } +} + +fileprivate class HideAnimationDelegate: NSObject, CAAnimationDelegate { + private weak var view: NSView? + fileprivate init(view: NSView) { + self.view = view + } + fileprivate static func delegate(forView NSView: NSView) -> CAAnimationDelegate { + return HideAnimationDelegate(view: NSView) + } + fileprivate func animationDidStart(_ anim: CAAnimation) { + view?.layer?.opacity = 0.0 + } + func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { + view?.removeFromSuperview() + view = nil + } +} + +func hideAnimation(view: NSView, style: Style) { + let anim = CABasicAnimation(keyPath: "opacity") + let timing = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) + anim.timingFunction = timing + let currentLayerTime = view.layer?.convertTime(CACurrentMediaTime(), from: nil) + anim.beginTime = currentLayerTime! + CFTimeInterval(style.fadeInOutDelay) + anim.duration = CFTimeInterval(style.fadeInOutDuration) + anim.fromValue = 1.0 + anim.toValue = 0.0 + anim.isRemovedOnCompletion = false + anim.delegate = HideAnimationDelegate.delegate(forView: view) + + view.layer?.add(anim, forKey: "hide animation") +} + +public protocol Style { + var fontSize: CGFloat {get} + var horizontalMargin: CGFloat {get} + var verticalMargin: CGFloat {get} + var cornerRadius: CGFloat {get} + var font: NSFont {get} + var backgroundColor: NSColor {get} + var foregroundColor: NSColor {get} + var fadeInOutDuration: CGFloat {get} + var fadeInOutDelay: CGFloat {get} + var labelOriginWithMargin: CGPoint {get} + var activitySize: CGSize {get} +} + +extension Style { + public var labelOriginWithMargin: CGPoint { + return CGPoint(x: horizontalMargin, y: verticalMargin) + } + public var fontSize: CGFloat {return 12} + public var font: NSFont { + if let avenirFont = NSFont(name: "Avenir-Light", size: fontSize) { + return avenirFont + } + return NSFont.systemFont(ofSize: fontSize) + } + public var horizontalMargin: CGFloat {return 10} + public var verticalMargin: CGFloat {return 5} + public var cornerRadius: CGFloat {return 8} + public var backgroundColor: NSColor {return .black} + public var foregroundColor: NSColor {return .white} + public var activitySize: CGSize {return CGSize(width: 100, height: 100)} + public var fadeInOutDuration: CGFloat {return 1.0} + public var fadeInOutDelay: CGFloat {return 1.0} +} + +public struct DefaultStyle: Style { + public static let shared = DefaultStyle() +} + +private struct ToastKeys { + static var ActiveToast = "TSToastActiveToastKey" +} + +class ToastView: NSView { + private let message: String + private let labelSize: CGSize + private let style: Style + init(message: String) { + self.message = message + self.style = DefaultStyle() + self.labelSize = message.size(with: style.fontSize) + let size = CGSize( + width: labelSize.width + style.horizontalMargin*2, + height: labelSize.height + style.verticalMargin*2 + ) + let rect = CGRect(origin: .zero, size: size) + super.init(frame: rect) + wantsLayer = true + } + required init?(coder aDecoder: NSCoder) { fatalError() } + + override func viewDidMoveToSuperview() { + super.viewDidMoveToSuperview() + if superview != nil { + configure() + } + } + + private func configure() { + frame = superview?.bounds ?? NSRect.zero + let rect = CGRect(origin: style.labelOriginWithMargin, size: labelSize) + let sizeWithMargin = CGSize( + width: rect.width + style.horizontalMargin*2, + height: rect.height + style.verticalMargin*2 + ) + let rectWithMargin = CGRect( + origin: .zero, // position is manipulated later anyways + size: sizeWithMargin + ) + // outside Container + let container = CALayer() + container.frame = rectWithMargin + container.position = CGRect.center(of: superview!) + container.backgroundColor = style.backgroundColor.cgColor + container.cornerRadius = style.cornerRadius + layer?.addSublayer(container) + // inside TextLayer + let text = CATextLayer() + text.frame = rect + text.position = CGRect.center(of: container) + text.string = message + text.font = NSFont.systemFont(ofSize: style.fontSize) + text.fontSize = style.fontSize + text.alignmentMode = .center + text.foregroundColor = style.foregroundColor.cgColor + text.backgroundColor = style.backgroundColor.cgColor + text.contentsScale = layer?.contentsScale ?? 0 // For Retina Display + container.addSublayer(text) + } +} + +extension NSView { + public func makeToast(_ message: String) { + let toast = ToastView(message: message) + self.addSubview(toast) + hideAnimation(view: toast, style: DefaultStyle.shared) + } +}