From 8235fc6755c316d0d54369765df85fea48d0ea1c Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 21 Dec 2021 12:44:45 +0400 Subject: [PATCH] Context menu improvements --- .../ContextControllerActionsStackNode.swift | 58 +++++++++++++------ ...tControllerExtractedPresentationNode.swift | 25 +++++--- .../ContainedViewLayoutTransition.swift | 17 ++++-- 3 files changed, 69 insertions(+), 31 deletions(-) diff --git a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift index 48ba044a90..c3ae419655 100644 --- a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift @@ -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 + } + } } diff --git a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift index 00f2486bd3..748e2630e8 100644 --- a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift @@ -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 diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index 2a4b4e5d7c..74eec36301 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -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 {