Fix context menus

This commit is contained in:
Isaac 2026-01-17 01:03:38 +08:00
parent 1b52877d0b
commit e0367409f2
3 changed files with 62 additions and 33 deletions

View file

@ -1249,8 +1249,8 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
let constrainedSize = CGSize(width: min(self.displayReadTimestamps ? 280.0 : 260.0, constrainedWidth), height: maxHeight)
var topContentHeight: CGFloat = 0.0
topContentHeight += 6.0
if let backButtonNode = self.backButtonNode {
topContentHeight += 6.0
let backButtonFrame = CGRect(origin: CGPoint(x: 0.0, y: topContentHeight), size: CGSize(width: constrainedSize.width, height: 44.0))
backButtonNode.update(size: backButtonFrame.size, presentationData: self.presentationData, isLast: self.tabListNode == nil)
transition.updateFrame(node: backButtonNode, frame: backButtonFrame)

View file

@ -329,7 +329,6 @@ private final class InnerActionsContainerNode: ASDisplayNode {
final class InnerTextSelectionTipContainerNode: ASDisplayNode {
private let presentationData: PresentationData
private var background: (container: GlassBackgroundContainerView, background: GlassBackgroundView)?
private let buttonNode: HighlightTrackingButtonNode
private let textNode: TextNodeWithEntities
private var textSelectionNode: TextSelectionNode?
private let iconNode: ASImageNode
@ -357,8 +356,6 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
self.background = nil
}
self.buttonNode = HighlightTrackingButtonNode()
self.textNode = TextNodeWithEntities()
self.textNode.textNode.displaysAsynchronously = false
self.textNode.textNode.isUserInteractionEnabled = false
@ -466,17 +463,7 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
parentView.addSubview(textSelectionNode.highlightAreaNode.view)
parentView.addSubview(self.buttonNode.view)
self.buttonNode.highligthedChanged = { [weak self] highlighted in
guard let strongSelf = self else {
return
}
strongSelf.isButtonHighlighted = highlighted
strongSelf.updateHighlight(animated: false)
}
self.buttonNode.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
parentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:))))
let shimmeringForegroundColor: UIColor
if presentationData.theme.overallDarkAppearance {
@ -491,10 +478,12 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
self.isUserInteractionEnabled = isUserInteractionEnabled
}
@objc func pressed() {
self.requestDismiss({
self.action?()
})
@objc func onTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.requestDismiss({
self.action?()
})
}
}
func animateTransitionInside(other: InnerTextSelectionTipContainerNode) {
@ -528,7 +517,8 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
}
func updateLayout(widthClass: ContainerViewLayoutSizeClass, presentation: ContextControllerActionsStackNode.Presentation, width: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
let verticalInset: CGFloat = 16.0
let topInset: CGFloat = self.background != nil ? 16.0 : 9.0
let bottomInset: CGFloat = self.background != nil ? 16.0 : 9.0
let horizontalInset: CGFloat = 18.0
let standardIconWidth: CGFloat = 32.0
let iconSideInset: CGFloat = 20.0
@ -567,10 +557,10 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
}
let makeTextLayout = TextNodeWithEntities.asyncLayout(self.textNode)
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, minimumNumberOfLines: 0, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - horizontalInset * 2.0 - textRightInset, height: .greatestFiniteMagnitude), alignment: .left, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets(), lineColor: nil, textShadowColor: nil, textStroke: nil))
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, minimumNumberOfLines: 0, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - horizontalInset * 2.0 - textRightInset, height: .greatestFiniteMagnitude), alignment: .left, lineSpacing: 0.12, cutout: nil, insets: UIEdgeInsets(), lineColor: nil, textShadowColor: nil, textStroke: nil))
let _ = textApply(self.arguments?.withUpdatedPlaceholderColor(shimmeringForegroundColor))
let textFrame = CGRect(origin: CGPoint(x: horizontalInset, y: verticalInset), size: textLayout.size)
let textFrame = CGRect(origin: CGPoint(x: horizontalInset, y: topInset), size: textLayout.size)
transition.updateFrame(node: self.textNode.textNode, frame: textFrame)
if textFrame.size.height.isZero {
self.textNode.textNode.alpha = 0.0
@ -586,7 +576,7 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
contentHeight = 32.0
}
let size = CGSize(width: width, height: contentHeight + verticalInset * 2.0)
let size = CGSize(width: width, height: contentHeight + topInset + bottomInset)
let lineHeight: CGFloat = 8.0
transition.updateFrame(node: self.placeholderNode, frame: CGRect(origin: CGPoint(x: horizontalInset, y: floorToScreenPixels((size.height - lineHeight) / 2.0)), size: CGSize(width: width - horizontalInset * 2.0, height: lineHeight)))
@ -613,8 +603,6 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
background.background.update(size: size, cornerRadius: min(30.0, size.height * 0.5), isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: transition)
transition.setFrame(view: background.background, frame: CGRect(origin: CGPoint(), size: size))
}
self.buttonNode.frame = CGRect(origin: CGPoint(), size: size)
}
func updateTheme(presentationData: PresentationData) {
@ -665,7 +653,9 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
if self.isHighlighted {
self.setHighlighted(false)
if performAction {
self.pressed()
self.requestDismiss({
self.action?()
})
}
}
}

View file

@ -627,7 +627,7 @@ private final class ContextControllerActionsListSeparatorItemNode: ASDisplayNode
}
func update(presentationData: PresentationData, constrainedSize: CGSize) -> (minSize: CGSize, apply: (_ size: CGSize, _ transition: ContainedViewLayoutTransition) -> Void) {
return (minSize: CGSize(width: 0.0, height: 10.0), apply: { size, transition in
return (minSize: CGSize(width: 0.0, height: 20.0), apply: { size, transition in
let sideInset: CGFloat = 18.0
self.separatorView.tintColor = presentationData.theme.contextMenu.itemSeparatorColor
transition.updateFrame(view: self.separatorView, frame: CGRect(origin: CGPoint(x: sideInset, y: floorToScreenPixels((size.height - 1.0) * 0.5)), size: CGSize(width: max(0.0, size.width - sideInset * 2.0), height: 1.0)))
@ -756,6 +756,7 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio
private var tip: ContextController.Tip?
private var tipDisposable: Disposable?
private var tipNode: InnerTextSelectionTipContainerNode?
private var tipSeparatorNode: ContextControllerActionsListSeparatorItemNode?
private var hapticFeedback: HapticFeedback?
@ -977,7 +978,7 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio
}
}
if let tip = self.tip, !"".isEmpty {
if let tip = self.tip {
let tipNode: InnerTextSelectionTipContainerNode
var tipTransition = transition
if let current = self.tipNode {
@ -987,15 +988,44 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio
tipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip, isInline: true)
self.addSubnode(tipNode)
self.tipNode = tipNode
let getController = self.getController
tipNode.requestDismiss = { completion in
getController()?.dismiss(completion: completion)
}
}
let tipSeparatorNode: ContextControllerActionsListSeparatorItemNode
if let current = self.tipSeparatorNode {
tipSeparatorNode = current
} else {
tipSeparatorNode = ContextControllerActionsListSeparatorItemNode()
self.addSubnode(tipSeparatorNode)
self.tipSeparatorNode = tipSeparatorNode
}
let (tipSeparatorMinSize, tipSeparatorApply) = tipSeparatorNode.update(presentationData: presentationData, constrainedSize: CGSize(width: combinedSize.width, height: 10.0))
let tipSeparatorSize = CGSize(width: combinedSize.width, height: tipSeparatorMinSize.height)
tipSeparatorApply(tipSeparatorSize, tipTransition)
let tipSeparatorFrame = CGRect(origin: nextItemOrigin, size: tipSeparatorSize)
tipTransition.updateFrame(node: tipSeparatorNode, frame: tipSeparatorFrame)
nextItemOrigin.y += tipSeparatorSize.height
combinedSize.height += tipSeparatorSize.height
let tipSize = tipNode.updateLayout(widthClass: .compact, presentation: .inline, width: combinedSize.width, transition: tipTransition)
let tipFrame = CGRect(origin: nextItemOrigin, size: tipSize)
tipNode.setActualSize(size: tipFrame.size, transition: tipTransition)
tipTransition.updateFrame(node: tipNode, frame: tipFrame)
nextItemOrigin.y += tipSize.height
combinedSize.height += tipSize.height
} else if let tipNode = self.tipNode {
tipNode.removeFromSupernode()
self.tipNode = nil
} else {
if let tipSeparatorNode = self.tipSeparatorNode {
tipSeparatorNode.removeFromSupernode()
self.tipSeparatorNode = nil
}
if let tipNode = self.tipNode {
tipNode.removeFromSupernode()
self.tipNode = nil
}
}
if let highlightedItemFrame {
@ -1036,6 +1066,11 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio
break
}
}
if let tipNode = self.tipNode {
if tipNode.frame.contains(location) {
return false
}
}
return true
}
@ -1625,6 +1660,10 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context
}
func updateTip(presentationData: PresentationData, presentation: ContextControllerActionsStackNode.Presentation, width: CGFloat, transition: ContainedViewLayoutTransition) -> (node: InnerTextSelectionTipContainerNode, height: CGFloat)? {
if self.item is ContextControllerActionsListStackItem {
return nil
}
if let tip = self.tip {
var updatedTransition = transition
if let tipNode = self.tipNode, tipNode.tip == tip {
@ -2096,9 +2135,9 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context
})
if let tipNode = itemContainer.tipNode {
let tipFrame = CGRect(origin: CGPoint(x: navigationContainerFrame.minX, y: navigationContainerFrame.maxY + tipSpacing), size: tipNode.frame.size)
transition.updateFrame(node: tipNode, frame: tipFrame, beginWithCurrentState: true)
ComponentTransition(transition).setFrame(view: tipNode.view, frame: tipFrame)
ComponentTransition(transition).setAlpha(view: tipNode.view, alpha: 0.0, completion: { [weak tipNode] _ in
ComponentTransition(transition).setAlpha(view: tipNode.view, alpha: 0.01, completion: { [weak tipNode] _ in
tipNode?.removeFromSupernode()
})
}