iPad trackpad support improvements

This commit is contained in:
Ilya Laktyushin
2022-07-27 21:49:25 +03:00
parent 689a5e0427
commit f1ccd3cf33
47 changed files with 526 additions and 197 deletions

View File

@@ -193,7 +193,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
fileprivate var dismissedForCancel: (() -> Void)?
private let getController: () -> ContextControllerProtocol?
private weak var gesture: ContextGesture?
private var didSetItemsReady = false
let itemsReady = Promise<Bool>()
let contentReady = Promise<Bool>()
@@ -371,7 +371,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
if strongSelf.didMoveFromInitialGesturePoint {
if let presentationNode = strongSelf.presentationNode {
let presentationPoint = strongSelf.view.convert(localPoint, to: presentationNode.view)
presentationNode.highlightGestureMoved(location: presentationPoint)
presentationNode.highlightGestureMoved(location: presentationPoint, hover: false)
} else {
let actionPoint = strongSelf.view.convert(localPoint, to: strongSelf.actionsContainerNode.view)
let actionNode = strongSelf.actionsContainerNode.actionNode(at: actionPoint)
@@ -445,7 +445,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
if strongSelf.didMoveFromInitialGesturePoint {
if let presentationNode = strongSelf.presentationNode {
let presentationPoint = strongSelf.view.convert(localPoint, to: presentationNode.view)
presentationNode.highlightGestureMoved(location: presentationPoint)
presentationNode.highlightGestureMoved(location: presentationPoint, hover: false)
} else {
let actionPoint = strongSelf.view.convert(localPoint, to: strongSelf.actionsContainerNode.view)
var actionNode = strongSelf.actionsContainerNode.actionNode(at: actionPoint)
@@ -504,7 +504,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
switch source {
case .reference, .extracted:
case .location, .reference, .extracted:
self.contentReady.set(.single(true))
case let .controller(source):
self.contentReady.set(source.controller.ready.get())
@@ -538,6 +538,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
super.didLoad()
self.dismissNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimNodeTapped)))
if #available(iOS 13.0, *) {
self.view.addGestureRecognizer(UIHoverGestureRecognizer(target: self, action: #selector(self.hoverGesture(_:))))
}
}
@objc private func dimNodeTapped() {
@@ -548,8 +552,93 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self.beginDismiss(.default)
}
@available(iOS 13.0, *)
@objc private func hoverGesture(_ gestureRecognizer: UIHoverGestureRecognizer) {
guard self.didCompleteAnimationIn else {
return
}
let localPoint = gestureRecognizer.location(in: self.view)
switch gestureRecognizer.state {
case .changed:
if let presentationNode = self.presentationNode {
let presentationPoint = self.view.convert(localPoint, to: presentationNode.view)
presentationNode.highlightGestureMoved(location: presentationPoint, hover: true)
} else {
let actionPoint = self.view.convert(localPoint, to: self.actionsContainerNode.view)
let actionNode = self.actionsContainerNode.actionNode(at: actionPoint)
if self.highlightedActionNode !== actionNode {
self.highlightedActionNode?.setIsHighlighted(false)
self.highlightedActionNode = actionNode
if let actionNode = actionNode {
actionNode.setIsHighlighted(true)
}
}
if let reactionContextNode = self.reactionContextNode {
let reactionPoint = self.view.convert(localPoint, to: reactionContextNode.view)
let highlightedReaction = reactionContextNode.reaction(at: reactionPoint)?.reaction
if self.highlightedReaction?.rawValue != highlightedReaction?.rawValue {
self.highlightedReaction = highlightedReaction
self.hapticFeedback.tap()
}
}
}
case .ended, .cancelled:
if let presentationNode = self.presentationNode {
presentationNode.highlightGestureMoved(location: CGPoint(x: -1, y: -1), hover: true)
} else {
if let highlightedActionNode = self.highlightedActionNode {
self.highlightedActionNode = nil
highlightedActionNode.setIsHighlighted(false)
}
if let _ = self.reactionContextNode {
self.highlightedReaction = nil
}
}
default:
break
}
}
private func initializeContent() {
switch self.source {
case let .location(source):
let presentationNode = ContextControllerExtractedPresentationNode(
getController: { [weak self] in
return self?.getController()
},
requestUpdate: { [weak self] transition in
guard let strongSelf = self else {
return
}
if let validLayout = strongSelf.validLayout {
strongSelf.updateLayout(
layout: validLayout,
transition: transition,
previousActionsContainerNode: nil
)
}
},
requestDismiss: { [weak self] result in
guard let strongSelf = self else {
return
}
strongSelf.dismissedForCancel?()
strongSelf.beginDismiss(result)
},
requestAnimateOut: { [weak self] result, completion in
guard let strongSelf = self else {
return
}
strongSelf.animateOut(result: result, completion: completion)
},
source: .location(source)
)
self.presentationNode = presentationNode
self.addSubnode(presentationNode)
case let .reference(source):
if let controller = self.getController() as? ContextController, controller.workaroundUseLegacyImplementation {
let transitionInfo = source.transitionInfo()
@@ -701,7 +790,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
switch self.source {
case .reference:
case .location, .reference:
break
case .extracted:
if let contentAreaInScreenSpace = self.contentAreaInScreenSpace, let maybeContentNode = self.contentContainerNode.contentNode, case .extracted = maybeContentNode {
@@ -900,6 +989,53 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
var result = initialResult
switch self.source {
case let .location(source):
let transitionInfo = source.transitionInfo()
if transitionInfo == nil {
result = .dismissWithoutContent
}
switch result {
case let .custom(value):
switch value {
case let .animated(duration, curve):
transitionDuration = duration
transitionCurve = curve
default:
break
}
default:
break
}
self.isUserInteractionEnabled = false
self.isAnimatingOut = true
self.scrollNode.view.setContentOffset(self.scrollNode.view.contentOffset, animated: false)
if !self.dimNode.isHidden {
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
} else {
self.withoutBlurDimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
}
self.actionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15 * animationDurationFactor, removeOnCompletion: false, completion: { _ in
completion()
})
self.actionsContainerNode.layer.animateScale(from: 1.0, to: 0.1, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
let animateOutToItem: Bool
switch result {
case .default, .custom:
animateOutToItem = true
case .dismissWithoutContent:
animateOutToItem = false
}
if animateOutToItem, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
let localSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.scrollNode.view)
self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true)
}
case let .reference(source):
guard let maybeContentNode = self.contentContainerNode.contentNode, case let .reference(referenceView) = maybeContentNode else {
return
@@ -2071,6 +2207,30 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
}
public final class ContextControllerLocationViewInfo {
public let location: CGPoint
public let contentAreaInScreenSpace: CGRect
public let insets: UIEdgeInsets
public init(location: CGPoint, contentAreaInScreenSpace: CGRect, insets: UIEdgeInsets = UIEdgeInsets()) {
self.location = location
self.contentAreaInScreenSpace = contentAreaInScreenSpace
self.insets = insets
}
}
public protocol ContextLocationContentSource: AnyObject {
var shouldBeDismissed: Signal<Bool, NoError> { get }
func transitionInfo() -> ContextControllerLocationViewInfo?
}
public extension ContextLocationContentSource {
var shouldBeDismissed: Signal<Bool, NoError> {
return .single(false)
}
}
public final class ContextControllerReferenceViewInfo {
public let referenceView: UIView
public let contentAreaInScreenSpace: CGRect
@@ -2171,6 +2331,7 @@ public protocol ContextControllerContentSource: AnyObject {
}
public enum ContextContentSource {
case location(ContextLocationContentSource)
case reference(ContextReferenceContentSource)
case extracted(ContextExtractedContentSource)
case controller(ContextControllerContentSource)
@@ -2297,6 +2458,18 @@ public final class ContextController: ViewController, StandalonePresentableContr
super.init(navigationBarPresentationData: nil)
switch source {
case let .location(locationSource):
self.statusBar.statusBarStyle = .Ignore
self.shouldBeDismissedDisposable = (locationSource.shouldBeDismissed
|> filter { $0 }
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.dismiss(result: .default, completion: {})
})
case let .reference(referenceSource):
self.statusBar.statusBarStyle = .Ignore