167 lines
4.5 KiB
Swift
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)
|
|
}
|
|
|
|
}
|