Bot keyboard improvements

This commit is contained in:
Ilya Laktyushin
2022-11-10 16:58:30 +04:00
parent f382b0a2c4
commit 6e18ef51a1
11 changed files with 218 additions and 40 deletions

View File

@@ -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)