mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Attachment menu improvements
This commit is contained in:
parent
4608a5f6cd
commit
9786748565
@ -12,6 +12,7 @@ swift_library(
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode",
|
||||
],
|
||||
|
@ -2,6 +2,7 @@ import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import Postbox
|
||||
import TelegramPresentationData
|
||||
import WallpaperBackgroundNode
|
||||
|
||||
@ -339,3 +340,251 @@ public final class ChatMessageShadowNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private let maskInset: CGFloat = 1.0
|
||||
|
||||
public func bubbleMaskForType(_ type: ChatMessageBackgroundType, graphics: PrincipalThemeEssentialGraphics) -> UIImage? {
|
||||
let image: UIImage?
|
||||
switch type {
|
||||
case .none:
|
||||
image = nil
|
||||
case let .incoming(mergeType):
|
||||
switch mergeType {
|
||||
case .None:
|
||||
image = graphics.chatMessageBackgroundIncomingMaskImage
|
||||
case let .Top(side):
|
||||
if side {
|
||||
image = graphics.chatMessageBackgroundIncomingMergedTopSideMaskImage
|
||||
} else {
|
||||
image = graphics.chatMessageBackgroundIncomingMergedTopMaskImage
|
||||
}
|
||||
case .Bottom:
|
||||
image = graphics.chatMessageBackgroundIncomingMergedBottomMaskImage
|
||||
case .Both:
|
||||
image = graphics.chatMessageBackgroundIncomingMergedBothMaskImage
|
||||
case .Side:
|
||||
image = graphics.chatMessageBackgroundIncomingMergedSideMaskImage
|
||||
case .Extracted:
|
||||
image = graphics.chatMessageBackgroundIncomingExtractedMaskImage
|
||||
}
|
||||
case let .outgoing(mergeType):
|
||||
switch mergeType {
|
||||
case .None:
|
||||
image = graphics.chatMessageBackgroundOutgoingMaskImage
|
||||
case let .Top(side):
|
||||
if side {
|
||||
image = graphics.chatMessageBackgroundOutgoingMergedTopSideMaskImage
|
||||
} else {
|
||||
image = graphics.chatMessageBackgroundOutgoingMergedTopMaskImage
|
||||
}
|
||||
case .Bottom:
|
||||
image = graphics.chatMessageBackgroundOutgoingMergedBottomMaskImage
|
||||
case .Both:
|
||||
image = graphics.chatMessageBackgroundOutgoingMergedBothMaskImage
|
||||
case .Side:
|
||||
image = graphics.chatMessageBackgroundOutgoingMergedSideMaskImage
|
||||
case .Extracted:
|
||||
image = graphics.chatMessageBackgroundOutgoingExtractedMaskImage
|
||||
}
|
||||
}
|
||||
return image
|
||||
}
|
||||
|
||||
public final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||
private var backgroundContent: WallpaperBubbleBackgroundNode?
|
||||
|
||||
private var currentType: ChatMessageBackgroundType?
|
||||
private var currentMaskMode: Bool?
|
||||
private var theme: ChatPresentationThemeData?
|
||||
private var essentialGraphics: PrincipalThemeEssentialGraphics?
|
||||
private weak var backgroundNode: WallpaperBackgroundNode?
|
||||
|
||||
private var maskView: UIImageView?
|
||||
private var fixedMaskMode: Bool?
|
||||
|
||||
private var absolutePosition: (CGRect, CGSize)?
|
||||
|
||||
public var hasImage: Bool {
|
||||
return self.backgroundContent != nil
|
||||
}
|
||||
|
||||
public override var frame: CGRect {
|
||||
didSet {
|
||||
if let maskView = self.maskView {
|
||||
let maskFrame = self.bounds.insetBy(dx: -maskInset, dy: -maskInset)
|
||||
if maskView.frame != maskFrame {
|
||||
maskView.frame = maskFrame
|
||||
}
|
||||
}
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
backgroundContent.frame = self.bounds
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override init() {
|
||||
super.init()
|
||||
|
||||
self.clipsToBounds = true
|
||||
}
|
||||
|
||||
public func setMaskMode(_ maskMode: Bool, mediaBox: MediaBox) {
|
||||
if let currentType = self.currentType, let theme = self.theme, let essentialGraphics = self.essentialGraphics, let backgroundNode = self.backgroundNode {
|
||||
self.setType(type: currentType, theme: theme, essentialGraphics: essentialGraphics, maskMode: maskMode, backgroundNode: backgroundNode)
|
||||
}
|
||||
}
|
||||
|
||||
public func setType(type: ChatMessageBackgroundType, theme: ChatPresentationThemeData, essentialGraphics: PrincipalThemeEssentialGraphics, maskMode inputMaskMode: Bool, backgroundNode: WallpaperBackgroundNode?) {
|
||||
let maskMode = self.fixedMaskMode ?? inputMaskMode
|
||||
|
||||
if self.currentType != type || self.theme != theme || self.currentMaskMode != maskMode || self.essentialGraphics !== essentialGraphics || self.backgroundNode !== backgroundNode {
|
||||
let typeUpdated = self.currentType != type || self.theme != theme || self.currentMaskMode != maskMode || self.backgroundNode !== backgroundNode
|
||||
|
||||
self.currentType = type
|
||||
self.theme = theme
|
||||
self.essentialGraphics = essentialGraphics
|
||||
self.backgroundNode = backgroundNode
|
||||
|
||||
if maskMode != self.currentMaskMode {
|
||||
self.currentMaskMode = maskMode
|
||||
|
||||
if maskMode {
|
||||
let maskView: UIImageView
|
||||
if let current = self.maskView {
|
||||
maskView = current
|
||||
} else {
|
||||
maskView = UIImageView()
|
||||
maskView.frame = self.bounds.insetBy(dx: -maskInset, dy: -maskInset)
|
||||
self.maskView = maskView
|
||||
self.view.mask = maskView
|
||||
}
|
||||
} else {
|
||||
if let _ = self.maskView {
|
||||
self.view.mask = nil
|
||||
self.maskView = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
backgroundContent.frame = self.bounds
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
if typeUpdated {
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
self.backgroundContent = nil
|
||||
backgroundContent.removeFromSupernode()
|
||||
}
|
||||
|
||||
switch type {
|
||||
case .none:
|
||||
break
|
||||
case .incoming:
|
||||
if let backgroundContent = backgroundNode?.makeBubbleBackground(for: .incoming) {
|
||||
backgroundContent.frame = self.bounds
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
self.backgroundContent = backgroundContent
|
||||
self.insertSubnode(backgroundContent, at: 0)
|
||||
}
|
||||
case .outgoing:
|
||||
if let backgroundContent = backgroundNode?.makeBubbleBackground(for: .outgoing) {
|
||||
backgroundContent.frame = self.bounds
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
self.backgroundContent = backgroundContent
|
||||
self.insertSubnode(backgroundContent, at: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let maskView = self.maskView {
|
||||
maskView.image = bubbleMaskForType(type, graphics: essentialGraphics)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition = .immediate) {
|
||||
self.absolutePosition = (rect, containerSize)
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
|
||||
self.backgroundContent?.offset(value: value, animationCurve: animationCurve, duration: duration)
|
||||
}
|
||||
|
||||
public func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
|
||||
self.backgroundContent?.offsetSpring(value: value, duration: duration, damping: damping)
|
||||
}
|
||||
|
||||
public func updateFrame(_ value: CGRect, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void = {}) {
|
||||
if let maskView = self.maskView {
|
||||
transition.updateFrame(view: maskView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width, height: value.size.height)).insetBy(dx: -maskInset, dy: -maskInset))
|
||||
}
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
transition.updateFrame(layer: backgroundContent.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width, height: value.size.height)))
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: transition)
|
||||
}
|
||||
}
|
||||
transition.updateFrame(node: self, frame: value, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
public func updateFrame(_ value: CGRect, transition: CombinedTransition, completion: @escaping () -> Void = {}) {
|
||||
if let maskView = self.maskView {
|
||||
transition.updateFrame(layer: maskView.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width, height: value.size.height)).insetBy(dx: -maskInset, dy: -maskInset))
|
||||
}
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
transition.updateFrame(layer: backgroundContent.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width, height: value.size.height)))
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: transition)
|
||||
}
|
||||
}
|
||||
transition.updateFrame(layer: self.layer, frame: value, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
public func animateFrom(sourceView: UIView, mediaBox: MediaBox, transition: CombinedTransition) {
|
||||
if transition.isAnimated {
|
||||
let previousFrame = self.frame
|
||||
self.updateFrame(CGRect(origin: CGPoint(x: previousFrame.minX, y: sourceView.frame.minY), size: sourceView.frame.size), transition: .immediate)
|
||||
self.updateFrame(previousFrame, transition: transition)
|
||||
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import CheckNode
|
||||
import MosaicLayout
|
||||
import WallpaperBackgroundNode
|
||||
import AccountContext
|
||||
import ChatMessageBackground
|
||||
|
||||
|
||||
private class MediaPickerSelectedItemNode: ASDisplayNode {
|
||||
let asset: TGMediaAsset
|
||||
@ -189,12 +191,56 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
private class MessageBackgroundNode: ASDisplayNode {
|
||||
private let backgroundWallpaperNode: ChatMessageBubbleBackdrop
|
||||
private let backgroundNode: ChatMessageBackground
|
||||
private let shadowNode: ChatMessageShadowNode
|
||||
|
||||
override init() {
|
||||
self.backgroundWallpaperNode = ChatMessageBubbleBackdrop()
|
||||
self.backgroundNode = ChatMessageBackground()
|
||||
self.shadowNode = ChatMessageShadowNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundWallpaperNode)
|
||||
self.addSubnode(self.backgroundNode)
|
||||
}
|
||||
|
||||
private var absoluteRect: (CGRect, CGSize)?
|
||||
|
||||
func update(size: CGSize, theme: PresentationTheme, wallpaper: TelegramWallpaper, graphics: PrincipalThemeEssentialGraphics, wallpaperBackgroundNode: WallpaperBackgroundNode, transition: ContainedViewLayoutTransition) {
|
||||
|
||||
self.backgroundNode.setType(type: .outgoing(.Extracted), highlighted: false, graphics: graphics, maskMode: false, hasWallpaper: wallpaper.hasWallpaper, transition: transition, backgroundNode: wallpaperBackgroundNode)
|
||||
self.backgroundWallpaperNode.setType(type: .outgoing(.Extracted), theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), essentialGraphics: graphics, maskMode: true, backgroundNode: wallpaperBackgroundNode)
|
||||
self.shadowNode.setType(type: .outgoing(.Extracted), hasWallpaper: wallpaper.hasWallpaper, graphics: graphics)
|
||||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(), size: size)
|
||||
self.backgroundNode.updateLayout(size: backgroundFrame.size, transition: transition)
|
||||
self.backgroundWallpaperNode.updateFrame(backgroundFrame, transition: transition)
|
||||
self.shadowNode.updateLayout(backgroundFrame: backgroundFrame, transition: transition)
|
||||
|
||||
if let (rect, size) = self.absoluteRect {
|
||||
self.updateAbsoluteRect(rect, within: size)
|
||||
}
|
||||
}
|
||||
|
||||
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||
self.absoluteRect = (rect, containerSize)
|
||||
|
||||
var backgroundWallpaperFrame = self.backgroundWallpaperNode.frame
|
||||
backgroundWallpaperFrame.origin.x += rect.minX
|
||||
backgroundWallpaperFrame.origin.y += rect.minY
|
||||
self.backgroundWallpaperNode.update(rect: backgroundWallpaperFrame, within: containerSize)
|
||||
}
|
||||
}
|
||||
|
||||
final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDelegate {
|
||||
private let context: AccountContext
|
||||
|
||||
fileprivate let wallpaperBackgroundNode: WallpaperBackgroundNode
|
||||
private let scrollNode: ASScrollNode
|
||||
private var backgroundNodes: [Int: ASImageNode] = [:]
|
||||
private var backgroundNodes: [Int: MessageBackgroundNode] = [:]
|
||||
private var itemNodes: [String: MediaPickerSelectedItemNode] = [:]
|
||||
|
||||
private var reorderFeedback: HapticFeedback?
|
||||
@ -231,7 +277,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
|
||||
self.scrollNode.view.showsVerticalScrollIndicator = false
|
||||
|
||||
self.view.addGestureRecognizer(ReorderingGestureRecognizer(shouldBegin: { [weak self] point in
|
||||
if let strongSelf = self, !strongSelf.scrollNode.view.isDragging {
|
||||
if let strongSelf = self, !strongSelf.scrollNode.view.isDragging && strongSelf.itemNodes.count > 1 {
|
||||
let point = strongSelf.view.convert(point, to: strongSelf.scrollNode.view)
|
||||
for (_, itemNode) in strongSelf.itemNodes {
|
||||
if itemNode.frame.contains(point) {
|
||||
@ -292,6 +338,15 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
|
||||
absoluteRect.origin.y = size.height - absoluteRect.origin.y - absoluteRect.size.height
|
||||
itemNode.updateAbsoluteRect(absoluteRect, within: self.bounds.size)
|
||||
}
|
||||
|
||||
for (_, itemNode) in self.backgroundNodes {
|
||||
var absoluteRect = itemNode.frame
|
||||
if let supernode = self.supernode {
|
||||
absoluteRect = supernode.convert(itemNode.bounds, from: itemNode)
|
||||
}
|
||||
absoluteRect.origin.y = size.height - absoluteRect.origin.y - absoluteRect.size.height
|
||||
itemNode.updateAbsoluteRect(absoluteRect, within: self.bounds.size)
|
||||
}
|
||||
}
|
||||
|
||||
private func beginReordering(itemNode: MediaPickerSelectedItemNode) {
|
||||
@ -486,21 +541,24 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
|
||||
previewNode.updateFrame(previewNodeFrame, within: size, updateFrame: false)
|
||||
}
|
||||
|
||||
let graphics = PresentationResourcesChat.principalGraphics(theme: theme, wallpaper: wallpaper, bubbleCorners: bubbleCorners)
|
||||
|
||||
var groupIndex = 0
|
||||
for (items, groupSize) in groupLayouts {
|
||||
let groupRect = CGRect(origin: CGPoint(x: insets.left + floorToScreenPixels((size.width - insets.left - insets.right - groupSize.width) / 2.0), y: insets.top + contentHeight), size: groupSize)
|
||||
|
||||
let groupBackgroundNode: ASImageNode
|
||||
let groupBackgroundNode: MessageBackgroundNode
|
||||
if let current = self.backgroundNodes[groupIndex] {
|
||||
groupBackgroundNode = current
|
||||
} else {
|
||||
groupBackgroundNode = ASImageNode()
|
||||
groupBackgroundNode = MessageBackgroundNode()
|
||||
groupBackgroundNode.displaysAsynchronously = false
|
||||
self.backgroundNodes[groupIndex] = groupBackgroundNode
|
||||
self.scrollNode.insertSubnode(groupBackgroundNode, at: 0)
|
||||
}
|
||||
groupBackgroundNode.image = self.graphics?.chatMessageBackgroundOutgoingExtractedImage
|
||||
transition.updateFrame(node: groupBackgroundNode, frame: groupRect.insetBy(dx: -6.0, dy: -3.0).offsetBy(dx: 3.0, dy: 0.0))
|
||||
|
||||
transition.updateFrame(node: groupBackgroundNode, frame: groupRect.insetBy(dx: -5.0, dy: -2.0).offsetBy(dx: 3.0, dy: 0.0))
|
||||
groupBackgroundNode.update(size: groupBackgroundNode.frame.size, theme: theme, wallpaper: wallpaper, graphics: graphics, wallpaperBackgroundNode: self.wallpaperBackgroundNode, transition: transition)
|
||||
|
||||
for (item, itemRect, itemPosition) in items {
|
||||
if let identifier = item.uniqueIdentifier, let itemNode = self.itemNodes[identifier] {
|
||||
|
@ -1,255 +0,0 @@
|
||||
import Foundation
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import Postbox
|
||||
import TelegramPresentationData
|
||||
import WallpaperBackgroundNode
|
||||
import ChatMessageBackground
|
||||
|
||||
private let maskInset: CGFloat = 1.0
|
||||
|
||||
func bubbleMaskForType(_ type: ChatMessageBackgroundType, graphics: PrincipalThemeEssentialGraphics) -> UIImage? {
|
||||
let image: UIImage?
|
||||
switch type {
|
||||
case .none:
|
||||
image = nil
|
||||
case let .incoming(mergeType):
|
||||
switch mergeType {
|
||||
case .None:
|
||||
image = graphics.chatMessageBackgroundIncomingMaskImage
|
||||
case let .Top(side):
|
||||
if side {
|
||||
image = graphics.chatMessageBackgroundIncomingMergedTopSideMaskImage
|
||||
} else {
|
||||
image = graphics.chatMessageBackgroundIncomingMergedTopMaskImage
|
||||
}
|
||||
case .Bottom:
|
||||
image = graphics.chatMessageBackgroundIncomingMergedBottomMaskImage
|
||||
case .Both:
|
||||
image = graphics.chatMessageBackgroundIncomingMergedBothMaskImage
|
||||
case .Side:
|
||||
image = graphics.chatMessageBackgroundIncomingMergedSideMaskImage
|
||||
case .Extracted:
|
||||
image = graphics.chatMessageBackgroundIncomingExtractedMaskImage
|
||||
}
|
||||
case let .outgoing(mergeType):
|
||||
switch mergeType {
|
||||
case .None:
|
||||
image = graphics.chatMessageBackgroundOutgoingMaskImage
|
||||
case let .Top(side):
|
||||
if side {
|
||||
image = graphics.chatMessageBackgroundOutgoingMergedTopSideMaskImage
|
||||
} else {
|
||||
image = graphics.chatMessageBackgroundOutgoingMergedTopMaskImage
|
||||
}
|
||||
case .Bottom:
|
||||
image = graphics.chatMessageBackgroundOutgoingMergedBottomMaskImage
|
||||
case .Both:
|
||||
image = graphics.chatMessageBackgroundOutgoingMergedBothMaskImage
|
||||
case .Side:
|
||||
image = graphics.chatMessageBackgroundOutgoingMergedSideMaskImage
|
||||
case .Extracted:
|
||||
image = graphics.chatMessageBackgroundOutgoingExtractedMaskImage
|
||||
}
|
||||
}
|
||||
return image
|
||||
}
|
||||
|
||||
final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||
private var backgroundContent: WallpaperBubbleBackgroundNode?
|
||||
|
||||
private var currentType: ChatMessageBackgroundType?
|
||||
private var currentMaskMode: Bool?
|
||||
private var theme: ChatPresentationThemeData?
|
||||
private var essentialGraphics: PrincipalThemeEssentialGraphics?
|
||||
private weak var backgroundNode: WallpaperBackgroundNode?
|
||||
|
||||
private var maskView: UIImageView?
|
||||
private var fixedMaskMode: Bool?
|
||||
|
||||
private var absolutePosition: (CGRect, CGSize)?
|
||||
|
||||
var hasImage: Bool {
|
||||
return self.backgroundContent != nil
|
||||
}
|
||||
|
||||
override var frame: CGRect {
|
||||
didSet {
|
||||
if let maskView = self.maskView {
|
||||
let maskFrame = self.bounds.insetBy(dx: -maskInset, dy: -maskInset)
|
||||
if maskView.frame != maskFrame {
|
||||
maskView.frame = maskFrame
|
||||
}
|
||||
}
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
backgroundContent.frame = self.bounds
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
self.clipsToBounds = true
|
||||
}
|
||||
|
||||
func setMaskMode(_ maskMode: Bool, mediaBox: MediaBox) {
|
||||
if let currentType = self.currentType, let theme = self.theme, let essentialGraphics = self.essentialGraphics, let backgroundNode = self.backgroundNode {
|
||||
self.setType(type: currentType, theme: theme, essentialGraphics: essentialGraphics, maskMode: maskMode, backgroundNode: backgroundNode)
|
||||
}
|
||||
}
|
||||
|
||||
func setType(type: ChatMessageBackgroundType, theme: ChatPresentationThemeData, essentialGraphics: PrincipalThemeEssentialGraphics, maskMode inputMaskMode: Bool, backgroundNode: WallpaperBackgroundNode?) {
|
||||
let maskMode = self.fixedMaskMode ?? inputMaskMode
|
||||
|
||||
if self.currentType != type || self.theme != theme || self.currentMaskMode != maskMode || self.essentialGraphics !== essentialGraphics || self.backgroundNode !== backgroundNode {
|
||||
let typeUpdated = self.currentType != type || self.theme != theme || self.currentMaskMode != maskMode || self.backgroundNode !== backgroundNode
|
||||
|
||||
self.currentType = type
|
||||
self.theme = theme
|
||||
self.essentialGraphics = essentialGraphics
|
||||
self.backgroundNode = backgroundNode
|
||||
|
||||
if maskMode != self.currentMaskMode {
|
||||
self.currentMaskMode = maskMode
|
||||
|
||||
if maskMode {
|
||||
let maskView: UIImageView
|
||||
if let current = self.maskView {
|
||||
maskView = current
|
||||
} else {
|
||||
maskView = UIImageView()
|
||||
maskView.frame = self.bounds.insetBy(dx: -maskInset, dy: -maskInset)
|
||||
self.maskView = maskView
|
||||
self.view.mask = maskView
|
||||
}
|
||||
} else {
|
||||
if let _ = self.maskView {
|
||||
self.view.mask = nil
|
||||
self.maskView = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
backgroundContent.frame = self.bounds
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
if typeUpdated {
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
self.backgroundContent = nil
|
||||
backgroundContent.removeFromSupernode()
|
||||
}
|
||||
|
||||
switch type {
|
||||
case .none:
|
||||
break
|
||||
case .incoming:
|
||||
if let backgroundContent = backgroundNode?.makeBubbleBackground(for: .incoming) {
|
||||
backgroundContent.frame = self.bounds
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
self.backgroundContent = backgroundContent
|
||||
self.insertSubnode(backgroundContent, at: 0)
|
||||
}
|
||||
case .outgoing:
|
||||
if let backgroundContent = backgroundNode?.makeBubbleBackground(for: .outgoing) {
|
||||
backgroundContent.frame = self.bounds
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
self.backgroundContent = backgroundContent
|
||||
self.insertSubnode(backgroundContent, at: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let maskView = self.maskView {
|
||||
maskView.image = bubbleMaskForType(type, graphics: essentialGraphics)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition = .immediate) {
|
||||
self.absolutePosition = (rect, containerSize)
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
|
||||
self.backgroundContent?.offset(value: value, animationCurve: animationCurve, duration: duration)
|
||||
}
|
||||
|
||||
func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
|
||||
self.backgroundContent?.offsetSpring(value: value, duration: duration, damping: damping)
|
||||
}
|
||||
|
||||
func updateFrame(_ value: CGRect, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void = {}) {
|
||||
if let maskView = self.maskView {
|
||||
transition.updateFrame(view: maskView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width, height: value.size.height)).insetBy(dx: -maskInset, dy: -maskInset))
|
||||
}
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
transition.updateFrame(layer: backgroundContent.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width, height: value.size.height)))
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: transition)
|
||||
}
|
||||
}
|
||||
transition.updateFrame(node: self, frame: value, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
func updateFrame(_ value: CGRect, transition: CombinedTransition, completion: @escaping () -> Void = {}) {
|
||||
if let maskView = self.maskView {
|
||||
transition.updateFrame(layer: maskView.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width, height: value.size.height)).insetBy(dx: -maskInset, dy: -maskInset))
|
||||
}
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
transition.updateFrame(layer: backgroundContent.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width, height: value.size.height)))
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: transition)
|
||||
}
|
||||
}
|
||||
transition.updateFrame(layer: self.layer, frame: value, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
func animateFrom(sourceView: UIView, mediaBox: MediaBox, transition: CombinedTransition) {
|
||||
if transition.isAnimated {
|
||||
let previousFrame = self.frame
|
||||
self.updateFrame(CGRect(origin: CGPoint(x: previousFrame.minX, y: sourceView.frame.minY), size: sourceView.frame.size), transition: .immediate)
|
||||
self.updateFrame(previousFrame, transition: transition)
|
||||
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user