Context menu improvements

This commit is contained in:
Ali
2021-12-21 12:44:45 +04:00
parent ee7873b8f2
commit 8235fc6755
3 changed files with 69 additions and 31 deletions

View File

@@ -93,9 +93,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
if highlighted { if highlighted {
strongSelf.highlightBackgroundNode.alpha = 1.0 strongSelf.highlightBackgroundNode.alpha = 1.0
} else { } else {
let previousAlpha = strongSelf.highlightBackgroundNode.alpha
strongSelf.highlightBackgroundNode.alpha = 0.0 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 titleFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalInset), size: titleSize)
let subtitleFrame = CGRect(origin: CGPoint(x: sideInset, y: titleFrame.maxY + titleSubtitleSpacing), size: subtitleSize) 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.titleLabelNode, frame: titleFrame)
transition.updateFrameAdditive(node: self.subtitleNode, frame: subtitleFrame) transition.updateFrameAdditive(node: self.subtitleNode, frame: subtitleFrame)
if let iconSize = iconSize { if let iconSize = iconSize {
let iconWidth = max(standardIconWidth, iconSize.width) 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) 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) let itemLayoutAndApply = itemNode.updateLayout(constrainedWidth: constrainedSize.width, constrainedHeight: constrainedSize.height)
return (minSize: itemLayoutAndApply.0, apply: { size, transition in 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) itemLayoutAndApply.1(size, transition)
}) })
} }
@@ -493,10 +491,10 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack
let itemSize = CGSize(width: combinedSize.width, height: itemNodeLayout.minSize.height) let itemSize = CGSize(width: combinedSize.width, height: itemNodeLayout.minSize.height)
let itemFrame = CGRect(origin: nextItemOrigin, size: itemSize) 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 { 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 { if i != self.itemNodes.count - 1 {
switch self.items[i + 1] { switch self.items[i + 1] {
case .separator: case .separator:
@@ -543,10 +541,9 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack
func highlightGestureFinished(performAction: Bool) { func highlightGestureFinished(performAction: Bool) {
if let highlightedItemNode = self.highlightedItemNode { if let highlightedItemNode = self.highlightedItemNode {
self.highlightedItemNode = nil self.highlightedItemNode = nil
highlightedItemNode.node.updateIsHighlighted(isHighlighted: false)
if performAction { if performAction {
highlightedItemNode.node.performAction() highlightedItemNode.node.performAction()
} else {
highlightedItemNode.node.updateIsHighlighted(isHighlighted: false)
} }
} }
} }
@@ -614,7 +611,7 @@ final class ContextControllerActionsCustomStackItem: ContextControllerActionsSta
bottomInset: 0.0, bottomInset: 0.0,
transition: transition 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) return (contentLayout.cleanSize, contentLayout.apparentHeight)
} }
@@ -779,9 +776,9 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
let scale: CGFloat = (size.width - scaleOffset) / size.width let scale: CGFloat = (size.width - scaleOffset) / size.width
let yOffset: CGFloat = size.height * (1.0 - scale) let yOffset: CGFloat = size.height * (1.0 - scale)
let transitionOffset = (1.0 - transitionFraction) * size.width / 2.0 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.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)) transition.updateBounds(node: self.node, bounds: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true)
transition.updateTransformScale(node: self.node, scale: scale) transition.updateTransformScale(node: self.node, scale: scale, beginWithCurrentState: true)
return (size, apparentHeight) return (size, apparentHeight)
} }
@@ -789,8 +786,8 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
func updateDimNode(presentationData: PresentationData, size: CGSize, transitionFraction: CGFloat, transition: ContainedViewLayoutTransition) { func updateDimNode(presentationData: PresentationData, size: CGSize, transitionFraction: CGFloat, transition: ContainedViewLayoutTransition) {
self.dimNode.backgroundColor = presentationData.theme.contextMenu.sectionSeparatorColor self.dimNode.backgroundColor = presentationData.theme.contextMenu.sectionSeparatorColor
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true)
transition.updateAlpha(node: self.dimNode, alpha: 1.0 - transitionFraction) transition.updateAlpha(node: self.dimNode, alpha: 1.0 - transitionFraction, beginWithCurrentState: true)
} }
func highlightGestureMoved(location: CGPoint) { func highlightGestureMoved(location: CGPoint) {
@@ -810,6 +807,8 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
private var itemContainers: [ItemContainer] = [] private var itemContainers: [ItemContainer] = []
private var dismissingItemContainers: [(container: ItemContainer, isPopped: Bool)] = [] private var dismissingItemContainers: [(container: ItemContainer, isPopped: Bool)] = []
private var selectionPanGesture: UIPanGestureRecognizer?
var topReactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])? { var topReactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])? {
return self.itemContainers.last?.reactionItems return self.itemContainers.last?.reactionItems
} }
@@ -850,6 +849,25 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
} }
strongSelf.pop() 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) { 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))) 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) self.navigationContainer.update(presentationData: presentationData, size: navigationContainerFrame.size, transition: transition)
for i in 0 ..< self.itemContainers.count { 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)) 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 { if itemLayouts[i].animateAppearingContainer {
transition.animatePositionAdditive(node: self.itemContainers[i], offset: CGPoint(x: itemFrame.width, y: 0.0)) 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) topItemContainer.highlightGestureFinished(performAction: performAction)
} }
} }
func updatePanSelection(isEnabled: Bool) {
if let selectionPanGesture = self.selectionPanGesture {
selectionPanGesture.isEnabled = isEnabled
}
}
} }

View File

@@ -23,12 +23,17 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
super.init() super.init()
self.addSubnode(self.offsetContainerNode) self.addSubnode(self.offsetContainerNode)
self.offsetContainerNode.addSubnode(self.containingNode.contentNode)
} }
func update(presentationData: PresentationData, size: CGSize, transition: ContainedViewLayoutTransition) { 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? { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if !self.bounds.contains(point) { if !self.bounds.contains(point) {
return nil return nil
@@ -217,12 +222,12 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
transition: .immediate 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) 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) { 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 { if let current = self.contentNode {
@@ -328,7 +333,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
if reactionContextNode.frame.isEmpty { if reactionContextNode.frame.isEmpty {
reactionContextNodeTransition = .immediate 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) 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 { 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) var actionsFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: contentRect.maxY + contentActionsSpacing), size: actionsSize)
if self.source.keepInPlace { if self.source.keepInPlace {
@@ -365,9 +370,9 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
actionsFrame.origin.x = actionsEdgeInset 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 let contentHeight: CGFloat
if self.actionsStackNode.topPositionLock != nil { 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 defaultScrollY = contentSize.height - layout.size.height
if defaultScrollY < 0.0 { if defaultScrollY < 0.0 {
defaultScrollY = 0.0 defaultScrollY = 0.0
@@ -401,6 +408,8 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
switch stateTransition { switch stateTransition {
case .animateIn: case .animateIn:
contentNode.takeContainingNode()
let duration: Double = 0.42 let duration: Double = 0.42
let springDamping: CGFloat = 104.0 let springDamping: CGFloat = 104.0

View File

@@ -105,7 +105,7 @@ public extension CGRect {
} }
public extension ContainedViewLayoutTransition { 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 { if frame.origin.x.isNaN {
return 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 { if node.frame.equalTo(frame) && !force {
completion?(true) completion?(true)
} else { } 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 { if layer.frame.equalTo(frame) && !force {
completion?(true) completion?(true)
} else { } 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 { if node.bounds.equalTo(bounds) && !force {
completion?(true) completion?(true)
} else { } else {
@@ -272,7 +272,12 @@ public extension ContainedViewLayoutTransition {
completion(true) completion(true)
} }
case let .animated(duration, curve): 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.bounds = bounds
node.layer.animateBounds(from: previousBounds, to: bounds, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, force: force, completion: { result in node.layer.animateBounds(from: previousBounds, to: bounds, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, force: force, completion: { result in
if let completion = completion { 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) { if node.position.equalTo(position) {
completion?(true) completion?(true)
} else { } else {