Skip to content

Commit

Permalink
Indicate that CustomButton is vendored
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Sep 22, 2019
1 parent cf0f6eb commit bc78bf3
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 138 deletions.
6 changes: 5 additions & 1 deletion Gifski.xcodeproj/project.pbxproj
Expand Up @@ -22,6 +22,7 @@
B576D25422294F9900A9B75C /* CircularProgress+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = B576D24F22294F9900A9B75C /* CircularProgress+Util.swift */; };
C2040B8920435871004EE259 /* GifskiWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2040B8820435871004EE259 /* GifskiWrapper.swift */; };
C2AFA91D204FFEFD00FC5A7F /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2AFA91B204FFEFD00FC5A7F /* MainWindowController.swift */; };
E3030388233785C100B8ED1F /* CustomButton+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3030387233785C100B8ED1F /* CustomButton+Util.swift */; };
E30557F0207E521F003401A1 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = E30557EF207E521F003401A1 /* Defaults.swift */; };
E317FF132057E24700A80A18 /* CircularProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = E317FF122057E24700A80A18 /* CircularProgress.swift */; };
E317FF1C20583E9800A80A18 /* DockProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = E317FF1B20583E9800A80A18 /* DockProgress.swift */; };
Expand Down Expand Up @@ -97,6 +98,7 @@
B576D24F22294F9900A9B75C /* CircularProgress+Util.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "CircularProgress+Util.swift"; sourceTree = "<group>"; usesTabs = 1; };
C2040B8820435871004EE259 /* GifskiWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = GifskiWrapper.swift; sourceTree = "<group>"; usesTabs = 1; };
C2AFA91B204FFEFD00FC5A7F /* MainWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = MainWindowController.swift; sourceTree = "<group>"; usesTabs = 1; };
E3030387233785C100B8ED1F /* CustomButton+Util.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "CustomButton+Util.swift"; sourceTree = "<group>"; usesTabs = 1; };
E30557EF207E521F003401A1 /* Defaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Defaults.swift; sourceTree = "<group>"; };
E317FF122057E24700A80A18 /* CircularProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgress.swift; sourceTree = "<group>"; };
E317FF1B20583E9800A80A18 /* DockProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DockProgress.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -164,6 +166,8 @@
B576D24F22294F9900A9B75C /* CircularProgress+Util.swift */,
E317FF1B20583E9800A80A18 /* DockProgress.swift */,
E30557EF207E521F003401A1 /* Defaults.swift */,
E3A940112182DCE5006981D5 /* CustomButton.swift */,
E3030387233785C100B8ED1F /* CustomButton+Util.swift */,
);
path = Vendor;
sourceTree = "<group>";
Expand Down Expand Up @@ -216,7 +220,6 @@
9F3340A222431CC3006EF9B5 /* TimeRemainingEstimator.swift */,
E339F010203820ED003B78FB /* Gifski.swift */,
C2040B8820435871004EE259 /* GifskiWrapper.swift */,
E3A940112182DCE5006981D5 /* CustomButton.swift */,
8588EB0C22A424B800030A59 /* ResizableDimensions.swift */,
8548806422B78E8300E97401 /* IntTextField.swift */,
8548806D22B82D1300E97401 /* MenuPopUpButton.swift */,
Expand Down Expand Up @@ -395,6 +398,7 @@
C2040B8920435871004EE259 /* GifskiWrapper.swift in Sources */,
8588EB0D22A424B800030A59 /* ResizableDimensions.swift in Sources */,
858380EA22BFD38C0086BC98 /* ExtendedAttributes.swift in Sources */,
E3030388233785C100B8ED1F /* CustomButton+Util.swift in Sources */,
E3CB1DD71F7E4CBC00D79BFC /* VideoDropView.swift in Sources */,
85BF910922F3279300AD3FF6 /* TrimmingAVPlayerViewController.swift in Sources */,
8548806522B78E8300E97401 /* IntTextField.swift in Sources */,
Expand Down
135 changes: 135 additions & 0 deletions Gifski/Vendor/CustomButton+Util.swift
@@ -0,0 +1,135 @@
import Cocoa

/**
Convenience class for adding a tracking area to a view.
```
final class HoverView: NSView {
private lazy var trackingArea = TrackingArea(
for: self,
options: [
.mouseEnteredAndExited,
.activeInActiveApp
]
)
override func updateTrackingAreas() {
super.updateTrackingAreas()
trackingArea.update()
}
}
```
*/
final class TrackingArea {
private weak var view: NSView?
private let rect: CGRect
private let options: NSTrackingArea.Options
private var trackingArea: NSTrackingArea?

/**
- Parameters:
- view: The view to add tracking to.
- rect: The area inside the view to track. Defaults to the whole view (`view.bounds`).
*/
init(
for view: NSView,
rect: CGRect? = nil,
options: NSTrackingArea.Options = []
) {
self.view = view
self.rect = rect ?? view.bounds
self.options = options
}

/**
Updates the tracking area.
- Note: This should be called in your `NSView#updateTrackingAreas()` method.
*/
func update() {
if let oldTrackingArea = trackingArea {
view?.removeTrackingArea(oldTrackingArea)
}

let newTrackingArea = NSTrackingArea(
rect: rect,
options: [
.mouseEnteredAndExited,
.activeInActiveApp
],
owner: view,
userInfo: nil
)

view?.addTrackingArea(newTrackingArea)
trackingArea = newTrackingArea
}
}


final class AnimationDelegate: NSObject, CAAnimationDelegate {
var didStopHandler: ((Bool) -> Void)?

func animationDidStop(_ animation: CAAnimation, finished flag: Bool) {
didStopHandler?(flag)
}
}

extension CALayer {
// TODO: Find a way to use a strongly-typed KeyPath here.
// TODO: Accept `NSColor` instead of `CGColor`.
func animate(color: CGColor, keyPath: String, duration: Double) {
guard (value(forKey: keyPath) as! CGColor?) != color else {
return
}

let animation = CABasicAnimation(keyPath: keyPath)
animation.fromValue = value(forKey: keyPath)
animation.toValue = color
animation.duration = duration
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
add(animation, forKey: keyPath) { [weak self] _ in
self?.setValue(color, forKey: keyPath)
}
}

func add(
_ animation: CAAnimation,
forKey key: String?,
completion: @escaping ((Bool) -> Void)
) {
let animationDelegate = AnimationDelegate()
animationDelegate.didStopHandler = completion
animation.delegate = animationDelegate
add(animation, forKey: key)
}
}


extension CGPoint {
func rounded(_ rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero) -> Self {
Self(x: x.rounded(rule), y: y.rounded(rule))
}
}


extension CGRect {
func roundedOrigin(_ rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero) -> Self {
var rect = self
rect.origin = rect.origin.rounded(rule)
return rect
}
}


extension CGSize {
/// Returns a CGRect with `self` centered in it.
func centered(in rect: CGRect) -> CGRect {
CGRect(
x: (rect.width - width) / 2,
y: (rect.height - height) / 2,
width: width,
height: height
)
}
}
142 changes: 5 additions & 137 deletions Gifski/CustomButton.swift → Gifski/Vendor/CustomButton.swift
@@ -1,138 +1,6 @@
// Vendored from: https://github.com/sindresorhus/CustomButton
import Cocoa

// TODO(sindresorhus): I plan to extract this into a reusable package when it's more mature.

/**
Convenience class for adding a tracking area to a view.
```
final class HoverView: NSView {
private lazy var trackingArea = TrackingArea(
for: self,
options: [
.mouseEnteredAndExited,
.activeInActiveApp
]
)
override func updateTrackingAreas() {
super.updateTrackingAreas()
trackingArea.update()
}
}
```
*/
final class TrackingArea {
private weak var view: NSView?
private let rect: CGRect
private let options: NSTrackingArea.Options
private var trackingArea: NSTrackingArea?

/**
- Parameters:
- view: The view to add tracking to.
- rect: The area inside the view to track. Defaults to the whole view (`view.bounds`).
*/
init(
for view: NSView,
rect: CGRect? = nil,
options: NSTrackingArea.Options = []
) {
self.view = view
self.rect = rect ?? view.bounds
self.options = options
}

/**
Updates the tracking area.
- Note: This should be called in your `NSView#updateTrackingAreas()` method.
*/
func update() {
if let oldTrackingArea = trackingArea {
view?.removeTrackingArea(oldTrackingArea)
}

let newTrackingArea = NSTrackingArea(
rect: rect,
options: [
.mouseEnteredAndExited,
.activeInActiveApp
],
owner: view,
userInfo: nil
)

view?.addTrackingArea(newTrackingArea)
trackingArea = newTrackingArea
}
}

final class AnimationDelegate: NSObject, CAAnimationDelegate {
var didStopHandler: ((Bool) -> Void)?

func animationDidStop(_ animation: CAAnimation, finished flag: Bool) {
didStopHandler?(flag)
}
}

extension CALayer {
// TODO: Find a way to use a strongly-typed KeyPath here.
// TODO: Accept `NSColor` instead of `CGColor`.
func animate(color: CGColor, keyPath: String, duration: Double) {
guard (value(forKey: keyPath) as! CGColor?) != color else {
return
}

let animation = CABasicAnimation(keyPath: keyPath)
animation.fromValue = value(forKey: keyPath)
animation.toValue = color
animation.duration = duration
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
add(animation, forKey: keyPath) { [weak self] _ in
self?.setValue(color, forKey: keyPath)
}
}

func add(
_ animation: CAAnimation,
forKey key: String?,
completion: @escaping ((Bool) -> Void)
) {
let animationDelegate = AnimationDelegate()
animationDelegate.didStopHandler = completion
animation.delegate = animationDelegate
add(animation, forKey: key)
}
}

extension CGPoint {
func rounded(_ rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero) -> Self {
Self(x: x.rounded(rule), y: y.rounded(rule))
}
}

extension CGRect {
func roundedOrigin(_ rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero) -> Self {
var rect = self
rect.origin = rect.origin.rounded(rule)
return rect
}
}

extension CGSize {
/// Returns a CGRect with `self` centered in it.
func centered(in rect: CGRect) -> CGRect {
CGRect(
x: (rect.width - width) / 2,
y: (rect.height - height) / 2,
width: width,
height: height
)
}
}

// TODO: Add padding option.
@IBDesignable
open class CustomButton: NSButton {
private let titleLayer = CATextLayer()
Expand All @@ -149,7 +17,7 @@ open class CustomButton: NSButton {

override open var wantsUpdateLayer: Bool { true }

@IBInspectable override open var title: String {
@IBInspectable override public var title: String {
didSet {
setTitle()
}
Expand Down Expand Up @@ -251,13 +119,13 @@ open class CustomButton: NSButton {
}
}

override open var font: NSFont? {
override public var font: NSFont? {
didSet {
setTitle()
}
}

override open var isEnabled: Bool {
override public var isEnabled: Bool {
didSet {
alphaValue = isEnabled ? 1 : 0.6
}
Expand Down Expand Up @@ -328,7 +196,7 @@ open class CustomButton: NSButton {
/// Gets or sets the color generation closure for the provided key path.
///
/// - Parameter keyPath: The key path that specifies the color related property.
subscript(colorGenerator keyPath: KeyPath<CustomButton, NSColor>) -> ColorGenerator? {
public subscript(colorGenerator keyPath: KeyPath<CustomButton, NSColor>) -> ColorGenerator? {
get { colorGenerators[keyPath] }
set {
colorGenerators[keyPath] = newValue
Expand Down

0 comments on commit bc78bf3

Please sign in to comment.