import Foundation import UIKit import AsyncDisplayKit import Display import Postbox import TelegramCore import TelegramPresentationData import ItemListUI import PresentationDataUtils import SearchBarNode import GlassBackgroundComponent import ComponentFlow import ComponentDisplayAdapters import AppBundle import ActivityIndicator private let searchBarFont = Font.regular(17.0) final class GroupStickerSearchNavigationContentNode: NavigationBarContentNode, ItemListControllerSearchNavigationContentNode { private struct Params: Equatable { let size: CGSize let leftInset: CGFloat let rightInset: CGFloat init(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) { self.size = size self.leftInset = leftInset self.rightInset = rightInset } } private var theme: PresentationTheme private let strings: PresentationStrings private let cancel: () -> Void private let backgroundContainer: GlassBackgroundContainerView private let backgroundView: GlassBackgroundView private let iconView: UIImageView private var activityIndicator: ActivityIndicator? private let searchBar: SearchBarNode private let close: (background: GlassBackgroundView, icon: UIImageView) private var params: Params? private var queryUpdated: ((String) -> Void)? var activity: Bool = false { didSet { if self.activity != oldValue { if let params = self.params { self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate) } } } } init(theme: PresentationTheme, strings: PresentationStrings, cancel: @escaping () -> Void, updateActivity: @escaping(@escaping(Bool)->Void) -> Void) { self.theme = theme 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: 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.inputControlColor, accent: theme.chat.inputPanel.panelControlAccentColor, keyboard: theme.rootController.keyboardColor ), strings: strings, fieldStyle: .inlineNavigation, forceSeparator: false, displayBackground: false, cancelText: nil ) super.init() self.view.addSubview(self.backgroundContainer) self.backgroundView.contentView.addSubview(self.searchBar.view) self.backgroundContainer.contentView.addSubview(self.close.background) self.close.background.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) } updateActivity({ [weak self] value in guard let self else { return } self.activity = value }) self.updatePlaceholder() } @objc private func onCloseTapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { self.searchBar.cancel?() } } func setQueryUpdated(_ f: @escaping (String) -> Void) { self.queryUpdated = f } func updateTheme(_ theme: PresentationTheme) { self.theme = theme self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: self.theme), strings: self.strings) self.updatePlaceholder() } func updatePlaceholder() { self.searchBar.placeholderString = NSAttributedString(string: self.strings.Common_Search, font: searchBarFont, textColor: self.theme.rootController.navigationSearchBar.inputPlaceholderTextColor) } override var nominalHeight: CGFloat { return 60.0 } override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { self.params = Params(size: size, leftInset: leftInset, rightInset: 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: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), 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 imageSize: CGSize let iconFrame: CGRect let iconFraction: CGFloat = 0.8 imageSize = CGSize(width: image.size.width * iconFraction, height: image.size.height * iconFraction) 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)) } if self.activity { let activityIndicator: ActivityIndicator if let current = self.activityIndicator { activityIndicator = current } else { activityIndicator = ActivityIndicator(type: .custom(self.theme.chat.inputPanel.inputControlColor, 14.0, 14.0, false)) self.activityIndicator = activityIndicator self.backgroundView.contentView.addSubview(activityIndicator.view) } let indicatorSize = activityIndicator.measure(CGSize(width: 32.0, height: 32.0)) let indicatorFrame = CGRect(origin: CGPoint(x: 15.0, y: floorToScreenPixels((backgroundFrame.height - indicatorSize.height) * 0.5)), size: indicatorSize) transition.setPosition(view: activityIndicator.view, position: indicatorFrame.center) transition.setBounds(view: activityIndicator.view, bounds: CGRect(origin: CGPoint(), size: indicatorFrame.size)) } else if let activityIndicator = self.activityIndicator { self.activityIndicator = nil activityIndicator.view.removeFromSuperview() } self.iconView.isHidden = self.activity 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 = 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: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) } func activate() { self.searchBar.activate() } func deactivate() { self.searchBar.deactivate(clear: false) } }