Files
ghostgram/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsSearchNavigationContentNode.swift
T
ichmagmaus111andClaude Opus 5.5 0622057a4f Ghostgram: open-source release
Ghostgram for iOS, rebuilt from scratch on Telegram iOS 13.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 17:57:09 +01:00

182 lines
8.4 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import Display
import TelegramCore
import TelegramPresentationData
import SearchBarNode
import ComponentFlow
import GlassBackgroundComponent
private let searchBarFont = Font.regular(17.0)
final class ChatRecentActionsSearchNavigationContentNode: NavigationBarContentNode {
private var theme: PresentationTheme
private var preferClearGlass: Bool
private var strings: PresentationStrings
private let cancel: () -> Void
private let backgroundContainer: GlassBackgroundContainerView
private let backgroundView: GlassBackgroundView
private let iconView: UIImageView
private let searchBar: SearchBarNode
private let close: (background: GlassBackgroundView, icon: UIImageView)
private var queryUpdated: ((String) -> Void)?
private var params: (size: CGSize, leftInset: CGFloat, rightInset: CGFloat)?
init(theme: PresentationTheme, preferClearGlass: Bool, strings: PresentationStrings, cancel: @escaping () -> Void) {
self.theme = theme
self.preferClearGlass = preferClearGlass
self.strings = strings
self.cancel = cancel
self.backgroundContainer = GlassBackgroundContainerView()
self.backgroundView = GlassBackgroundView()
self.backgroundContainer.contentView.addSubview(self.backgroundView)
self.iconView = UIImageView()
self.backgroundView.contentView.addSubview(self.iconView)
self.close = (GlassBackgroundView(), UIImageView())
self.close.background.contentView.addSubview(self.close.icon)
self.searchBar = SearchBarNode(theme: ChatRecentActionsSearchNavigationContentNode.searchBarTheme(theme), presentationTheme: theme, preferClearGlass: preferClearGlass, strings: strings, fieldStyle: .inlineNavigation, forceSeparator: false, displayBackground: false, cancelText: nil)
let placeholderText = strings.Common_Search
self.searchBar.placeholderString = NSAttributedString(string: placeholderText, font: searchBarFont, textColor: theme.chat.inputPanel.inputPlaceholderColor)
super.init()
self.view.addSubview(self.backgroundContainer)
self.backgroundView.contentView.addSubview(self.searchBar.view)
self.backgroundContainer.contentView.addSubview(self.close.background)
self.close.background.contentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onCloseTapGesture(_:))))
self.searchBar.cancel = { [weak self] in
self?.searchBar.deactivate(clear: false)
self?.cancel()
}
self.searchBar.textUpdated = { [weak self] query, _ in
self?.queryUpdated?(query)
}
}
@objc private func onCloseTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.searchBar.cancel?()
}
}
func setQueryUpdated(_ f: @escaping (String) -> Void) {
self.queryUpdated = f
}
private static func searchBarTheme(_ theme: PresentationTheme) -> SearchBarNodeTheme {
return SearchBarNodeTheme(
background: .clear,
separator: .clear,
inputFill: .clear,
primaryText: theme.chat.inputPanel.panelControlColor,
placeholder: theme.chat.inputPanel.inputPlaceholderColor,
inputIcon: theme.chat.inputPanel.inputControlColor,
inputClear: theme.chat.inputPanel.panelControlColor,
accent: theme.chat.inputPanel.panelControlAccentColor,
keyboard: theme.rootController.keyboardColor
)
}
func updateThemeAndStrings(theme: PresentationTheme, preferClearGlass: Bool, strings: PresentationStrings) {
if self.theme !== theme || self.preferClearGlass != preferClearGlass || self.strings !== strings {
self.theme = theme
self.preferClearGlass = preferClearGlass
self.strings = strings
self.searchBar.updateThemeAndStrings(
theme: ChatRecentActionsSearchNavigationContentNode.searchBarTheme(theme),
presentationTheme: theme,
preferClearGlass: preferClearGlass,
strings: strings
)
self.searchBar.placeholderString = NSAttributedString(string: strings.Common_Search, font: searchBarFont, textColor: theme.chat.inputPanel.inputPlaceholderColor)
}
if let params = self.params {
let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
}
}
override var nominalHeight: CGFloat {
return 60.0
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
self.params = (size, leftInset, rightInset)
let transition = ComponentTransition(transition)
let backgroundFrame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: 6.0), size: CGSize(width: size.width - 16.0 * 2.0 - leftInset - rightInset - 44.0 - 8.0, height: 44.0))
let closeFrame = CGRect(origin: CGPoint(x: size.width - 16.0 - rightInset - 44.0, y: backgroundFrame.minY), size: CGSize(width: 44.0, height: 44.0))
transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: size))
self.backgroundContainer.update(size: size, isDark: self.theme.overallDarkAppearance, transition: transition)
transition.setFrame(view: self.backgroundView, frame: backgroundFrame)
self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: self.preferClearGlass ? .clear : .panel), isInteractive: true, transition: transition)
if self.iconView.image == nil {
self.iconView.image = UIImage(bundleImageName: "Navigation/Search")?.withRenderingMode(.alwaysTemplate)
}
transition.setTintColor(view: self.iconView, color: self.theme.rootController.navigationSearchBar.inputIconColor)
if let image = self.iconView.image {
let iconFraction: CGFloat = 0.8
let imageSize = CGSize(width: image.size.width * iconFraction, height: image.size.height * iconFraction)
let iconFrame = CGRect(origin: CGPoint(x: 12.0, y: floor((backgroundFrame.height - imageSize.height) * 0.5)), size: imageSize)
transition.setPosition(view: self.iconView, position: iconFrame.center)
transition.setBounds(view: self.iconView, bounds: CGRect(origin: CGPoint(), size: iconFrame.size))
}
let searchBarFrame = CGRect(origin: CGPoint(x: 36.0, y: 0.0), size: CGSize(width: backgroundFrame.width - 36.0 - 4.0, height: 44.0))
transition.setFrame(view: self.searchBar.view, frame: searchBarFrame)
self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: 0.0, rightInset: 0.0, transition: transition.containedViewLayoutTransition)
if self.close.icon.image == nil {
self.close.icon.image = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setLineWidth(2.0)
context.setLineCap(.round)
context.setStrokeColor(UIColor.white.cgColor)
context.beginPath()
context.move(to: CGPoint(x: 12.0, y: 12.0))
context.addLine(to: CGPoint(x: size.width - 12.0, y: size.height - 12.0))
context.move(to: CGPoint(x: size.width - 12.0, y: 12.0))
context.addLine(to: CGPoint(x: 12.0, y: size.height - 12.0))
context.strokePath()
})?.withRenderingMode(.alwaysTemplate)
}
if let image = self.close.icon.image {
self.close.icon.frame = image.size.centered(in: CGRect(origin: CGPoint(), size: closeFrame.size))
}
self.close.icon.tintColor = self.theme.chat.inputPanel.panelControlColor
transition.setFrame(view: self.close.background, frame: closeFrame)
self.close.background.update(size: closeFrame.size, cornerRadius: closeFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: self.preferClearGlass ? .clear : .panel), isInteractive: true, transition: transition)
return size
}
func activate() {
self.searchBar.activate()
}
func deactivate() {
self.searchBar.deactivate(clear: false)
}
}