import Foundation import UIKit import Display import TelegramCore import SwiftSignalKit import AsyncDisplayKit import Postbox import AccountContext import GridMessageSelectionNode import SettingsThemeWallpaperNode import TelegramPresentationData private var cachedBorderImages: [String: UIImage] = [:] private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selected: Bool) -> UIImage? { let key = "\(theme.list.itemBlocksBackgroundColor.hexString)_\(selected ? "s" + theme.list.itemAccentColor.hexString : theme.list.disclosureArrowColor.hexString)" if let image = cachedBorderImages[key] { return image } else { let image = generateImage(CGSize(width: 20.0, height: 20.0), rotatedContext: { size, context in let bounds = CGRect(origin: CGPoint(), size: size) context.clear(bounds) let lineWidth: CGFloat if selected { lineWidth = 2.0 context.setLineWidth(lineWidth) context.setStrokeColor(theme.list.itemBlocksBackgroundColor.cgColor) context.strokeEllipse(in: bounds.insetBy(dx: 2.0 + lineWidth / 2.0, dy: 2.0 + lineWidth / 2.0)) var accentColor = theme.list.itemAccentColor if accentColor.rgb == 0xffffff { accentColor = UIColor(rgb: 0x999999) } context.setStrokeColor(accentColor.cgColor) } else { context.setStrokeColor(theme.list.disclosureArrowColor.withAlphaComponent(0.4).cgColor) lineWidth = 1.0 } if bordered || selected { context.setLineWidth(lineWidth) context.strokeEllipse(in: bounds.insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0)) } })?.stretchableImage(withLeftCapWidth: 10, topCapHeight: 10) cachedBorderImages[key] = image return image } } final class ThemeGridControllerItem: GridItem { let context: AccountContext let theme: PresentationTheme? let wallpaper: TelegramWallpaper let wallpaperId: ThemeGridControllerEntry.StableId let isEmpty: Bool let emojiFile: TelegramMediaFile? let channelMode: Bool let index: Int let editable: Bool let selected: Bool let interaction: ThemeGridControllerInteraction let section: GridSection? = nil init(context: AccountContext, theme: PresentationTheme? = nil, wallpaper: TelegramWallpaper, wallpaperId: ThemeGridControllerEntry.StableId, isEmpty: Bool = false, emojiFile: TelegramMediaFile? = nil, channelMode: Bool = false, index: Int, editable: Bool, selected: Bool, interaction: ThemeGridControllerInteraction) { self.context = context self.theme = theme self.wallpaper = wallpaper self.wallpaperId = wallpaperId self.isEmpty = isEmpty self.emojiFile = emojiFile self.channelMode = channelMode self.index = index self.editable = editable self.selected = selected self.interaction = interaction } func node(layout: GridNodeLayout, synchronousLoad: Bool) -> GridItemNode { let node = ThemeGridControllerItemNode() node.setup(item: self, synchronousLoad: synchronousLoad) return node } func update(node: GridItemNode) { guard let node = node as? ThemeGridControllerItemNode else { assertionFailure() return } node.setup(item: self, synchronousLoad: false) } } final class ThemeGridControllerItemNode: GridItemNode { private let wallpaperNode: SettingsThemeWallpaperNode private var selectionNode: GridMessageSelectionNode? private var selectionBorderNode: ASImageNode? private var textNode: ImmediateTextNode? private var item: ThemeGridControllerItem? override init() { self.wallpaperNode = SettingsThemeWallpaperNode(displayLoading: false) super.init() self.clipsToBounds = true self.addSubnode(self.wallpaperNode) } override func didLoad() { super.didLoad() self.view.layer.cornerRadius = 10.0 self.view.isExclusiveTouch = true self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) } func setup(item: ThemeGridControllerItem, synchronousLoad: Bool) { self.item = item self.updateSelectionState(animated: false) if item.channelMode, item.selected, let theme = item.theme { let selectionBorderNode: ASImageNode if let current = self.selectionBorderNode { selectionBorderNode = current } else { selectionBorderNode = ASImageNode() selectionBorderNode.displaysAsynchronously = false self.selectionBorderNode = selectionBorderNode self.addSubnode(selectionBorderNode) } selectionBorderNode.image = generateBorderImage(theme: theme, bordered: true, selected: true) } else { self.selectionBorderNode?.removeFromSupernode() } if item.channelMode, item.isEmpty, let theme = item.theme { let textNode: ImmediateTextNode if let current = self.textNode { textNode = current } else { textNode = ImmediateTextNode() textNode.maximumNumberOfLines = 2 textNode.textAlignment = .center self.textNode = textNode self.addSubnode(textNode) } let strings = item.context.sharedContext.currentPresentationData.with { $0 }.strings textNode.attributedText = NSAttributedString(string: strings.Wallpaper_NoWallpaper, font: Font.regular(15.0), textColor: theme.list.itemSecondaryTextColor) } self.setNeedsLayout() } @objc func tapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { if let item = self.item, !item.isEmpty { item.interaction.openWallpaper(item.wallpaper) } } } func updateSelectionState(animated: Bool) { if let item = self.item { let (editing, selectedIds) = item.interaction.selectionState if editing && item.editable { let selected = selectedIds.contains(item.wallpaperId) if let selectionNode = self.selectionNode { selectionNode.updateSelected(selected, animated: animated) selectionNode.frame = CGRect(origin: CGPoint(), size: self.bounds.size) } else { let theme = item.context.sharedContext.currentPresentationData.with { $0 }.theme let selectionNode = GridMessageSelectionNode(theme: theme, toggle: { [weak self] value in if let strongSelf = self { strongSelf.item?.interaction.toggleWallpaperSelection(item.wallpaperId, value) } }) selectionNode.frame = CGRect(origin: CGPoint(), size: self.bounds.size) self.addSubnode(selectionNode) self.selectionNode = selectionNode selectionNode.updateSelected(selected, animated: false) if animated { selectionNode.animateIn() } } } else { if let selectionNode = self.selectionNode { self.selectionNode = nil if animated { selectionNode.animateOut { [weak selectionNode] in selectionNode?.removeFromSupernode() } } else { selectionNode.removeFromSupernode() } } } } } override func layout() { super.layout() let bounds = self.bounds if let item = self.item { self.wallpaperNode.setWallpaper(context: item.context, theme: item.theme, wallpaper: item.wallpaper, isEmpty: item.isEmpty, emojiFile: item.emojiFile, selected: !item.channelMode && item.selected, size: bounds.size, synchronousLoad: false) self.selectionNode?.frame = CGRect(origin: CGPoint(), size: bounds.size) } self.selectionBorderNode?.frame = CGRect(origin: CGPoint(), size: bounds.size) if let textNode = self.textNode { let textSize = textNode.updateLayout(bounds.size) textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((bounds.width - textSize.width) / 2.0), y: floorToScreenPixels((bounds.height - textSize.height) / 2.0) - 18.0), size: textSize) } } }