242 lines
8.1 KiB
Swift
242 lines
8.1 KiB
Swift
//
|
|
// ImageButton.swift
|
|
// TGUIKit
|
|
//
|
|
// Created by keepcoder on 26/09/2016.
|
|
// Copyright © 2016 Telegram. All rights reserved.
|
|
//
|
|
|
|
import Cocoa
|
|
|
|
public enum ButtonHoverPolicy {
|
|
case none
|
|
case enlarge(value: CGFloat)
|
|
}
|
|
|
|
public enum ButtonBackgroundCornerRadius {
|
|
case none
|
|
case appSpecific
|
|
case half
|
|
}
|
|
|
|
public enum ImageButtonAnimationPolicy {
|
|
case animateContents
|
|
case replaceScale
|
|
}
|
|
|
|
open class ImageButton: Button {
|
|
|
|
internal private(set) var imageView:ImageView = ImageView()
|
|
internal let additionBackgroundView: View = View()
|
|
|
|
private var additionStateBackground:[ControlState:NSColor] = [:]
|
|
private var cornerRadius:[ControlState : ButtonBackgroundCornerRadius] = [:]
|
|
|
|
private var hoverAdditionPolicy:[ControlState : ButtonHoverPolicy] = [:]
|
|
|
|
private var additionBackgroundMultiplier:[ControlState: CGFloat] = [:]
|
|
|
|
private var images:[ControlState:CGImage] = [:]
|
|
|
|
private var backgroundImage:[ControlState:CGImage] = [:]
|
|
|
|
|
|
public func removeImage(for state:ControlState) {
|
|
images.removeValue(forKey: state)
|
|
apply(state: self.controlState)
|
|
|
|
}
|
|
|
|
public func setImageContentGravity(_ gravity: CALayerContentsGravity) {
|
|
imageView.contentGravity = gravity
|
|
}
|
|
|
|
public func set(image:CGImage, for state:ControlState) -> Void {
|
|
if images[state] != image {
|
|
images[state] = image
|
|
apply(state: self.controlState)
|
|
}
|
|
}
|
|
|
|
open override func viewDidChangeBackingProperties() {
|
|
super.viewDidChangeBackingProperties()
|
|
}
|
|
|
|
override func prepare() {
|
|
super.prepare()
|
|
imageView.animates = true
|
|
additionBackgroundView.isEventLess = true
|
|
// imageView.isEventLess = true
|
|
self.addSubview(additionBackgroundView)
|
|
self.addSubview(imageView)
|
|
}
|
|
|
|
public func set(additionBackgroundColor:NSColor, for state:ControlState) -> Void {
|
|
additionStateBackground[state] = additionBackgroundColor
|
|
apply(state: self.controlState)
|
|
}
|
|
public func set(additionBackgroundMultiplier: CGFloat, for state:ControlState) -> Void {
|
|
self.additionBackgroundMultiplier[state] = additionBackgroundMultiplier
|
|
apply(state: self.controlState)
|
|
}
|
|
|
|
|
|
public func set(cornerRadius: ButtonBackgroundCornerRadius, for state:ControlState) -> Void {
|
|
self.cornerRadius[state] = cornerRadius
|
|
apply(state: self.controlState)
|
|
}
|
|
public func set(hoverAdditionPolicy: ButtonHoverPolicy, for state:ControlState) -> Void {
|
|
self.hoverAdditionPolicy[state] = hoverAdditionPolicy
|
|
apply(state: self.controlState)
|
|
}
|
|
|
|
|
|
|
|
|
|
public override var animates: Bool {
|
|
didSet {
|
|
imageView.animates = animates
|
|
}
|
|
}
|
|
|
|
private var previousState: ControlState?
|
|
|
|
override public func apply(state: ControlState) {
|
|
let previous = self.previousState
|
|
let state:ControlState = self.isSelected ? .Highlight : state
|
|
super.apply(state: state)
|
|
self.previousState = state
|
|
|
|
|
|
let updated: CGImage?
|
|
|
|
if let image = images[state], isEnabled {
|
|
updated = image
|
|
} else if state == .Highlight && autohighlight, isEnabled, let image = images[.Normal] {
|
|
updated = style.highlight(image: image)
|
|
} else if state == .Hover && highlightHovered, isEnabled, let image = images[.Normal] {
|
|
updated = style.highlight(image: image)
|
|
} else {
|
|
updated = images[.Normal]
|
|
}
|
|
|
|
if imageView.image != updated {
|
|
self.imageView.image = updated
|
|
}
|
|
// if state != previousState {
|
|
self.imageView.animator().alphaValue = isEnabled ? 1 : 0.8
|
|
|
|
if let policy = self.hoverAdditionPolicy[state], previous != state {
|
|
switch policy {
|
|
case .none:
|
|
break
|
|
case let .enlarge(value):
|
|
let current = additionBackgroundView.layer?.presentation()?.value(forKeyPath: "transform.scale") as? CGFloat ?? 1.0
|
|
additionBackgroundView.layer?.animateScaleSpring(from: current, to: value, duration: 0.35, removeOnCompletion: false)
|
|
}
|
|
}
|
|
|
|
if let color = self.additionStateBackground[state] ?? self.additionStateBackground[.Normal] {
|
|
additionBackgroundView.backgroundColor = color
|
|
} else {
|
|
additionBackgroundView.backgroundColor = .clear
|
|
}
|
|
|
|
updateLayout()
|
|
|
|
if let cornerRadius = self.cornerRadius[state] {
|
|
switch cornerRadius {
|
|
case .none:
|
|
self.layer?.cornerRadius = 0
|
|
self.additionBackgroundView.layer?.cornerRadius = 0
|
|
case .appSpecific:
|
|
self.layer?.cornerRadius = .cornerRadius
|
|
self.additionBackgroundView.layer?.cornerRadius = .cornerRadius
|
|
case .half:
|
|
self.layer?.cornerRadius = max(frame.width, frame.height) / 2
|
|
self.additionBackgroundView.layer?.cornerRadius = max(additionBackgroundView.frame.width, additionBackgroundView.frame.height) / 2
|
|
}
|
|
}
|
|
//}
|
|
}
|
|
|
|
public func applyAnimation(from: CGImage, to: CGImage, animation: ImageButtonAnimationPolicy) {
|
|
switch animation {
|
|
case .animateContents:
|
|
self.imageView.image = to
|
|
case .replaceScale:
|
|
let imageView = self.imageView
|
|
imageView.image = from
|
|
let newImageView = ImageView()
|
|
self.imageView = newImageView
|
|
newImageView.image = to
|
|
newImageView.sizeToFit()
|
|
addSubview(newImageView)
|
|
newImageView.center()
|
|
|
|
|
|
imageView.layer?.animateScaleCenter(from: 1, to: 0.1, duration: 0.25, removeOnCompletion: false, completion: { [weak imageView] _ in
|
|
imageView?.removeFromSuperview()
|
|
})
|
|
imageView.layer?.animateAlpha(from: 1, to: 0, duration: 0.2, removeOnCompletion: false)
|
|
|
|
newImageView.layer?.animateScaleCenter(from: 0.1, to: 1, duration: 0.25, removeOnCompletion: true)
|
|
}
|
|
}
|
|
|
|
public func disableActions() {
|
|
animates = false
|
|
self.layer?.disableActions()
|
|
layer?.removeAllAnimations()
|
|
imageView.animates = false
|
|
// imageView.layer?.disableActions()
|
|
}
|
|
|
|
open override func setFrameOrigin(_ newOrigin: NSPoint) {
|
|
super.setFrameOrigin(newOrigin)
|
|
}
|
|
|
|
@discardableResult override public func sizeToFit(_ addition: NSSize = NSZeroSize, _ maxSize:NSSize = NSZeroSize, thatFit:Bool = false) -> Bool {
|
|
_ = super.sizeToFit(addition, maxSize, thatFit: thatFit)
|
|
|
|
if let image = images[.Normal] {
|
|
var size = image.backingSize
|
|
|
|
if maxSize.width > 0 || maxSize.height > 0 {
|
|
size = maxSize
|
|
}
|
|
|
|
size.width += addition.width
|
|
size.height += addition.height
|
|
self.setFrameSize(size)
|
|
}
|
|
return true
|
|
}
|
|
|
|
open override func layout() {
|
|
super.layout()
|
|
updateLayout()
|
|
}
|
|
|
|
public override func updateLayout() {
|
|
if let image = self.imageView.image {
|
|
switch imageView.contentGravity {
|
|
case .resize, .resizeAspectFill:
|
|
imageView.setFrameSize(frame.size)
|
|
default:
|
|
imageView.setFrameSize(image.backingSize)
|
|
}
|
|
}
|
|
imageView.center()
|
|
|
|
if let multiplier = additionBackgroundMultiplier[controlState] {
|
|
additionBackgroundView.setFrameSize(NSMakeSize(floorToScreenPixels(backingScaleFactor, frame.width * multiplier), floorToScreenPixels(backingScaleFactor, frame.height * multiplier)))
|
|
} else {
|
|
additionBackgroundView.setFrameSize(frame.size)
|
|
}
|
|
|
|
additionBackgroundView.center()
|
|
}
|
|
|
|
}
|