mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Implement tooltips
This commit is contained in:
parent
a154aae943
commit
81810c94a9
@ -337,7 +337,7 @@ private final class InnerActionsContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||||
private let presentationData: PresentationData
|
private let presentationData: PresentationData
|
||||||
private var effectView: UIVisualEffectView?
|
private var effectView: UIVisualEffectView?
|
||||||
private let textNode: TextNode
|
private let textNode: TextNode
|
||||||
|
@ -29,6 +29,7 @@ public protocol ContextControllerActionsStackItem: AnyObject {
|
|||||||
requestUpdateApparentHeight: @escaping (ContainedViewLayoutTransition) -> Void
|
requestUpdateApparentHeight: @escaping (ContainedViewLayoutTransition) -> Void
|
||||||
) -> ContextControllerActionsStackItemNode
|
) -> ContextControllerActionsStackItemNode
|
||||||
|
|
||||||
|
var tip: ContextController.Tip? { get }
|
||||||
var reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])? { get }
|
var reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])? { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,13 +559,16 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack
|
|||||||
|
|
||||||
private let items: [ContextMenuItem]
|
private let items: [ContextMenuItem]
|
||||||
let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?
|
let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?
|
||||||
|
let tip: ContextController.Tip?
|
||||||
|
|
||||||
init(
|
init(
|
||||||
items: [ContextMenuItem],
|
items: [ContextMenuItem],
|
||||||
reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?
|
reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?,
|
||||||
|
tip: ContextController.Tip?
|
||||||
) {
|
) {
|
||||||
self.items = items
|
self.items = items
|
||||||
self.reactionItems = reactionItems
|
self.reactionItems = reactionItems
|
||||||
|
self.tip = tip
|
||||||
}
|
}
|
||||||
|
|
||||||
func node(
|
func node(
|
||||||
@ -632,13 +636,16 @@ final class ContextControllerActionsCustomStackItem: ContextControllerActionsSta
|
|||||||
|
|
||||||
private let content: ContextControllerItemsContent
|
private let content: ContextControllerItemsContent
|
||||||
let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?
|
let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?
|
||||||
|
let tip: ContextController.Tip?
|
||||||
|
|
||||||
init(
|
init(
|
||||||
content: ContextControllerItemsContent,
|
content: ContextControllerItemsContent,
|
||||||
reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?
|
reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?,
|
||||||
|
tip: ContextController.Tip?
|
||||||
) {
|
) {
|
||||||
self.content = content
|
self.content = content
|
||||||
self.reactionItems = reactionItems
|
self.reactionItems = reactionItems
|
||||||
|
self.tip = tip
|
||||||
}
|
}
|
||||||
|
|
||||||
func node(
|
func node(
|
||||||
@ -663,9 +670,9 @@ func makeContextControllerActionsStackItem(items: ContextController.Items) -> Co
|
|||||||
}
|
}
|
||||||
switch items.content {
|
switch items.content {
|
||||||
case let .list(listItems):
|
case let .list(listItems):
|
||||||
return ContextControllerActionsListStackItem(items: listItems, reactionItems: reactionItems)
|
return ContextControllerActionsListStackItem(items: listItems, reactionItems: reactionItems, tip: items.tip)
|
||||||
case let .custom(customContent):
|
case let .custom(customContent):
|
||||||
return ContextControllerActionsCustomStackItem(content: customContent, reactionItems: reactionItems)
|
return ContextControllerActionsCustomStackItem(content: customContent, reactionItems: reactionItems, tip: items.tip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,6 +735,8 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
|||||||
let requestUpdate: (ContainedViewLayoutTransition) -> Void
|
let requestUpdate: (ContainedViewLayoutTransition) -> Void
|
||||||
let node: ContextControllerActionsStackItemNode
|
let node: ContextControllerActionsStackItemNode
|
||||||
let dimNode: ASDisplayNode
|
let dimNode: ASDisplayNode
|
||||||
|
let tip: ContextController.Tip?
|
||||||
|
var tipNode: InnerTextSelectionTipContainerNode?
|
||||||
let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?
|
let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?
|
||||||
var storedScrollingState: CGFloat?
|
var storedScrollingState: CGFloat?
|
||||||
let positionLock: CGFloat?
|
let positionLock: CGFloat?
|
||||||
@ -738,6 +747,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
|||||||
requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void,
|
requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void,
|
||||||
requestUpdateApparentHeight: @escaping (ContainedViewLayoutTransition) -> Void,
|
requestUpdateApparentHeight: @escaping (ContainedViewLayoutTransition) -> Void,
|
||||||
item: ContextControllerActionsStackItem,
|
item: ContextControllerActionsStackItem,
|
||||||
|
tip: ContextController.Tip?,
|
||||||
reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?,
|
reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?,
|
||||||
positionLock: CGFloat?
|
positionLock: CGFloat?
|
||||||
) {
|
) {
|
||||||
@ -756,6 +766,8 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
|||||||
self.reactionItems = reactionItems
|
self.reactionItems = reactionItems
|
||||||
self.positionLock = positionLock
|
self.positionLock = positionLock
|
||||||
|
|
||||||
|
self.tip = tip
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.clipsToBounds = true
|
self.clipsToBounds = true
|
||||||
@ -790,6 +802,27 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
|||||||
return (size, apparentHeight)
|
return (size, apparentHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateTip(presentationData: PresentationData, width: CGFloat, transition: ContainedViewLayoutTransition) -> (node: ASDisplayNode, height: CGFloat)? {
|
||||||
|
if let tip = self.tip {
|
||||||
|
var updatedTransition = transition
|
||||||
|
if self.tipNode == nil {
|
||||||
|
updatedTransition = .immediate
|
||||||
|
let tipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip)
|
||||||
|
tipNode.isUserInteractionEnabled = false
|
||||||
|
self.tipNode = tipNode
|
||||||
|
}
|
||||||
|
|
||||||
|
if let tipNode = self.tipNode {
|
||||||
|
let size = tipNode.updateLayout(widthClass: .compact, width: width, transition: updatedTransition)
|
||||||
|
return (tipNode, size.height)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@ -906,6 +939,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
|||||||
strongSelf.requestUpdate(transition)
|
strongSelf.requestUpdate(transition)
|
||||||
},
|
},
|
||||||
item: item,
|
item: item,
|
||||||
|
tip: item.tip,
|
||||||
reactionItems: item.reactionItems,
|
reactionItems: item.reactionItems,
|
||||||
positionLock: positionLock
|
positionLock: positionLock
|
||||||
)
|
)
|
||||||
@ -946,6 +980,8 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
|||||||
constrainedSize: CGSize,
|
constrainedSize: CGSize,
|
||||||
transition: ContainedViewLayoutTransition
|
transition: ContainedViewLayoutTransition
|
||||||
) -> CGSize {
|
) -> CGSize {
|
||||||
|
let tipSpacing: CGFloat = 10.0
|
||||||
|
|
||||||
self.navigationContainer.backgroundColor = presentationData.theme.contextMenu.backgroundColor
|
self.navigationContainer.backgroundColor = presentationData.theme.contextMenu.backgroundColor
|
||||||
|
|
||||||
let animateAppearingContainers = transition.isAnimated && !self.dismissingItemContainers.isEmpty
|
let animateAppearingContainers = transition.isAnimated && !self.dismissingItemContainers.isEmpty
|
||||||
@ -1047,6 +1083,23 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.itemContainers[i].updateDimNode(presentationData: presentationData, size: CGSize(width: itemLayouts[i].size.width, height: navigationContainerFrame.size.height), transitionFraction: itemLayouts[i].alphaTransitionFraction, transition: transition)
|
self.itemContainers[i].updateDimNode(presentationData: presentationData, size: CGSize(width: itemLayouts[i].size.width, height: navigationContainerFrame.size.height), transitionFraction: itemLayouts[i].alphaTransitionFraction, transition: transition)
|
||||||
|
|
||||||
|
if let (tipNode, tipHeight) = self.itemContainers[i].updateTip(presentationData: presentationData, width: itemLayouts[i].size.width, transition: transition) {
|
||||||
|
var tipTransition = transition
|
||||||
|
if tipNode.supernode == nil {
|
||||||
|
tipTransition = .immediate
|
||||||
|
self.addSubnode(tipNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
let tipAlpha: CGFloat = itemLayouts[i].alphaTransitionFraction
|
||||||
|
|
||||||
|
tipTransition.updateFrame(node: tipNode, frame: CGRect(origin: CGPoint(x: navigationContainerFrame.minX, y: navigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tipHeight)), beginWithCurrentState: true)
|
||||||
|
tipTransition.updateAlpha(node: tipNode, alpha: tipAlpha, beginWithCurrentState: true)
|
||||||
|
|
||||||
|
if i == self.itemContainers.count - 1 {
|
||||||
|
topItemSize.height += tipSpacing + tipHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (itemContainer, isPopped) in self.dismissingItemContainers {
|
for (itemContainer, isPopped) in self.dismissingItemContainers {
|
||||||
@ -1059,6 +1112,11 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
|||||||
transition.updatePosition(node: itemContainer, position: position, completion: { [weak itemContainer] _ in
|
transition.updatePosition(node: itemContainer, position: position, completion: { [weak itemContainer] _ in
|
||||||
itemContainer?.removeFromSupernode()
|
itemContainer?.removeFromSupernode()
|
||||||
})
|
})
|
||||||
|
if let tipNode = itemContainer.tipNode {
|
||||||
|
transition.updateAlpha(node: tipNode, alpha: 0.0, completion: { [weak tipNode] _ in
|
||||||
|
tipNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.dismissingItemContainers.removeAll()
|
self.dismissingItemContainers.removeAll()
|
||||||
|
|
||||||
@ -1082,4 +1140,12 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
|||||||
selectionPanGesture.isEnabled = isEnabled
|
selectionPanGesture.isEnabled = isEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func animateIn() {
|
||||||
|
for itemContainer in self.itemContainers {
|
||||||
|
if let tipNode = itemContainer.tipNode {
|
||||||
|
tipNode.animateIn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -503,6 +503,8 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
reactionContextNode.animateIn(from: currentContentScreenFrame)
|
reactionContextNode.animateIn(from: currentContentScreenFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.actionsStackNode.animateIn()
|
||||||
|
|
||||||
contentNode.containingNode.isExtractedToContextPreview = true
|
contentNode.containingNode.isExtractedToContextPreview = true
|
||||||
contentNode.containingNode.isExtractedToContextPreviewUpdated?(true)
|
contentNode.containingNode.isExtractedToContextPreviewUpdated?(true)
|
||||||
contentNode.containingNode.willUpdateIsExtractedToContextPreview?(true, transition)
|
contentNode.containingNode.willUpdateIsExtractedToContextPreview?(true, transition)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user