Files
2024-06-29 16:20:21 -03:00

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()
}
}