mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Context menu improvements
This commit is contained in:
@@ -93,9 +93,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
|
||||
if highlighted {
|
||||
strongSelf.highlightBackgroundNode.alpha = 1.0
|
||||
} else {
|
||||
let previousAlpha = strongSelf.highlightBackgroundNode.alpha
|
||||
strongSelf.highlightBackgroundNode.alpha = 0.0
|
||||
strongSelf.highlightBackgroundNode.layer.animateAlpha(from: previousAlpha, to: 0.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,14 +244,14 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
|
||||
let titleFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalInset), size: titleSize)
|
||||
let subtitleFrame = CGRect(origin: CGPoint(x: sideInset, y: titleFrame.maxY + titleSubtitleSpacing), size: subtitleSize)
|
||||
|
||||
transition.updateFrame(node: self.highlightBackgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateFrame(node: self.highlightBackgroundNode, frame: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true)
|
||||
transition.updateFrameAdditive(node: self.titleLabelNode, frame: titleFrame)
|
||||
transition.updateFrameAdditive(node: self.subtitleNode, frame: subtitleFrame)
|
||||
|
||||
if let iconSize = iconSize {
|
||||
let iconWidth = max(standardIconWidth, iconSize.width)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: size.width - iconSideInset - iconWidth + floor((iconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
|
||||
transition.updateFrame(node: self.iconNode, frame: iconFrame)
|
||||
transition.updateFrame(node: self.iconNode, frame: iconFrame, beginWithCurrentState: true)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -344,7 +342,7 @@ private final class ContextControllerActionsListCustomItemNode: ASDisplayNode, C
|
||||
let itemLayoutAndApply = itemNode.updateLayout(constrainedWidth: constrainedSize.width, constrainedHeight: constrainedSize.height)
|
||||
|
||||
return (minSize: itemLayoutAndApply.0, apply: { size, transition in
|
||||
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true)
|
||||
itemLayoutAndApply.1(size, transition)
|
||||
})
|
||||
}
|
||||
@@ -493,10 +491,10 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack
|
||||
|
||||
let itemSize = CGSize(width: combinedSize.width, height: itemNodeLayout.minSize.height)
|
||||
let itemFrame = CGRect(origin: nextItemOrigin, size: itemSize)
|
||||
itemTransition.updateFrame(node: item.node, frame: itemFrame)
|
||||
itemTransition.updateFrame(node: item.node, frame: itemFrame, beginWithCurrentState: true)
|
||||
|
||||
if let separatorNode = item.separatorNode {
|
||||
itemTransition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: itemFrame.minX, y: itemFrame.maxY), size: CGSize(width: itemFrame.width, height: UIScreenPixel)))
|
||||
itemTransition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: itemFrame.minX, y: itemFrame.maxY), size: CGSize(width: itemFrame.width, height: UIScreenPixel)), beginWithCurrentState: true)
|
||||
if i != self.itemNodes.count - 1 {
|
||||
switch self.items[i + 1] {
|
||||
case .separator:
|
||||
@@ -543,10 +541,9 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack
|
||||
func highlightGestureFinished(performAction: Bool) {
|
||||
if let highlightedItemNode = self.highlightedItemNode {
|
||||
self.highlightedItemNode = nil
|
||||
highlightedItemNode.node.updateIsHighlighted(isHighlighted: false)
|
||||
if performAction {
|
||||
highlightedItemNode.node.performAction()
|
||||
} else {
|
||||
highlightedItemNode.node.updateIsHighlighted(isHighlighted: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -614,7 +611,7 @@ final class ContextControllerActionsCustomStackItem: ContextControllerActionsSta
|
||||
bottomInset: 0.0,
|
||||
transition: transition
|
||||
)
|
||||
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(), size: contentLayout.cleanSize))
|
||||
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(), size: contentLayout.cleanSize), beginWithCurrentState: true)
|
||||
|
||||
return (contentLayout.cleanSize, contentLayout.apparentHeight)
|
||||
}
|
||||
@@ -779,9 +776,9 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
let scale: CGFloat = (size.width - scaleOffset) / size.width
|
||||
let yOffset: CGFloat = size.height * (1.0 - scale)
|
||||
let transitionOffset = (1.0 - transitionFraction) * size.width / 2.0
|
||||
transition.updatePosition(node: self.node, position: CGPoint(x: size.width / 2.0 + scaleOffset / 2.0 + transitionOffset, y: size.height / 2.0 - yOffset / 2.0))
|
||||
transition.updateBounds(node: self.node, bounds: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateTransformScale(node: self.node, scale: scale)
|
||||
transition.updatePosition(node: self.node, position: CGPoint(x: size.width / 2.0 + scaleOffset / 2.0 + transitionOffset, y: size.height / 2.0 - yOffset / 2.0), beginWithCurrentState: true)
|
||||
transition.updateBounds(node: self.node, bounds: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true)
|
||||
transition.updateTransformScale(node: self.node, scale: scale, beginWithCurrentState: true)
|
||||
|
||||
return (size, apparentHeight)
|
||||
}
|
||||
@@ -789,8 +786,8 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
func updateDimNode(presentationData: PresentationData, size: CGSize, transitionFraction: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.dimNode.backgroundColor = presentationData.theme.contextMenu.sectionSeparatorColor
|
||||
|
||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateAlpha(node: self.dimNode, alpha: 1.0 - transitionFraction)
|
||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true)
|
||||
transition.updateAlpha(node: self.dimNode, alpha: 1.0 - transitionFraction, beginWithCurrentState: true)
|
||||
}
|
||||
|
||||
func highlightGestureMoved(location: CGPoint) {
|
||||
@@ -810,6 +807,8 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
private var itemContainers: [ItemContainer] = []
|
||||
private var dismissingItemContainers: [(container: ItemContainer, isPopped: Bool)] = []
|
||||
|
||||
private var selectionPanGesture: UIPanGestureRecognizer?
|
||||
|
||||
var topReactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])? {
|
||||
return self.itemContainers.last?.reactionItems
|
||||
}
|
||||
@@ -850,6 +849,25 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
}
|
||||
strongSelf.pop()
|
||||
}
|
||||
|
||||
let selectionPanGesture = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
||||
self.selectionPanGesture = selectionPanGesture
|
||||
self.view.addGestureRecognizer(selectionPanGesture)
|
||||
selectionPanGesture.isEnabled = false
|
||||
}
|
||||
|
||||
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .changed:
|
||||
let location = recognizer.location(in: self.view)
|
||||
self.highlightGestureMoved(location: location)
|
||||
case .ended:
|
||||
self.highlightGestureFinished(performAction: true)
|
||||
case .cancelled:
|
||||
self.highlightGestureFinished(performAction: false)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func replace(item: ContextControllerActionsStackItem, animated: Bool) {
|
||||
@@ -1000,7 +1018,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
let navigationContainerFrame = CGRect(origin: CGPoint(), size: CGSize(width: topItemWidth, height: max(14 * 2.0, topItemApparentHeight)))
|
||||
transition.updateFrame(node: self.navigationContainer, frame: navigationContainerFrame)
|
||||
transition.updateFrame(node: self.navigationContainer, frame: navigationContainerFrame, beginWithCurrentState: true)
|
||||
self.navigationContainer.update(presentationData: presentationData, size: navigationContainerFrame.size, transition: transition)
|
||||
|
||||
for i in 0 ..< self.itemContainers.count {
|
||||
@@ -1016,7 +1034,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
}
|
||||
let itemFrame = CGRect(origin: CGPoint(x: xOffset, y: 0.0), size: CGSize(width: itemLayouts[i].size.width, height: navigationContainerFrame.height))
|
||||
|
||||
itemLayouts[i].itemTransition.updateFrame(node: self.itemContainers[i], frame: itemFrame)
|
||||
itemLayouts[i].itemTransition.updateFrame(node: self.itemContainers[i], frame: itemFrame, beginWithCurrentState: true)
|
||||
if itemLayouts[i].animateAppearingContainer {
|
||||
transition.animatePositionAdditive(node: self.itemContainers[i], offset: CGPoint(x: itemFrame.width, y: 0.0))
|
||||
}
|
||||
@@ -1051,4 +1069,10 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
topItemContainer.highlightGestureFinished(performAction: performAction)
|
||||
}
|
||||
}
|
||||
|
||||
func updatePanSelection(isEnabled: Bool) {
|
||||
if let selectionPanGesture = self.selectionPanGesture {
|
||||
selectionPanGesture.isEnabled = isEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,17 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.offsetContainerNode)
|
||||
self.offsetContainerNode.addSubnode(self.containingNode.contentNode)
|
||||
}
|
||||
|
||||
func update(presentationData: PresentationData, size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
}
|
||||
|
||||
func takeContainingNode() {
|
||||
if self.containingNode.contentNode.supernode !== self.offsetContainerNode {
|
||||
self.offsetContainerNode.addSubnode(self.containingNode.contentNode)
|
||||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if !self.bounds.contains(point) {
|
||||
return nil
|
||||
@@ -217,12 +222,12 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true)
|
||||
self.backgroundNode.update(size: layout.size, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true)
|
||||
if self.scrollNode.frame != CGRect(origin: CGPoint(), size: layout.size) {
|
||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true)
|
||||
}
|
||||
|
||||
if let current = self.contentNode {
|
||||
@@ -328,7 +333,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
if reactionContextNode.frame.isEmpty {
|
||||
reactionContextNodeTransition = .immediate
|
||||
}
|
||||
reactionContextNodeTransition.updateFrame(node: reactionContextNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
reactionContextNodeTransition.updateFrame(node: reactionContextNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true)
|
||||
reactionContextNode.updateLayout(size: layout.size, insets: UIEdgeInsets(top: topInset, left: 0.0, bottom: 0.0, right: 0.0), anchorRect: contentRect, transition: reactionContextNodeTransition)
|
||||
}
|
||||
if let removedReactionContextNode = removedReactionContextNode {
|
||||
@@ -338,7 +343,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
})
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.contentRectDebugNode, frame: contentRect)
|
||||
transition.updateFrame(node: self.contentRectDebugNode, frame: contentRect, beginWithCurrentState: true)
|
||||
|
||||
var actionsFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: contentRect.maxY + contentActionsSpacing), size: actionsSize)
|
||||
if self.source.keepInPlace {
|
||||
@@ -365,9 +370,9 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
actionsFrame.origin.x = actionsEdgeInset
|
||||
}
|
||||
}
|
||||
transition.updateFrame(node: self.actionsStackNode, frame: actionsFrame)
|
||||
transition.updateFrame(node: self.actionsStackNode, frame: actionsFrame, beginWithCurrentState: true)
|
||||
|
||||
contentTransition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(x: contentParentGlobalFrame.minX + contentRect.minX - contentNode.containingNode.contentRect.minX, y: contentRect.minY - contentNode.containingNode.contentRect.minY), size: contentNode.containingNode.bounds.size))
|
||||
contentTransition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(x: contentParentGlobalFrame.minX + contentRect.minX - contentNode.containingNode.contentRect.minX, y: contentRect.minY - contentNode.containingNode.contentRect.minY), size: contentNode.containingNode.bounds.size), beginWithCurrentState: true)
|
||||
|
||||
let contentHeight: CGFloat
|
||||
if self.actionsStackNode.topPositionLock != nil {
|
||||
@@ -391,6 +396,8 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
}
|
||||
}
|
||||
|
||||
self.actionsStackNode.updatePanSelection(isEnabled: contentSize.height <= layout.size.height)
|
||||
|
||||
defaultScrollY = contentSize.height - layout.size.height
|
||||
if defaultScrollY < 0.0 {
|
||||
defaultScrollY = 0.0
|
||||
@@ -401,6 +408,8 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
|
||||
switch stateTransition {
|
||||
case .animateIn:
|
||||
contentNode.takeContainingNode()
|
||||
|
||||
let duration: Double = 0.42
|
||||
let springDamping: CGFloat = 104.0
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ public extension CGRect {
|
||||
}
|
||||
|
||||
public extension ContainedViewLayoutTransition {
|
||||
func updateFrame(node: ASDisplayNode, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = true, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
|
||||
func updateFrame(node: ASDisplayNode, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = false, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
|
||||
if frame.origin.x.isNaN {
|
||||
return
|
||||
}
|
||||
@@ -157,7 +157,7 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
}
|
||||
|
||||
func updateFrameAsPositionAndBounds(node: ASDisplayNode, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
||||
func updateFrameAsPositionAndBounds(node: ASDisplayNode, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if node.frame.equalTo(frame) && !force {
|
||||
completion?(true)
|
||||
} else {
|
||||
@@ -190,7 +190,7 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
}
|
||||
|
||||
func updateFrameAsPositionAndBounds(layer: CALayer, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
||||
func updateFrameAsPositionAndBounds(layer: CALayer, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if layer.frame.equalTo(frame) && !force {
|
||||
completion?(true)
|
||||
} else {
|
||||
@@ -261,7 +261,7 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
}
|
||||
|
||||
func updateBounds(node: ASDisplayNode, bounds: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
func updateBounds(node: ASDisplayNode, bounds: CGRect, force: Bool = false, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if node.bounds.equalTo(bounds) && !force {
|
||||
completion?(true)
|
||||
} else {
|
||||
@@ -272,7 +272,12 @@ public extension ContainedViewLayoutTransition {
|
||||
completion(true)
|
||||
}
|
||||
case let .animated(duration, curve):
|
||||
let previousBounds = node.bounds
|
||||
let previousBounds: CGRect
|
||||
if beginWithCurrentState, let presentation = node.layer.presentation() {
|
||||
previousBounds = presentation.bounds
|
||||
} else {
|
||||
previousBounds = node.bounds
|
||||
}
|
||||
node.bounds = bounds
|
||||
node.layer.animateBounds(from: previousBounds, to: bounds, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, force: force, completion: { result in
|
||||
if let completion = completion {
|
||||
@@ -305,7 +310,7 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
}
|
||||
|
||||
func updatePosition(node: ASDisplayNode, position: CGPoint, beginWithCurrentState: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
||||
func updatePosition(node: ASDisplayNode, position: CGPoint, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if node.position.equalTo(position) {
|
||||
completion?(true)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user