mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Bot keyboard improvements
This commit is contained in:
@@ -8,21 +8,103 @@ import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import ChatPresentationInterfaceState
|
||||
import WallpaperBackgroundNode
|
||||
|
||||
private final class ChatButtonKeyboardInputButtonNode: ASButtonNode {
|
||||
private final class ChatButtonKeyboardInputButtonNode: HighlightTrackingButtonNode {
|
||||
var button: ReplyMarkupButton? {
|
||||
didSet {
|
||||
self.updateIcon()
|
||||
}
|
||||
}
|
||||
var iconNode: ASImageNode?
|
||||
|
||||
private let backgroundContainerNode: ASDisplayNode
|
||||
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||
private let backgroundColorNode: ASDisplayNode
|
||||
private let backgroundAdditionalColorNode: ASDisplayNode
|
||||
|
||||
private let shadowNode: ASImageNode
|
||||
private let highlightNode: ASImageNode
|
||||
|
||||
private let textNode: ImmediateTextNode
|
||||
private var iconNode: ASImageNode?
|
||||
|
||||
private var theme: PresentationTheme?
|
||||
|
||||
init(theme: PresentationTheme) {
|
||||
init() {
|
||||
self.backgroundContainerNode = ASDisplayNode()
|
||||
self.backgroundContainerNode.clipsToBounds = true
|
||||
self.backgroundContainerNode.allowsGroupOpacity = true
|
||||
self.backgroundContainerNode.isUserInteractionEnabled = false
|
||||
self.backgroundContainerNode.cornerRadius = 5.0
|
||||
if #available(iOS 13.0, *) {
|
||||
self.backgroundContainerNode.layer.cornerCurve = .continuous
|
||||
}
|
||||
|
||||
self.backgroundColorNode = ASDisplayNode()
|
||||
self.backgroundColorNode.cornerRadius = 5.0
|
||||
if #available(iOS 13.0, *) {
|
||||
self.backgroundColorNode.layer.cornerCurve = .continuous
|
||||
}
|
||||
|
||||
self.backgroundAdditionalColorNode = ASDisplayNode()
|
||||
self.backgroundAdditionalColorNode.backgroundColor = UIColor(rgb: 0xffffff, alpha: 0.1)
|
||||
self.backgroundAdditionalColorNode.isHidden = true
|
||||
|
||||
self.shadowNode = ASImageNode()
|
||||
self.shadowNode.isUserInteractionEnabled = false
|
||||
|
||||
self.highlightNode = ASImageNode()
|
||||
self.highlightNode.isUserInteractionEnabled = false
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.textAlignment = .center
|
||||
self.textNode.maximumNumberOfLines = 2
|
||||
|
||||
super.init()
|
||||
|
||||
self.updateTheme(theme: theme)
|
||||
self.addSubnode(self.backgroundContainerNode)
|
||||
|
||||
self.backgroundContainerNode.addSubnode(self.backgroundColorNode)
|
||||
self.backgroundContainerNode.addSubnode(self.backgroundAdditionalColorNode)
|
||||
self.addSubnode(self.textNode)
|
||||
|
||||
self.backgroundContainerNode.addSubnode(self.shadowNode)
|
||||
self.backgroundContainerNode.addSubnode(self.highlightNode)
|
||||
|
||||
self.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted, !strongSelf.bounds.width.isZero {
|
||||
let scale = (strongSelf.bounds.width - 10.0) / strongSelf.bounds.width
|
||||
|
||||
strongSelf.layer.animateScale(from: 1.0, to: scale, duration: 0.15, removeOnCompletion: false)
|
||||
|
||||
strongSelf.backgroundContainerNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.backgroundContainerNode.alpha = 0.6
|
||||
} else if let presentationLayer = strongSelf.layer.presentation() {
|
||||
strongSelf.layer.animateScale(from: CGFloat((presentationLayer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0), to: 1.0, duration: 0.25, removeOnCompletion: false)
|
||||
|
||||
strongSelf.backgroundContainerNode.alpha = 1.0
|
||||
strongSelf.backgroundContainerNode.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func setAttributedTitle(_ title: NSAttributedString, for state: UIControl.State) {
|
||||
self.textNode.attributedText = title
|
||||
}
|
||||
|
||||
private var absoluteRect: (CGRect, CGSize)?
|
||||
func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.absoluteRect = (rect, containerSize)
|
||||
|
||||
if let backgroundNode = self.backgroundNode {
|
||||
var backgroundFrame = backgroundNode.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundNode.update(rect: backgroundFrame, within: containerSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateIcon() {
|
||||
@@ -55,32 +137,71 @@ private final class ChatButtonKeyboardInputButtonNode: ASButtonNode {
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
|
||||
func updateTheme(theme: PresentationTheme) {
|
||||
func updateTheme(theme: PresentationTheme, wallpaperBackgroundNode: WallpaperBackgroundNode?) {
|
||||
if theme !== self.theme {
|
||||
self.theme = theme
|
||||
|
||||
self.setBackgroundImage(PresentationResourcesChat.chatInputButtonPanelButtonImage(theme), for: [])
|
||||
self.setBackgroundImage(PresentationResourcesChat.chatInputButtonPanelButtonHighlightedImage(theme), for: [.highlighted])
|
||||
|
||||
self.highlightNode.image = PresentationResourcesChat.chatInputButtonPanelButtonHighlightImage(theme)
|
||||
self.shadowNode.image = PresentationResourcesChat.chatInputButtonPanelButtonShadowImage(theme)
|
||||
|
||||
self.updateIcon()
|
||||
}
|
||||
|
||||
self.backgroundColorNode.backgroundColor = theme.chat.inputButtonPanel.buttonFillColor
|
||||
if let alpha = self.backgroundColorNode.backgroundColor?.alpha, alpha < 1.0 {
|
||||
self.backgroundColorNode.layer.compositingFilter = "softLightBlendMode"
|
||||
self.backgroundAdditionalColorNode.isHidden = false
|
||||
} else {
|
||||
self.backgroundColorNode.layer.compositingFilter = nil
|
||||
self.backgroundAdditionalColorNode.isHidden = true
|
||||
}
|
||||
|
||||
if wallpaperBackgroundNode?.hasExtraBubbleBackground() == true {
|
||||
if self.backgroundNode == nil, let backgroundContent = wallpaperBackgroundNode?.makeBubbleBackground(for: .free) {
|
||||
self.backgroundNode = backgroundContent
|
||||
self.backgroundContainerNode.insertSubnode(backgroundContent, at: 0)
|
||||
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
} else {
|
||||
self.backgroundNode?.removeFromSupernode()
|
||||
self.backgroundNode = nil
|
||||
}
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
|
||||
self.backgroundContainerNode.frame = self.bounds
|
||||
self.backgroundColorNode.frame = CGRect(origin: .zero, size: CGSize(width: self.bounds.width, height: self.bounds.height - 1.0))
|
||||
self.backgroundAdditionalColorNode.frame = self.backgroundColorNode.frame
|
||||
self.backgroundNode?.frame = self.backgroundColorNode.frame
|
||||
|
||||
self.highlightNode.frame = self.bounds
|
||||
self.shadowNode.frame = self.bounds
|
||||
|
||||
if let (rect, containerSize) = self.absoluteRect {
|
||||
self.update(rect: rect, within: containerSize, transition: .immediate)
|
||||
}
|
||||
|
||||
if let iconNode = self.iconNode {
|
||||
iconNode.frame = CGRect(x: self.frame.width - 16.0, y: 4.0, width: 12.0, height: 12.0)
|
||||
}
|
||||
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: self.bounds.width - 16.0, height: self.bounds.height))
|
||||
self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.bounds.width - textSize.width) / 2.0), y: floorToScreenPixels((self.bounds.height - textSize.height) / 2.0)), size: textSize)
|
||||
}
|
||||
}
|
||||
|
||||
final class ChatButtonKeyboardInputNode: ChatInputNode {
|
||||
private let context: AccountContext
|
||||
private let controllerInteraction: ChatControllerInteraction
|
||||
|
||||
|
||||
private let separatorNode: ASDisplayNode
|
||||
private let scrollNode: ASScrollNode
|
||||
|
||||
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||
private let backgroundColorNode: ASDisplayNode
|
||||
|
||||
private var buttonNodes: [ChatButtonKeyboardInputButtonNode] = []
|
||||
private var message: Message?
|
||||
@@ -93,11 +214,15 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
|
||||
self.backgroundColorNode = ASDisplayNode()
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundColorNode)
|
||||
|
||||
self.addSubnode(self.scrollNode)
|
||||
self.scrollNode.view.delaysContentTouches = false
|
||||
self.scrollNode.view.canCancelContentTouches = true
|
||||
@@ -115,15 +240,41 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
|
||||
}
|
||||
}
|
||||
|
||||
private var absoluteRect: (CGRect, CGSize)?
|
||||
override func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.absoluteRect = (rect, containerSize)
|
||||
|
||||
if let backgroundNode = self.backgroundNode {
|
||||
var backgroundFrame = backgroundNode.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundNode.update(rect: backgroundFrame, within: containerSize, transition: transition)
|
||||
}
|
||||
|
||||
for buttonNode in self.buttonNodes {
|
||||
var buttonFrame = buttonNode.frame
|
||||
buttonFrame.origin.x += rect.minX
|
||||
buttonFrame.origin.y += rect.minY
|
||||
buttonNode.update(rect: buttonFrame, within: containerSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool) -> (CGFloat, CGFloat) {
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: UIScreenPixel)))
|
||||
|
||||
if self.backgroundNode == nil {
|
||||
if let backgroundNode = self.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
||||
self.backgroundNode = backgroundNode
|
||||
self.insertSubnode(backgroundNode, at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
let updatedTheme = self.theme !== interfaceState.theme
|
||||
if updatedTheme {
|
||||
self.theme = interfaceState.theme
|
||||
|
||||
self.separatorNode.backgroundColor = interfaceState.theme.chat.inputButtonPanel.panelSeparatorColor
|
||||
self.backgroundColor = interfaceState.theme.chat.inputButtonPanel.panelBackgroundColor
|
||||
self.backgroundColorNode.backgroundColor = interfaceState.theme.chat.inputButtonPanel.panelBackgroundColor
|
||||
}
|
||||
|
||||
var validatedMarkup: ReplyMarkupMessageAttribute?
|
||||
@@ -165,14 +316,14 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
|
||||
let buttonNode: ChatButtonKeyboardInputButtonNode
|
||||
if buttonIndex < self.buttonNodes.count {
|
||||
buttonNode = self.buttonNodes[buttonIndex]
|
||||
buttonNode.updateTheme(theme: interfaceState.theme)
|
||||
} else {
|
||||
buttonNode = ChatButtonKeyboardInputButtonNode(theme: interfaceState.theme)
|
||||
buttonNode = ChatButtonKeyboardInputButtonNode()
|
||||
buttonNode.titleNode.maximumNumberOfLines = 2
|
||||
buttonNode.addTarget(self, action: #selector(self.buttonPressed(_:)), forControlEvents: [.touchUpInside])
|
||||
self.scrollNode.addSubnode(buttonNode)
|
||||
self.buttonNodes.append(buttonNode)
|
||||
}
|
||||
buttonNode.updateTheme(theme: interfaceState.theme, wallpaperBackgroundNode: self.controllerInteraction.presentationContext.backgroundNode)
|
||||
buttonIndex += 1
|
||||
if buttonNode.button != button || updatedTheme {
|
||||
buttonNode.button = button
|
||||
@@ -200,6 +351,15 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
|
||||
self.scrollNode.view.setContentOffset(CGPoint(), animated: false)
|
||||
}
|
||||
|
||||
if let backgroundNode = self.backgroundNode {
|
||||
backgroundNode.frame = CGRect(origin: .zero, size: CGSize(width: width, height: panelHeight))
|
||||
}
|
||||
self.backgroundColorNode.frame = CGRect(origin: .zero, size: CGSize(width: width, height: panelHeight))
|
||||
|
||||
if let (rect, containerSize) = self.absoluteRect {
|
||||
self.updateAbsoluteRect(rect, within: containerSize, transition: transition)
|
||||
}
|
||||
|
||||
return (panelHeight, 0.0)
|
||||
} else {
|
||||
return (0.0, 0.0)
|
||||
|
||||
Reference in New Issue
Block a user