294 lines
10 KiB
Swift
294 lines
10 KiB
Swift
//
|
|
// MagnifyView.swift
|
|
// TGUIKit
|
|
//
|
|
// Created by keepcoder on 15/12/2016.
|
|
// Copyright © 2016 Telegram. All rights reserved.
|
|
//
|
|
|
|
import Cocoa
|
|
import SwiftSignalKit
|
|
open class MagnifyView : NSView {
|
|
|
|
public private(set) var magnify:CGFloat = 1.0 {
|
|
didSet {
|
|
}
|
|
}
|
|
public var maxMagnify:CGFloat = 8.0
|
|
public var minMagnify:CGFloat = 1.0
|
|
|
|
private let smartUpdater:Promise<NSSize> = Promise()
|
|
|
|
public var smartUpdaterValue: Signal<NSSize, NoError> {
|
|
return smartUpdater.get() |> distinctUntilChanged
|
|
}
|
|
|
|
fileprivate let magnifyUpdater:Promise<CGFloat> = Promise(1)
|
|
|
|
public var magnifyUpdaterValue:Signal<CGFloat, NoError> {
|
|
return magnifyUpdater.get() |> distinctUntilChanged
|
|
}
|
|
|
|
private var mov_start:NSPoint = NSZeroPoint
|
|
private var mov_content_start:NSPoint = NSZeroPoint
|
|
|
|
|
|
public private(set) var contentView:NSView
|
|
let containerView:NSView = NSView()
|
|
open var contentSize:NSSize = NSZeroSize {
|
|
didSet {
|
|
if abs(oldValue.width - contentSize.width) > 1 || abs(oldValue.height - contentSize.height) > 1 {
|
|
contentView.frame = focus(magnifiedSize)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public var contentFrame: NSRect {
|
|
return contentView.frame.apply(multiplier: NSMakeSize(1 / magnify, 1 / magnify))
|
|
}
|
|
|
|
public var contentFrameMagnified: NSRect {
|
|
return contentView.frame
|
|
}
|
|
|
|
private var magnifiedSize:NSSize {
|
|
return NSMakeSize(floorToScreenPixels(backingScaleFactor, contentSize.width * magnify), floorToScreenPixels(backingScaleFactor, contentSize.height * magnify))
|
|
}
|
|
|
|
public func swapView(_ newView: NSView) {
|
|
self.contentView.removeFromSuperview()
|
|
newView.removeFromSuperview()
|
|
self.contentView = newView
|
|
containerView.addSubview(newView)
|
|
resetMagnify()
|
|
}
|
|
|
|
public init(_ contentView:NSView, contentSize:NSSize) {
|
|
self.contentView = contentView
|
|
contentView.setFrameSize(contentSize)
|
|
self.contentSize = contentSize
|
|
contentView.wantsLayer = true
|
|
super.init(frame: NSZeroRect)
|
|
wantsLayer = true
|
|
containerView.wantsLayer = true
|
|
addSubview(containerView)
|
|
containerView.addSubview(contentView)
|
|
containerView.autoresizesSubviews = false
|
|
contentView.background = .clear
|
|
background = .clear
|
|
smartUpdater.set(.single(contentSize))
|
|
}
|
|
|
|
public func resetMagnify() {
|
|
magnify = 1.0
|
|
contentView.setFrameSize(magnifiedSize)
|
|
contentView.center()
|
|
}
|
|
|
|
public func focusContentView() {
|
|
contentView.center()
|
|
}
|
|
|
|
public func zoomIn() {
|
|
add(magnify: 0.5, for: NSMakePoint(containerView.frame.width/2, containerView.frame.height/2), animated: true)
|
|
magnifyUpdater.set(.single(magnify) |> delay(0.2, queue: .mainQueue()))
|
|
}
|
|
|
|
public func zoomOut() {
|
|
add(magnify: -0.5, for: NSMakePoint(containerView.frame.width/2, containerView.frame.height/2), animated: true)
|
|
magnifyUpdater.set(.single(magnify) |> delay(0.2, queue: .mainQueue()))
|
|
}
|
|
|
|
open override func layout() {
|
|
super.layout()
|
|
updateLayout(size: frame.size, transition: .immediate)
|
|
}
|
|
|
|
public func updateLayout(size: NSSize, transition: ContainedViewLayoutTransition) {
|
|
transition.updateFrame(view: containerView, frame: bounds)
|
|
transition.updateFrame(view: contentView, frame: contentView.centerFrame())
|
|
}
|
|
|
|
required public init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override open func magnify(with event: NSEvent) {
|
|
// super.magnify(with: event)
|
|
|
|
add(magnify: event.magnification, for: containerView.convert(event.locationInWindow, from: nil))
|
|
|
|
if event.phase == .ended {
|
|
smartUpdater.set(.single(magnifiedSize) |> delay(0.3, queue: Queue.mainQueue()))
|
|
magnifyUpdater.set(.single(magnify) |> delay(0.2, queue: .mainQueue()))
|
|
} else if event.phase == .began {
|
|
smartUpdater.set(smartUpdater.get())
|
|
}
|
|
}
|
|
|
|
override open func smartMagnify(with event: NSEvent) {
|
|
// super.smartMagnify(with: event)
|
|
addSmart(for: containerView.convert(event.locationInWindow, from: nil))
|
|
smartUpdater.set(.single(magnifiedSize) |> delay(0.2, queue: Queue.mainQueue()))
|
|
magnifyUpdater.set(.single(magnify) |> delay(0.2, queue: .mainQueue()))
|
|
}
|
|
|
|
func addSmart(for location:NSPoint) {
|
|
var minFactor:CGFloat = min(max(floor(frame.size.width / magnifiedSize.width), floor(frame.size.height / magnifiedSize.height)),2.0)
|
|
if magnify > 1.0 {
|
|
minFactor = 1 - magnify
|
|
}
|
|
add(magnify: minFactor, for: location, animated: true)
|
|
}
|
|
|
|
open func add(magnify:CGFloat, for location:NSPoint, animated:Bool = false) {
|
|
self.magnify += magnify
|
|
self.magnify = min(max(minMagnify,self.magnify),maxMagnify)
|
|
let point = magnifyOrigin( for: location, from:contentView.frame, factor: magnify)
|
|
|
|
//contentView.change(pos: point, animated: animated)
|
|
// contentView.change(size: magnifiedSize, animated: animated)
|
|
// content.layer?.animateScaleCenter(from: <#T##CGFloat#>, to: <#T##CGFloat#>, duration: <#T##Double#>)
|
|
//content.anch
|
|
|
|
|
|
|
|
let content = animated ? contentView.animator() : contentView
|
|
content.frame = NSMakeRect(point.x, point.y, magnifiedSize.width, magnifiedSize.height)
|
|
|
|
|
|
}
|
|
|
|
func magnifyOrigin(for location:NSPoint, from past:NSRect, factor:CGFloat) -> NSPoint {
|
|
|
|
var point:NSPoint = past.origin
|
|
let focused = focus(magnifiedSize).origin
|
|
if NSPointInRect(location, contentView.frame) {
|
|
if magnifiedSize.width < frame.width {
|
|
point.x = focused.x
|
|
} else {
|
|
point.x -= (magnifiedSize.width - past.width) * ((location.x - past.minX) / past.width)
|
|
point = adjust(with: point, adjustX: true, adjustY: false)
|
|
|
|
}
|
|
if magnifiedSize.height < frame.height {
|
|
point.y = focused.y
|
|
} else {
|
|
point.y -= (magnifiedSize.height - past.height) * ((location.y - past.minY) / past.height)
|
|
point = adjust(with: point, adjustX: false, adjustY: true)
|
|
}
|
|
|
|
} else {
|
|
point = focused
|
|
}
|
|
return point
|
|
}
|
|
|
|
override open func mouseDown(with theEvent: NSEvent) {
|
|
self.mov_start = convert(theEvent.locationInWindow, from: nil)
|
|
self.mov_content_start = contentView.frame.origin
|
|
}
|
|
|
|
override open func mouseUp(with theEvent: NSEvent) {
|
|
self.mov_start = NSZeroPoint
|
|
self.mov_content_start = NSZeroPoint
|
|
super.mouseUp(with: theEvent)
|
|
}
|
|
|
|
override open func mouseDragged(with theEvent: NSEvent) {
|
|
super.mouseDragged(with: theEvent)
|
|
if (mov_start.x == 0 || mov_start.y == 0) || (frame.width > magnifiedSize.width && frame.height > magnifiedSize.height) {
|
|
return
|
|
}
|
|
var current = convert(theEvent.locationInWindow, from: nil)
|
|
current = NSMakePoint(current.x - mov_start.x, current.y - mov_start.y)
|
|
|
|
let adjust = self.adjust(with: NSMakePoint(mov_content_start.x + current.x, mov_content_start.y + current.y))
|
|
|
|
var point = contentView.frame.origin
|
|
if magnifiedSize.width > frame.width {
|
|
point.x = adjust.x
|
|
}
|
|
if magnifiedSize.height > frame.height {
|
|
point.y = adjust.y
|
|
}
|
|
|
|
contentView.setFrameOrigin(point)
|
|
|
|
|
|
}
|
|
|
|
private func adjust(with point:NSPoint, adjustX: Bool = true, adjustY: Bool = true) -> NSPoint {
|
|
var point = point
|
|
if adjustX {
|
|
point.x = floorToScreenPixels(backingScaleFactor, max(min(0, point.x), point.x + (frame.width - (point.x + magnifiedSize.width))))
|
|
}
|
|
if adjustY {
|
|
point.y = floorToScreenPixels(backingScaleFactor, max(min(0, point.y), point.y + (frame.height - (point.y + magnifiedSize.height))))
|
|
}
|
|
return point
|
|
}
|
|
|
|
override open func scrollWheel(with event: NSEvent) {
|
|
|
|
if magnify == minMagnify {
|
|
super.scrollWheel(with: event)
|
|
//return
|
|
}
|
|
|
|
if event.type == .smartMagnify || event.type == .magnify {
|
|
return
|
|
}
|
|
|
|
|
|
let content_f = contentView.frame.origin
|
|
if (content_f.x == 0 && event.scrollingDeltaX > 0) || (content_f.x == (frame.width - magnifiedSize.width) && event.scrollingDeltaX < 0) {
|
|
// super.scrollWheel(with: event)
|
|
// return
|
|
}
|
|
|
|
if (content_f.y == 0 && event.scrollingDeltaY < 0) || (content_f.y == (frame.height - magnifiedSize.height) && event.scrollingDeltaY > 0) {
|
|
// super.scrollWheel(with: event)
|
|
// return
|
|
}
|
|
|
|
var point = content_f
|
|
let adjust = self.adjust(with: NSMakePoint(content_f.x + event.scrollingDeltaX, content_f.y + -event.scrollingDeltaY))
|
|
if event.scrollingDeltaX != 0 && magnifiedSize.width > frame.width {
|
|
point.x = adjust.x
|
|
}
|
|
if event.scrollingDeltaY != 0 && magnifiedSize.height > frame.height {
|
|
point.y = adjust.y
|
|
}
|
|
if point.equalTo(content_f) {
|
|
// super.scrollWheel(with: event)
|
|
return
|
|
}
|
|
|
|
contentView.setFrameOrigin(point)
|
|
}
|
|
|
|
deinit {
|
|
var bp:Int = 0
|
|
bp += 1
|
|
}
|
|
|
|
|
|
open func mouseInside() -> Bool {
|
|
return super._mouseInside()
|
|
}
|
|
|
|
|
|
public var mouseInContent:Bool {
|
|
if let window = window {
|
|
let point = window.mouseLocationOutsideOfEventStream
|
|
return NSPointInRect(convert(point, from: nil), contentView.frame)
|
|
}
|
|
return false
|
|
}
|
|
|
|
|
|
}
|
|
|