Files

167 lines
4.5 KiB
Swift

//
// ImageView.swift
// Cyberlock
//
// Created by Juraldinio on 9/1/19.
// Copyright © 2019 Omicronmedia. All rights reserved.
//
import Foundation
import AppKit
final class ImageView: NSImageView {
typealias TrackingMouseOverClosure = (_ imageView: ImageView, _ isHover: Bool) -> Void
private var trackingArea: NSTrackingArea?
var roundImage = false
var imageUrl: URL? {
didSet {
guard let url = self.imageUrl else {
self.image = nil
return
}
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let image = NSImage(contentsOf: url) else { return }
DispatchQueue.main.async {
self?.image = image
}
}
}
}
var allowLeftClickMenu: Bool = false
var useHandCursor = false {
didSet {
self.window?.invalidateCursorRects(for: self)
}
}
var mouseOverTracker: TrackingMouseOverClosure? {
didSet {
self.setupTrackingArea(enable: self.mouseOverTracker.isExist)
}
}
var increaseHitArea = false
var isWindowDragging = false
var isDocked: Bool?
private var canBeDragged = false
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.setup()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setup() {
self.setContentCompressionResistancePriority(.windowSizeStayPut, for: .horizontal)
self.setContentCompressionResistancePriority(.windowSizeStayPut, for: .vertical)
self.imageScaling = .scaleProportionallyUpOrDown
self.translatesAutoresizingMaskIntoConstraints = false
self.wantsLayer = true
}
private func setupTrackingArea(enable: Bool) {
if let trackingArea = self.trackingArea {
self.removeTrackingArea(trackingArea)
}
guard enable else { return }
let area = NSTrackingArea.init(rect: self.bounds,
options: [NSTrackingArea.Options.mouseEnteredAndExited, NSTrackingArea.Options.activeAlways],
owner: self,
userInfo: nil)
self.trackingArea = area
self.addTrackingArea(area)
}
// MARK: - LifeCycle
override func layout() {
super.layout()
self.setupTrackingArea(enable: self.mouseOverTracker.isExist)
// Round image
guard self.roundImage else { return }
let radius = self.bounds.width / 2.0
self.layer?.cornerRadius = radius
}
override func resetCursorRects() {
super.resetCursorRects()
guard self.useHandCursor else { return }
self.addCursorRect(self.bounds, cursor: NSCursor.pointingHand)
}
override func mouseDown(with event: NSEvent) {
guard self.useHandCursor else { return }
if self.allowLeftClickMenu
, let menu = self.menu {
NSMenu.popUpContextMenu(menu, with: event, for: self)
return
}
guard let action = self.action else {
super.mouseDown(with: event)
return
}
NSApp.sendAction(action, to: self.target, from: self)
}
override func hitTest(_ point: NSPoint) -> NSView? {
if self.isWindowDragging {
let view = super.hitTest(point)
self.canBeDragged = view == self
return view
}
guard self.useHandCursor else { return nil }
guard self.increaseHitArea else {
return super.hitTest(point)
}
return self.frame.insetBy(dx: -5, dy: -5).contains(point)
? self
: nil
}
override func mouseEntered(with event: NSEvent) {
super.mouseEntered(with: event)
self.mouseOverTracker?(self, true)
}
override func mouseExited(with event: NSEvent) {
super.mouseExited(with: event)
self.mouseOverTracker?(self, false)
}
override func mouseDragged(with event: NSEvent) {
guard let isDocked = self.isDocked, !isDocked, self.canBeDragged else { return }
self.window?.performDrag(with: event)
}
}