mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
306 lines
11 KiB
Swift
306 lines
11 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import AccountContext
|
|
import TextFormat
|
|
|
|
public final class DrawingTextEntity: DrawingEntity, Codable {
|
|
final class CustomEmojiAttribute: Codable {
|
|
private enum CodingKeys: String, CodingKey {
|
|
case attribute
|
|
case rangeOrigin
|
|
case rangeLength
|
|
}
|
|
let attribute: ChatTextInputTextCustomEmojiAttribute
|
|
let range: NSRange
|
|
|
|
init(attribute: ChatTextInputTextCustomEmojiAttribute, range: NSRange) {
|
|
self.attribute = attribute
|
|
self.range = range
|
|
}
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
self.attribute = try container.decode(ChatTextInputTextCustomEmojiAttribute.self, forKey: .attribute)
|
|
|
|
let rangeOrigin = try container.decode(Int.self, forKey: .rangeOrigin)
|
|
let rangeLength = try container.decode(Int.self, forKey: .rangeLength)
|
|
self.range = NSMakeRange(rangeOrigin, rangeLength)
|
|
}
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(self.attribute, forKey: .attribute)
|
|
try container.encode(self.range.location, forKey: .rangeOrigin)
|
|
try container.encode(self.range.length, forKey: .rangeLength)
|
|
}
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case uuid
|
|
case text
|
|
case textAttributes
|
|
case style
|
|
case animation
|
|
case font
|
|
case alignment
|
|
case fontSize
|
|
case color
|
|
case referenceDrawingSize
|
|
case position
|
|
case width
|
|
case scale
|
|
case rotation
|
|
case renderImage
|
|
case renderSubEntities
|
|
case renderAnimationFrames
|
|
}
|
|
|
|
public enum Style: Codable, Equatable {
|
|
case regular
|
|
case filled
|
|
case semi
|
|
case stroke
|
|
}
|
|
|
|
public enum Animation: Codable, Equatable {
|
|
case none
|
|
case typing
|
|
case wiggle
|
|
case zoomIn
|
|
}
|
|
|
|
public enum Font: Codable, Equatable {
|
|
case sanFrancisco
|
|
case other(String, String)
|
|
}
|
|
|
|
public enum Alignment: Codable, Equatable {
|
|
case left
|
|
case center
|
|
case right
|
|
}
|
|
|
|
public var uuid: UUID
|
|
public var isAnimated: Bool {
|
|
if self.animation != .none {
|
|
return true
|
|
}
|
|
var isAnimated = false
|
|
self.text.enumerateAttributes(in: NSMakeRange(0, self.text.length), options: [], using: { attributes, range, _ in
|
|
if let _ = attributes[ChatTextInputAttributes.customEmoji] as? ChatTextInputTextCustomEmojiAttribute {
|
|
isAnimated = true
|
|
}
|
|
})
|
|
return isAnimated
|
|
}
|
|
|
|
public var text: NSAttributedString
|
|
public var style: Style
|
|
public var animation: Animation
|
|
public var font: Font
|
|
public var alignment: Alignment
|
|
public var fontSize: CGFloat
|
|
public var color: DrawingColor
|
|
public var lineWidth: CGFloat = 0.0
|
|
|
|
public var referenceDrawingSize: CGSize
|
|
public var position: CGPoint
|
|
public var width: CGFloat
|
|
public var scale: CGFloat
|
|
public var rotation: CGFloat
|
|
|
|
public var center: CGPoint {
|
|
return self.position
|
|
}
|
|
|
|
public var renderImage: UIImage?
|
|
public var renderSubEntities: [DrawingEntity]?
|
|
|
|
public var isMedia: Bool {
|
|
return false
|
|
}
|
|
|
|
public class AnimationFrame: Codable {
|
|
private enum CodingKeys: String, CodingKey {
|
|
case timestamp
|
|
case duration
|
|
case image
|
|
}
|
|
|
|
public let timestamp: Double
|
|
public let duration: Double
|
|
public let image: UIImage
|
|
|
|
public init(timestamp: Double, duration: Double, image: UIImage) {
|
|
self.timestamp = timestamp
|
|
self.duration = duration
|
|
self.image = image
|
|
}
|
|
|
|
required public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
self.timestamp = try container.decode(Double.self, forKey: .timestamp)
|
|
self.duration = try container.decode(Double.self, forKey: .duration)
|
|
if let renderImageData = try? container.decodeIfPresent(Data.self, forKey: .image) {
|
|
self.image = UIImage(data: renderImageData)!
|
|
} else {
|
|
fatalError()
|
|
}
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
|
|
try container.encode(self.timestamp, forKey: .timestamp)
|
|
try container.encode(self.duration, forKey: .duration)
|
|
if let data = self.image.pngData() {
|
|
try container.encode(data, forKey: .image)
|
|
}
|
|
}
|
|
}
|
|
public var renderAnimationFrames: [AnimationFrame]?
|
|
|
|
public init(text: NSAttributedString, style: Style, animation: Animation, font: Font, alignment: Alignment, fontSize: CGFloat, color: DrawingColor) {
|
|
self.uuid = UUID()
|
|
|
|
self.text = text
|
|
self.style = style
|
|
self.animation = animation
|
|
self.font = font
|
|
self.alignment = alignment
|
|
self.fontSize = fontSize
|
|
self.color = color
|
|
|
|
self.referenceDrawingSize = .zero
|
|
self.position = .zero
|
|
self.width = 100.0
|
|
self.scale = 1.0
|
|
self.rotation = 0.0
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
self.uuid = try container.decode(UUID.self, forKey: .uuid)
|
|
let text = try container.decode(String.self, forKey: .text)
|
|
|
|
let attributedString = NSMutableAttributedString(string: text)
|
|
let textAttributes = try container.decode([CustomEmojiAttribute].self, forKey: .textAttributes)
|
|
for attribute in textAttributes {
|
|
attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: attribute.attribute, range: attribute.range)
|
|
}
|
|
self.text = attributedString
|
|
|
|
self.style = try container.decode(Style.self, forKey: .style)
|
|
self.animation = try container.decode(Animation.self, forKey: .animation)
|
|
self.font = try container.decode(Font.self, forKey: .font)
|
|
self.alignment = try container.decode(Alignment.self, forKey: .alignment)
|
|
self.fontSize = try container.decode(CGFloat.self, forKey: .fontSize)
|
|
self.color = try container.decode(DrawingColor.self, forKey: .color)
|
|
self.referenceDrawingSize = try container.decode(CGSize.self, forKey: .referenceDrawingSize)
|
|
self.position = try container.decode(CGPoint.self, forKey: .position)
|
|
self.width = try container.decode(CGFloat.self, forKey: .width)
|
|
self.scale = try container.decode(CGFloat.self, forKey: .scale)
|
|
self.rotation = try container.decode(CGFloat.self, forKey: .rotation)
|
|
if let renderImageData = try? container.decodeIfPresent(Data.self, forKey: .renderImage) {
|
|
self.renderImage = UIImage(data: renderImageData)
|
|
}
|
|
if let renderSubEntities = try? container.decodeIfPresent([CodableDrawingEntity].self, forKey: .renderSubEntities) {
|
|
self.renderSubEntities = renderSubEntities.compactMap { $0.entity as? DrawingStickerEntity }
|
|
}
|
|
self.renderAnimationFrames = try container.decodeIfPresent([AnimationFrame].self, forKey: .renderAnimationFrames)
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(self.uuid, forKey: .uuid)
|
|
try container.encode(self.text.string, forKey: .text)
|
|
|
|
var textAttributes: [CustomEmojiAttribute] = []
|
|
self.text.enumerateAttributes(in: NSMakeRange(0, self.text.length), options: [], using: { attributes, range, _ in
|
|
if let value = attributes[ChatTextInputAttributes.customEmoji] as? ChatTextInputTextCustomEmojiAttribute {
|
|
textAttributes.append(CustomEmojiAttribute(attribute: value, range: range))
|
|
}
|
|
})
|
|
try container.encode(textAttributes, forKey: .textAttributes)
|
|
|
|
try container.encode(self.style, forKey: .style)
|
|
try container.encode(self.animation, forKey: .animation)
|
|
try container.encode(self.font, forKey: .font)
|
|
try container.encode(self.alignment, forKey: .alignment)
|
|
try container.encode(self.fontSize, forKey: .fontSize)
|
|
try container.encode(self.color, forKey: .color)
|
|
try container.encode(self.referenceDrawingSize, forKey: .referenceDrawingSize)
|
|
try container.encode(self.position, forKey: .position)
|
|
try container.encode(self.width, forKey: .width)
|
|
try container.encode(self.scale, forKey: .scale)
|
|
try container.encode(self.rotation, forKey: .rotation)
|
|
if let renderImage, let data = renderImage.pngData() {
|
|
try container.encode(data, forKey: .renderImage)
|
|
}
|
|
if let renderSubEntities = self.renderSubEntities {
|
|
let codableEntities: [CodableDrawingEntity] = renderSubEntities.compactMap { CodableDrawingEntity(entity: $0) }
|
|
try container.encode(codableEntities, forKey: .renderSubEntities)
|
|
}
|
|
if let renderAnimationFrames = self.renderAnimationFrames {
|
|
try container.encode(renderAnimationFrames, forKey: .renderAnimationFrames)
|
|
}
|
|
}
|
|
|
|
public func duplicate() -> DrawingEntity {
|
|
let newEntity = DrawingTextEntity(text: self.text, style: self.style, animation: self.animation, font: self.font, alignment: self.alignment, fontSize: self.fontSize, color: self.color)
|
|
newEntity.referenceDrawingSize = self.referenceDrawingSize
|
|
newEntity.position = self.position
|
|
newEntity.width = self.width
|
|
newEntity.scale = self.scale
|
|
newEntity.rotation = self.rotation
|
|
return newEntity
|
|
}
|
|
|
|
public func isEqual(to other: DrawingEntity) -> Bool {
|
|
guard let other = other as? DrawingTextEntity else {
|
|
return false
|
|
}
|
|
if self.uuid != other.uuid {
|
|
return false
|
|
}
|
|
if self.text != other.text {
|
|
return false
|
|
}
|
|
if self.style != other.style {
|
|
return false
|
|
}
|
|
if self.animation != other.animation {
|
|
return false
|
|
}
|
|
if self.font != other.font {
|
|
return false
|
|
}
|
|
if self.alignment != other.alignment {
|
|
return false
|
|
}
|
|
if self.fontSize != other.fontSize {
|
|
return false
|
|
}
|
|
if self.color != other.color {
|
|
return false
|
|
}
|
|
if self.referenceDrawingSize != other.referenceDrawingSize {
|
|
return false
|
|
}
|
|
if self.position != other.position {
|
|
return false
|
|
}
|
|
if self.width != other.width {
|
|
return false
|
|
}
|
|
if self.scale != other.scale {
|
|
return false
|
|
}
|
|
if self.rotation != other.rotation {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|