mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
iPad trackpad support improvements
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user