Skip to content

Commit

Permalink
[WIP] Stickers editor
Browse files Browse the repository at this point in the history
  • Loading branch information
laktyushin committed Apr 11, 2024
1 parent c7035b2 commit ccb2415
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 145 deletions.
15 changes: 15 additions & 0 deletions submodules/DrawingUI/Sources/DrawingEntitiesView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand All @@ -557,6 +565,7 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
if view.entity.isMedia {
continue
}
view.reset()
view.selectionView?.removeFromSuperview()
view.removeFromSuperview()
}
Expand Down Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
150 changes: 8 additions & 142 deletions submodules/DrawingUI/Sources/DrawingTextEntityView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)?
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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()] {
Expand Down Expand Up @@ -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
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1236,8 +1148,6 @@ final class SimpleTextLayer: CATextLayer {
}

final class DrawingTextView: UITextView, NSLayoutManagerDelegate {
var characterLayers: [CALayer] = []

var drawingLayoutManager: DrawingTextLayoutManager {
return self.layoutManager as! DrawingTextLayoutManager
}
Expand Down Expand Up @@ -1307,7 +1217,6 @@ final class DrawingTextView: UITextView, NSLayoutManagerDelegate {
}
}

var hasTextLayers = false
var visualText: NSAttributedString?

init(frame: CGRect) {
Expand Down Expand Up @@ -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
Expand All @@ -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() {
Expand Down
7 changes: 7 additions & 0 deletions submodules/MediaPickerUI/Sources/MediaPickerScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6498,6 +6498,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
return
}

if !isVideo {
self.stickerResultController?.disappeared = nil
}

let _ = (imagesReady.get()
|> filter { $0 }
|> take(1)
Expand All @@ -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: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1726,6 +1726,8 @@ extension ChatControllerImpl {
}

func openStickerEditor() {
self.chatDisplayNode.dismissInput()

var dismissImpl: (() -> Void)?
let mainController = self.context.sharedContext.makeStickerMediaPickerScreen(
context: self.context,
Expand Down

0 comments on commit ccb2415

Please sign in to comment.