From ccb24154732c6c4b2f7553b227c7fb11dc0d6552 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 11 Apr 2024 22:56:57 +0400 Subject: [PATCH] [WIP] Stickers editor --- .../Sources/DrawingEntitiesView.swift | 15 ++ .../Sources/DrawingLocationEntityView.swift | 1 - .../Sources/DrawingTextEntityView.swift | 150 +----------------- .../Sources/MediaPickerScreen.swift | 7 + .../Sources/StickerPreviewPeekContent.swift | 4 +- .../Sources/MediaEditorScreen.swift | 5 +- .../ChatControllerOpenAttachmentMenu.swift | 2 + 7 files changed, 39 insertions(+), 145 deletions(-) diff --git a/submodules/DrawingUI/Sources/DrawingEntitiesView.swift b/submodules/DrawingUI/Sources/DrawingEntitiesView.swift index f8c87ae3da2..876b2c01e98 100644 --- a/submodules/DrawingUI/Sources/DrawingEntitiesView.swift +++ b/submodules/DrawingUI/Sources/DrawingEntitiesView.swift @@ -156,6 +156,12 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { fatalError("init(coder:) has not been implemented") } + deinit { + self.eachView { entityView in + entityView.reset() + } + } + public override func layoutSubviews() { super.layoutSubviews() @@ -501,6 +507,7 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { self.hasSelectionChanged(false) view.selectionView?.removeFromSuperview() } + view.reset() if animated { view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak view] _ in view?.removeFromSuperview() @@ -539,6 +546,7 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { if view.entity.isMedia { continue } + view.reset() if let selectionView = view.selectionView { selectionView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak selectionView] _ in selectionView?.removeFromSuperview() @@ -557,6 +565,7 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { if view.entity.isMedia { continue } + view.reset() view.selectionView?.removeFromSuperview() view.removeFromSuperview() } @@ -946,6 +955,12 @@ public class DrawingEntityView: UIView { return self.bounds } + func reset() { + self.onSnapUpdated = { _, _ in } + self.onPositionUpdated = { _ in } + self.onInteractionUpdated = { _ in } + } + func animateInsertion() { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) diff --git a/submodules/DrawingUI/Sources/DrawingLocationEntityView.swift b/submodules/DrawingUI/Sources/DrawingLocationEntityView.swift index 832f8b53b27..3ed974a97c1 100644 --- a/submodules/DrawingUI/Sources/DrawingLocationEntityView.swift +++ b/submodules/DrawingUI/Sources/DrawingLocationEntityView.swift @@ -110,7 +110,6 @@ public final class DrawingLocationEntityView: DrawingEntityView, UITextViewDeleg private var textSize: CGSize = .zero public override func sizeThatFits(_ size: CGSize) -> CGSize { - self.textView.setNeedsLayersUpdate() var result = self.textView.sizeThatFits(CGSize(width: self.locationEntity.width, height: .greatestFiniteMagnitude)) self.textSize = result diff --git a/submodules/DrawingUI/Sources/DrawingTextEntityView.swift b/submodules/DrawingUI/Sources/DrawingTextEntityView.swift index c8ee6138ca6..1b89f1a6bcd 100644 --- a/submodules/DrawingUI/Sources/DrawingTextEntityView.swift +++ b/submodules/DrawingUI/Sources/DrawingTextEntityView.swift @@ -27,7 +27,7 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate return self.entity as! DrawingTextEntity } - let blurredBackgroundView: BlurredBackgroundView +// let blurredBackgroundView: BlurredBackgroundView let textView: DrawingTextView var customEmojiContainerView: CustomEmojiContainerView? var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)? @@ -37,9 +37,9 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate var replaceWithAnimatedImage: (Data, UIImage) -> Void = { _, _ in } init(context: AccountContext, entity: DrawingTextEntity) { - self.blurredBackgroundView = BlurredBackgroundView(color: UIColor(white: 0.0, alpha: 0.25), enableBlur: true) - self.blurredBackgroundView.clipsToBounds = true - self.blurredBackgroundView.isHidden = true +// self.blurredBackgroundView = BlurredBackgroundView(color: UIColor(white: 0.0, alpha: 0.25), enableBlur: true) +// self.blurredBackgroundView.clipsToBounds = true +// self.blurredBackgroundView.isHidden = true self.textView = DrawingTextView(frame: .zero) self.textView.clipsToBounds = false @@ -62,7 +62,7 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate super.init(context: context, entity: entity) self.textView.delegate = self - self.addSubview(self.blurredBackgroundView) +// self.addSubview(self.blurredBackgroundView) self.addSubview(self.textView) self.emojiViewProvider = { emoji in @@ -420,7 +420,6 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate } public override func sizeThatFits(_ size: CGSize) -> CGSize { - self.textView.setNeedsLayersUpdate() var result = self.textView.sizeThatFits(CGSize(width: self.textEntity.width, height: .greatestFiniteMagnitude)) result.width = max(224.0, ceil(result.width) + 20.0) result.height = ceil(result.height); @@ -458,7 +457,6 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate let range = NSMakeRange(0, text.length) let fontSize = self.displayFontSize - self.textView.hasTextLayers = [.typing, .wiggle].contains(self.textEntity.animation) self.textView.drawingLayoutManager.textContainers.first?.lineFragmentPadding = floor(fontSize * 0.24) if let (font, name) = availableFonts[text.string.lowercased()] { @@ -498,11 +496,8 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate guard let visualText = text.mutableCopy() as? NSMutableAttributedString else { return } - if self.textView.hasTextLayers { - text.addAttribute(.foregroundColor, value: UIColor.clear, range: range) - } else { - text.addAttribute(.foregroundColor, value: textColor, range: range) - } + text.addAttribute(.foregroundColor, value: textColor, range: range) + visualText.addAttribute(.foregroundColor, value: textColor, range: range) text.enumerateAttributes(in: range) { attributes, subrange, _ in @@ -522,67 +517,8 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate if keepSelectedRange { self.textView.selectedRange = previousRange } - - if self.textView.hasTextLayers { - self.textView.onLayersUpdate = { [weak self] in - self?.updateTextAnimations() - } - } else { - self.updateTextAnimations() - } } - func updateTextAnimations() { - for layer in self.textView.characterLayers { - layer.removeAllAnimations() - } - self.textView.layer.removeAllAnimations() - - guard self.textView.characterLayers.count > 0 || self.textEntity.animation == .zoomIn else { - return - } - - switch self.textEntity.animation { - case .typing: - let delta: CGFloat = 1.0 / CGFloat(self.textView.characterLayers.count + 3) - let duration = Double(self.textView.characterLayers.count + 3) * 0.28 - var offset = delta - for layer in self.textView.characterLayers { - let animation = CAKeyframeAnimation(keyPath: "opacity") - animation.calculationMode = .discrete - animation.values = [0.0, 1.0] - animation.keyTimes = [0.0, offset as NSNumber, 1.0] - animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) - animation.duration = duration - animation.repeatCount = .infinity - layer.add(animation, forKey: "opacity") - offset += delta - } - case .wiggle: - for layer in self.textView.characterLayers { - let animation = CABasicAnimation(keyPath: "transform.rotation.z") - animation.fromValue = (-.pi / 10.0) as NSNumber - animation.toValue = (.pi / 10.0) as NSNumber - animation.autoreverses = true - animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) - animation.duration = 0.6 - animation.repeatCount = .infinity - layer.add(animation, forKey: "transform.rotation.z") - } - case .zoomIn: - let animation = CABasicAnimation(keyPath: "transform.scale") - animation.fromValue = 0.001 as NSNumber - animation.toValue = 1.0 as NSNumber - animation.autoreverses = true - animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) - animation.duration = 0.8 - animation.repeatCount = .infinity - self.textView.layer.add(animation, forKey: "transform.scale") - default: - break - } - } - public override func update(animated: Bool = false) { self.update(animated: animated, keepSelectedRange: false, updateEditingPosition: true) } @@ -687,30 +623,6 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate return image } - func getPresentationRenderImage() -> UIImage? { - let rect = self.bounds - UIGraphicsBeginImageContextWithOptions(rect.size, false, 1.0) - if let context = UIGraphicsGetCurrentContext() { - for layer in self.textView.characterLayers { - if let presentation = layer.presentation() { - context.saveGState() - context.translateBy(x: presentation.position.x - presentation.bounds.width / 2.0, y: 0.0) - if let rotation = (presentation.value(forKeyPath: "transform.rotation.z") as? NSNumber)?.floatValue { - context.translateBy(x: presentation.bounds.width / 2.0, y: presentation.bounds.height) - context.rotate(by: CGFloat(rotation)) - context.translateBy(x: -presentation.bounds.width / 2.0, y: -presentation.bounds.height) - } - presentation.render(in: context) - context.restoreGState() - } - } - } - //self.textView.drawHierarchy(in: rect, afterScreenUpdates: true) - let image = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - return image - } - func getRenderSubEntities() -> [DrawingEntity] { let textSize = self.textView.bounds.size let textPosition = self.textEntity.position @@ -1236,8 +1148,6 @@ final class SimpleTextLayer: CATextLayer { } final class DrawingTextView: UITextView, NSLayoutManagerDelegate { - var characterLayers: [CALayer] = [] - var drawingLayoutManager: DrawingTextLayoutManager { return self.layoutManager as! DrawingTextLayoutManager } @@ -1307,7 +1217,6 @@ final class DrawingTextView: UITextView, NSLayoutManagerDelegate { } } - var hasTextLayers = false var visualText: NSAttributedString? init(frame: CGRect) { @@ -1357,15 +1266,8 @@ final class DrawingTextView: UITextView, NSLayoutManagerDelegate { } var onLayoutUpdate: (() -> Void)? - var onLayersUpdate: (() -> Void)? - - private var needsLayersUpdate = false - func setNeedsLayersUpdate() { - self.needsLayersUpdate = true - } - + func layoutManager(_ layoutManager: NSLayoutManager, didCompleteLayoutFor textContainer: NSTextContainer?, atEnd layoutFinishedFlag: Bool) { - self.updateCharLayers() if layoutFinishedFlag { if let onLayoutUpdate = self.onLayoutUpdate { self.onLayoutUpdate = nil @@ -1374,42 +1276,6 @@ final class DrawingTextView: UITextView, NSLayoutManagerDelegate { } } - func updateCharLayers() { - for layer in self.characterLayers { - layer.removeFromSuperlayer() - } - self.characterLayers = [] - - guard let attributedString = self.visualText, self.hasTextLayers else { - return - } - - let wordRange = NSMakeRange(0, attributedString.length) - - var index = wordRange.location - while index < wordRange.location + wordRange.length { - let glyphRange = NSMakeRange(index, 1) - let characterRange = self.layoutManager.characterRange(forGlyphRange: glyphRange, actualGlyphRange:nil) - var glyphRect = self.layoutManager.boundingRect(forGlyphRange: glyphRange, in: self.textContainer) - //let location = self.layoutManager.location(forGlyphAt: index) - - glyphRect.origin.y += glyphRect.height / 2.0 //location.y - (glyphRect.height / 2.0); - let textLayer = SimpleTextLayer() - textLayer.contentsScale = 1.0 - textLayer.frame = glyphRect - textLayer.string = attributedString.attributedSubstring(from: characterRange) - textLayer.anchorPoint = CGPoint(x: 0.5, y: 1.0) - - self.layer.addSublayer(textLayer) - self.characterLayers.append(textLayer) - - let stepGlyphRange = self.layoutManager.glyphRange(forCharacterRange: characterRange, actualCharacterRange:nil) - index += stepGlyphRange.length - } - - self.onLayersUpdate?() - } - var onPaste: () -> Bool = { return true } override func paste(_ sender: Any?) { if !self.text.isEmpty || self.onPaste() { diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 940e3538e68..9b0c5dc3d68 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -2225,6 +2225,13 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } } + if let camera = self.controllerNode.modernCamera { + if let cameraView = self.controllerNode.modernCameraView { + cameraView.isEnabled = false + } + camera.stopCapture(invalidate: true) + } + super.dismiss(completion: completion) } diff --git a/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift b/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift index 833153052a9..c7624af0e2e 100644 --- a/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift +++ b/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift @@ -578,7 +578,9 @@ final class EmojiStickerAccessoryNode: SparseNode, PeekControllerAccessoryNode { self.reactionContextNode.updateLayout(size: size, insets: UIEdgeInsets(top: 64.0, left: 0.0, bottom: 0.0, right: 0.0), anchorRect: anchorRect, centerAligned: true, isCoveredByInput: false, isAnimatingOut: false, forceUpdate: forceUpdate, transition: transition) if isFirstTime { - self.reactionContextNode.animateIn(from: anchorRect) + Queue.mainQueue().justDispatch { + self.reactionContextNode.animateIn(from: anchorRect) + } } } } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index e1b8723f469..7e90ee8700b 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -6498,6 +6498,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate return } + if !isVideo { + self.stickerResultController?.disappeared = nil + } + let _ = (imagesReady.get() |> filter { $0 } |> take(1) @@ -6508,7 +6512,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if isVideo { self.uploadSticker(file, action: .send) } else { - self.stickerResultController?.disappeared = nil self.completion(MediaEditorScreen.Result( media: .sticker(file: file, emoji: self.effectiveStickerEmoji()), mediaAreas: [], diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift index 14a0bb84b1d..6bf4da54af1 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift @@ -1726,6 +1726,8 @@ extension ChatControllerImpl { } func openStickerEditor() { + self.chatDisplayNode.dismissInput() + var dismissImpl: (() -> Void)? let mainController = self.context.sharedContext.makeStickerMediaPickerScreen( context: self.context,