mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Emoji improvements
This commit is contained in:
parent
97de870643
commit
b56132cbc1
@ -6,23 +6,56 @@ import ComponentDisplayAdapters
|
||||
|
||||
public final class BlurredBackgroundComponent: Component {
|
||||
public let color: UIColor
|
||||
public let tintContainerView: UIView?
|
||||
|
||||
public init(
|
||||
color: UIColor
|
||||
color: UIColor,
|
||||
tintContainerView: UIView? = nil
|
||||
) {
|
||||
self.color = color
|
||||
self.tintContainerView = tintContainerView
|
||||
}
|
||||
|
||||
public static func ==(lhs: BlurredBackgroundComponent, rhs: BlurredBackgroundComponent) -> Bool {
|
||||
if lhs.color != rhs.color {
|
||||
return false
|
||||
}
|
||||
if lhs.tintContainerView !== rhs.tintContainerView {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: BlurredBackgroundView {
|
||||
private var tintMaskView: UIView?
|
||||
|
||||
public func update(component: BlurredBackgroundComponent, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
if let tintContainerView = component.tintContainerView {
|
||||
self.updateColor(color: .clear, forceKeepBlur: true, transition: transition.containedViewLayoutTransition)
|
||||
|
||||
let tintMaskView: UIView
|
||||
if let current = self.tintMaskView {
|
||||
tintMaskView = current
|
||||
} else {
|
||||
tintMaskView = UIView()
|
||||
self.tintMaskView = tintMaskView
|
||||
self.addSubview(tintMaskView)
|
||||
}
|
||||
|
||||
tintMaskView.backgroundColor = component.color
|
||||
transition.setFrame(view: tintMaskView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
|
||||
if tintMaskView.mask !== tintContainerView {
|
||||
tintMaskView.mask = tintContainerView
|
||||
}
|
||||
} else {
|
||||
self.updateColor(color: component.color, transition: transition.containedViewLayoutTransition)
|
||||
|
||||
if let tintMaskView = self.tintMaskView {
|
||||
self.tintMaskView = nil
|
||||
tintMaskView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
self.update(size: availableSize, transition: transition.containedViewLayoutTransition)
|
||||
|
||||
return availableSize
|
||||
@ -30,7 +63,7 @@ public final class BlurredBackgroundComponent: Component {
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(color: .clear, enableBlur: true)
|
||||
return View(color: nil, enableBlur: true)
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
|
@ -62,7 +62,7 @@ private func adjustFrameRate(animation: CAAnimation) {
|
||||
}
|
||||
|
||||
public extension CALayer {
|
||||
func makeAnimation(from: AnyObject, to: AnyObject, keyPath: String, timingFunction: String, duration: Double, delay: Double = 0.0, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) -> CAAnimation {
|
||||
func makeAnimation(from: AnyObject?, to: AnyObject, keyPath: String, timingFunction: String, duration: Double, delay: Double = 0.0, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) -> CAAnimation {
|
||||
if timingFunction.hasPrefix(kCAMediaTimingFunctionCustomSpringPrefix) {
|
||||
let components = timingFunction.components(separatedBy: "_")
|
||||
let damping = Float(components[1]) ?? 100.0
|
||||
@ -158,7 +158,7 @@ public extension CALayer {
|
||||
}
|
||||
}
|
||||
|
||||
func animate(from: AnyObject, to: AnyObject, keyPath: String, timingFunction: String, duration: Double, delay: Double = 0.0, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
func animate(from: AnyObject?, to: AnyObject, keyPath: String, timingFunction: String, duration: Double, delay: Double = 0.0, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
let animation = self.makeAnimation(from: from, to: to, keyPath: keyPath, timingFunction: timingFunction, duration: duration, delay: delay, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: completion)
|
||||
self.add(animation, forKey: additive ? nil : keyPath)
|
||||
}
|
||||
|
@ -633,7 +633,7 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
case let .animated(duration, curve):
|
||||
let previousFrame: CGRect
|
||||
if beginWithCurrentState, let presentation = view.layer.presentation() {
|
||||
if beginWithCurrentState, (view.layer.animation(forKey: "position") != nil || view.layer.animation(forKey: "bounds") != nil), let presentation = view.layer.presentation() {
|
||||
previousFrame = presentation.frame
|
||||
} else {
|
||||
previousFrame = view.frame
|
||||
@ -662,7 +662,7 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
case let .animated(duration, curve):
|
||||
let previousFrame: CGRect
|
||||
if beginWithCurrentState, let presentation = layer.presentation() {
|
||||
if beginWithCurrentState, (layer.animation(forKey: "position") != nil || layer.animation(forKey: "bounds") != nil), let presentation = layer.presentation() {
|
||||
previousFrame = presentation.frame
|
||||
} else {
|
||||
previousFrame = layer.frame
|
||||
|
@ -287,7 +287,7 @@ public final class NavigationBackgroundNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
open class BlurredBackgroundView: UIView {
|
||||
private var _color: UIColor
|
||||
private var _color: UIColor?
|
||||
|
||||
private var enableBlur: Bool
|
||||
|
||||
@ -304,8 +304,8 @@ open class BlurredBackgroundView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public init(color: UIColor, enableBlur: Bool = true) {
|
||||
self._color = .clear
|
||||
public init(color: UIColor?, enableBlur: Bool = true) {
|
||||
self._color = nil
|
||||
self.enableBlur = enableBlur
|
||||
|
||||
self.backgroundView = UIView()
|
||||
@ -314,15 +314,17 @@ open class BlurredBackgroundView: UIView {
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
|
||||
if let color = color {
|
||||
self.updateColor(color: color, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func updateBackgroundBlur(forceKeepBlur: Bool) {
|
||||
if self.enableBlur && !sharedIsReduceTransparencyEnabled && ((self._color.alpha > .ulpOfOne && self._color.alpha < 0.95) || forceKeepBlur) {
|
||||
if let color = self._color, self.enableBlur && !sharedIsReduceTransparencyEnabled && ((color.alpha > .ulpOfOne && color.alpha < 0.95) || forceKeepBlur) {
|
||||
if self.effectView == nil {
|
||||
let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
|
||||
|
||||
@ -369,16 +371,16 @@ open class BlurredBackgroundView: UIView {
|
||||
public func updateColor(color: UIColor, enableBlur: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) {
|
||||
let effectiveEnableBlur = enableBlur ?? self.enableBlur
|
||||
|
||||
if self._color.isEqual(color) && self.enableBlur == effectiveEnableBlur {
|
||||
if self._color == color && self.enableBlur == effectiveEnableBlur {
|
||||
return
|
||||
}
|
||||
self._color = color
|
||||
self.enableBlur = effectiveEnableBlur
|
||||
|
||||
if sharedIsReduceTransparencyEnabled {
|
||||
transition.updateBackgroundColor(layer: self.backgroundView.layer, color: self._color.withAlphaComponent(1.0))
|
||||
transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color.withAlphaComponent(1.0))
|
||||
} else {
|
||||
transition.updateBackgroundColor(layer: self.backgroundView.layer, color: self._color)
|
||||
transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color)
|
||||
}
|
||||
|
||||
self.updateBackgroundBlur(forceKeepBlur: forceKeepBlur)
|
||||
|
@ -71,7 +71,7 @@ public func adjustSaturationInContext(context: DrawingContext, saturation: CGFlo
|
||||
vImageMatrixMultiply_ARGB8888(&buffer, &buffer, &matrix, divisor, nil, nil, vImage_Flags(kvImageDoNotTile))
|
||||
}
|
||||
|
||||
private func generateGradient(size: CGSize, colors inputColors: [UIColor], positions: [CGPoint], adjustSaturation: CGFloat = 1.0) -> UIImage {
|
||||
private func generateGradient(size: CGSize, colors inputColors: [UIColor], positions: [CGPoint], adjustSaturation: CGFloat = 1.0) -> (UIImage, String) {
|
||||
let colors: [UIColor] = inputColors.count == 1 ? [inputColors[0], inputColors[0], inputColors[0]] : inputColors
|
||||
|
||||
let width = Int(size.width)
|
||||
@ -179,7 +179,23 @@ private func generateGradient(size: CGSize, colors inputColors: [UIColor], posit
|
||||
adjustSaturationInContext(context: context, saturation: adjustSaturation)
|
||||
}
|
||||
|
||||
return context.generateImage()!
|
||||
var hashString = ""
|
||||
hashString.append("\(size.width)x\(size.height)")
|
||||
for color in colors {
|
||||
hashString.append("_\(color.argb)")
|
||||
}
|
||||
for position in positions {
|
||||
hashString.append("_\(position.x):\(position.y)")
|
||||
}
|
||||
hashString.append("_\(adjustSaturation)")
|
||||
|
||||
return (context.generateImage()!, hashString)
|
||||
}
|
||||
|
||||
public protocol GradientBackgroundPatternOverlayLayer: CALayer {
|
||||
var isAnimating: Bool { get set }
|
||||
|
||||
func updateCompositionData(size: CGSize, backgroundImage: UIImage, backgroundImageHash: String)
|
||||
}
|
||||
|
||||
public final class GradientBackgroundNode: ASDisplayNode {
|
||||
@ -218,12 +234,14 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
|
||||
public static func generatePreview(size: CGSize, colors: [UIColor]) -> UIImage {
|
||||
let positions = gatherPositions(shiftArray(array: GradientBackgroundNode.basePositions, offset: 0))
|
||||
return generateGradient(size: size, colors: colors, positions: positions)
|
||||
return generateGradient(size: size, colors: colors, positions: positions).0
|
||||
}
|
||||
|
||||
private var colors: [UIColor]
|
||||
private var phase: Int = 0
|
||||
|
||||
private var backgroundImageHash: String?
|
||||
|
||||
public let contentView: UIImageView
|
||||
private var validPhase: Int?
|
||||
private var invalidated: Bool = false
|
||||
@ -234,7 +252,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
if let current = self._dimmedImage {
|
||||
return current
|
||||
} else if let (size, colors, positions) = self.dimmedImageParams {
|
||||
self._dimmedImage = generateGradient(size: size, colors: colors, positions: positions, adjustSaturation: self.saturation)
|
||||
self._dimmedImage = generateGradient(size: size, colors: colors, positions: positions, adjustSaturation: self.saturation).0
|
||||
return self._dimmedImage
|
||||
} else {
|
||||
return nil
|
||||
@ -247,8 +265,12 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
private let useSharedAnimationPhase: Bool
|
||||
static var sharedPhase: Int = 0
|
||||
|
||||
private var isAnimating: Bool = false
|
||||
|
||||
private let saturation: CGFloat
|
||||
|
||||
private var patternOverlayLayer: GradientBackgroundPatternOverlayLayer?
|
||||
|
||||
public init(colors: [UIColor]? = nil, useSharedAnimationPhase: Bool = false, adjustSaturation: Bool = true) {
|
||||
self.useSharedAnimationPhase = useSharedAnimationPhase
|
||||
self.saturation = adjustSaturation ? 1.7 : 1.0
|
||||
@ -275,6 +297,31 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
deinit {
|
||||
}
|
||||
|
||||
public func setPatternOverlay(layer: GradientBackgroundPatternOverlayLayer?) {
|
||||
if self.patternOverlayLayer === layer {
|
||||
return
|
||||
}
|
||||
|
||||
if let patternOverlayLayer = self.patternOverlayLayer {
|
||||
if patternOverlayLayer.superlayer == self.layer {
|
||||
patternOverlayLayer.removeFromSuperlayer()
|
||||
}
|
||||
self.patternOverlayLayer = nil
|
||||
}
|
||||
|
||||
self.patternOverlayLayer = layer
|
||||
|
||||
if let patternOverlayLayer = self.patternOverlayLayer {
|
||||
self.layer.addSublayer(patternOverlayLayer)
|
||||
|
||||
patternOverlayLayer.isAnimating = self.isAnimating
|
||||
|
||||
if let image = self.contentView.image, let backgroundImageHash = self.backgroundImageHash, self.contentView.bounds.width > 1.0, self.contentView.bounds.height > 1.0 {
|
||||
patternOverlayLayer.updateCompositionData(size: self.contentView.bounds.size, backgroundImage: image, backgroundImageHash: backgroundImageHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, extendAnimation: Bool, backwards: Bool, completion: @escaping () -> Void) {
|
||||
let sizeUpdated = self.validLayout != size
|
||||
self.validLayout = size
|
||||
@ -283,6 +330,9 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
|
||||
let positions = gatherPositions(shiftArray(array: GradientBackgroundNode.basePositions, offset: self.phase % 8))
|
||||
|
||||
let previousImage = self.contentView.image
|
||||
let previousSize = self.contentView.bounds.size
|
||||
|
||||
if let validPhase = self.validPhase {
|
||||
if validPhase != self.phase || self.invalidated {
|
||||
self.validPhase = self.phase
|
||||
@ -318,7 +368,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if case let .animated(duration, curve) = transition, duration > 0.001 {
|
||||
var images: [UIImage] = []
|
||||
var images: [(UIImage, String)] = []
|
||||
|
||||
var dimmedImages: [UIImage] = []
|
||||
let needDimmedImages = !self.cloneNodes.isEmpty
|
||||
@ -350,16 +400,17 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
|
||||
images.append(generateGradient(size: imageSize, colors: self.colors, positions: morphedPositions))
|
||||
if needDimmedImages {
|
||||
dimmedImages.append(generateGradient(size: imageSize, colors: self.colors, positions: morphedPositions, adjustSaturation: self.saturation))
|
||||
dimmedImages.append(generateGradient(size: imageSize, colors: self.colors, positions: morphedPositions, adjustSaturation: self.saturation).0)
|
||||
}
|
||||
}
|
||||
|
||||
self.dimmedImageParams = (imageSize, self.colors, gatherPositions(shiftArray(array: GradientBackgroundNode.basePositions, offset: self.phase % 8)))
|
||||
|
||||
self.contentView.image = images.last
|
||||
self.contentView.image = images[images.count - 1].0
|
||||
self.backgroundImageHash = images[images.count - 1].1
|
||||
|
||||
let animation = CAKeyframeAnimation(keyPath: "contents")
|
||||
animation.values = images.map { $0.cgImage! }
|
||||
animation.values = images.map { $0.0.cgImage! }
|
||||
animation.duration = duration * UIView.animationDurationFactor()
|
||||
if backwards || extendAnimation {
|
||||
animation.calculationMode = .discrete
|
||||
@ -372,7 +423,19 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
animation.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25
|
||||
}
|
||||
|
||||
animation.completion = { _ in
|
||||
|
||||
self.isAnimating = true
|
||||
if let patternOverlayLayer = self.patternOverlayLayer {
|
||||
patternOverlayLayer.isAnimating = true
|
||||
}
|
||||
animation.completion = { [weak self] value in
|
||||
if let strongSelf = self, value {
|
||||
strongSelf.isAnimating = false
|
||||
if let patternOverlayLayer = strongSelf.patternOverlayLayer {
|
||||
patternOverlayLayer.isAnimating = false
|
||||
}
|
||||
}
|
||||
|
||||
completion()
|
||||
}
|
||||
|
||||
@ -399,10 +462,11 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let image = generateGradient(size: imageSize, colors: self.colors, positions: positions)
|
||||
let (image, imageHash) = generateGradient(size: imageSize, colors: self.colors, positions: positions)
|
||||
self.contentView.image = image
|
||||
self.backgroundImageHash = imageHash
|
||||
|
||||
let dimmedImage = generateGradient(size: imageSize, colors: self.colors, positions: positions, adjustSaturation: self.saturation)
|
||||
let dimmedImage = generateGradient(size: imageSize, colors: self.colors, positions: positions, adjustSaturation: self.saturation).0
|
||||
self._dimmedImage = dimmedImage
|
||||
self.dimmedImageParams = (imageSize, self.colors, positions)
|
||||
|
||||
@ -412,14 +476,30 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
|
||||
completion()
|
||||
}
|
||||
} else if sizeUpdated {
|
||||
let (image, imageHash) = generateGradient(size: imageSize, colors: self.colors, positions: positions)
|
||||
self.contentView.image = image
|
||||
self.backgroundImageHash = imageHash
|
||||
|
||||
let dimmedImage = generateGradient(size: imageSize, colors: self.colors, positions: positions, adjustSaturation: self.saturation).0
|
||||
self.dimmedImageParams = (imageSize, self.colors, positions)
|
||||
|
||||
for cloneNode in self.cloneNodes {
|
||||
cloneNode.value?.image = dimmedImage
|
||||
}
|
||||
|
||||
self.validPhase = self.phase
|
||||
|
||||
completion()
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
} else if sizeUpdated {
|
||||
let image = generateGradient(size: imageSize, colors: self.colors, positions: positions)
|
||||
let (image, imageHash) = generateGradient(size: imageSize, colors: self.colors, positions: positions)
|
||||
self.contentView.image = image
|
||||
self.backgroundImageHash = imageHash
|
||||
|
||||
let dimmedImage = generateGradient(size: imageSize, colors: self.colors, positions: positions, adjustSaturation: self.saturation)
|
||||
let dimmedImage = generateGradient(size: imageSize, colors: self.colors, positions: positions, adjustSaturation: self.saturation).0
|
||||
self.dimmedImageParams = (imageSize, self.colors, positions)
|
||||
|
||||
for cloneNode in self.cloneNodes {
|
||||
@ -434,6 +514,12 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
transition.updateFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
if self.contentView.image !== previousImage || self.contentView.bounds.size != previousSize {
|
||||
if let patternOverlayLayer = self.patternOverlayLayer, let imageHash = self.backgroundImageHash, let image = self.contentView.image, self.contentView.bounds.width > 1.0, self.contentView.bounds.height > 1.0 {
|
||||
patternOverlayLayer.updateCompositionData(size: size, backgroundImage: image, backgroundImageHash: imageHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateColors(colors: [UIColor]) {
|
||||
|
@ -1522,52 +1522,56 @@ public final class EmojiPagerContentComponent: Component {
|
||||
if let clearIconLayer = groupHeader.clearIconLayer, clearIconLayer.frame.insetBy(dx: -4.0, dy: -4.0).contains(groupHeaderPoint) {
|
||||
component.inputInteraction.clearGroup(id)
|
||||
}
|
||||
|
||||
/*for group in component.itemGroups {
|
||||
if group.groupId == id {
|
||||
if group.isPremium && !self.expandedPremiumGroups.contains(id) {
|
||||
if self.expandedPremiumGroups.contains(id) {
|
||||
self.expandedPremiumGroups.remove(id)
|
||||
} else {
|
||||
self.expandedPremiumGroups.insert(id)
|
||||
}
|
||||
|
||||
let previousItemLayout = self.itemLayout
|
||||
|
||||
let transition = Transition(animation: .curve(duration: 0.25, curve: .easeInOut))
|
||||
self.state?.updated(transition: transition)
|
||||
|
||||
if let previousItemLayout = previousItemLayout, let itemLayout = self.itemLayout {
|
||||
let boundsOffset = itemLayout.contentSize.height - previousItemLayout.contentSize.height
|
||||
self.scrollView.setContentOffset(CGPoint(x: 0.0, y: self.scrollView.contentOffset.y + boundsOffset), animated: false)
|
||||
transition.animateBoundsOrigin(view: self.scrollView, from: CGPoint(x: 0.0, y: -boundsOffset), to: CGPoint(), additive: true)
|
||||
}
|
||||
|
||||
return
|
||||
} else {
|
||||
break outer
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
var foundExactItem = false
|
||||
if let (item, itemKey) = self.item(atPoint: recognizer.location(in: self)), let itemLayer = self.visibleItemLayers[itemKey] {
|
||||
foundExactItem = true
|
||||
if !itemLayer.displayPlaceholder {
|
||||
component.inputInteraction.performItemAction(item, self, self.scrollView.convert(itemLayer.frame, to: self), itemLayer)
|
||||
}
|
||||
}
|
||||
|
||||
if !foundExactItem {
|
||||
if let (item, itemKey) = self.item(atPoint: recognizer.location(in: self), extendedHitRange: true), let itemLayer = self.visibleItemLayers[itemKey] {
|
||||
if !itemLayer.displayPlaceholder {
|
||||
component.inputInteraction.performItemAction(item, self, self.scrollView.convert(itemLayer.frame, to: self), itemLayer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func item(atPoint point: CGPoint) -> (Item, ItemLayer.Key)? {
|
||||
private func item(atPoint point: CGPoint, extendedHitRange: Bool = false) -> (Item, ItemLayer.Key)? {
|
||||
let localPoint = self.convert(point, to: self.scrollView)
|
||||
|
||||
var closestItem: (key: ItemLayer.Key, distance: CGFloat)?
|
||||
|
||||
for (key, itemLayer) in self.visibleItemLayers {
|
||||
if extendedHitRange {
|
||||
let position = CGPoint(x: itemLayer.frame.midX, y: itemLayer.frame.midY)
|
||||
let distance = CGPoint(x: localPoint.x - position.x, y: localPoint.y - position.y)
|
||||
let distance2 = distance.x * distance.x + distance.y * distance.y
|
||||
if let closestItemValue = closestItem {
|
||||
if closestItemValue.distance > distance2 {
|
||||
closestItem = (key, distance2)
|
||||
}
|
||||
} else {
|
||||
closestItem = (key, distance2)
|
||||
}
|
||||
} else {
|
||||
if itemLayer.frame.contains(localPoint) {
|
||||
return (itemLayer.item, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let key = closestItem?.key {
|
||||
if let itemLayer = self.visibleItemLayers[key] {
|
||||
return (itemLayer.item, key)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -1724,7 +1728,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
|
||||
let groupBorderRadius: CGFloat = 16.0
|
||||
|
||||
if itemGroup.isPremiumLocked {
|
||||
if itemGroup.isPremiumLocked && !itemGroup.isFeatured {
|
||||
validGroupBorderIds.insert(itemGroup.groupId)
|
||||
let groupBorderLayer: GroupBorderLayer
|
||||
var groupBorderTransition = transition
|
||||
|
@ -298,6 +298,7 @@ public final class EntityKeyboardComponent: Component {
|
||||
|
||||
if let stickerContent = component.stickerContent {
|
||||
var topStickerItems: [EntityKeyboardTopPanelComponent.Item] = []
|
||||
|
||||
for itemGroup in stickerContent.itemGroups {
|
||||
if let id = itemGroup.supergroupId.base as? String {
|
||||
let iconMapping: [String: String] = [
|
||||
|
@ -1063,6 +1063,10 @@ final class EntityKeyboardTopPanelComponent: Component {
|
||||
private var currentReorderingItemId: AnyHashable?
|
||||
private var currentReorderingItemContainerView: UIView?
|
||||
private var initialReorderingItemFrame: CGRect?
|
||||
private var currentReorderingScrollDisplayLink: ConstantDisplayLinkAnimator?
|
||||
private lazy var reorderingHapticFeedback: HapticFeedback = {
|
||||
return HapticFeedback()
|
||||
}()
|
||||
|
||||
private var itemLayout: ItemLayout?
|
||||
private var items: [Item] = []
|
||||
@ -1164,7 +1168,11 @@ final class EntityKeyboardTopPanelComponent: Component {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let wasReordering = strongSelf.isReordering
|
||||
strongSelf.updateIsReordering(isActive)
|
||||
if !isActive, wasReordering {
|
||||
strongSelf.endReordering()
|
||||
}
|
||||
}
|
||||
)
|
||||
self.reorderGestureRecognizer = reorderGestureRecognizer
|
||||
@ -1327,6 +1335,18 @@ final class EntityKeyboardTopPanelComponent: Component {
|
||||
if let componentView = itemView.componentView {
|
||||
reorderingItemContainerView.addSubview(componentView)
|
||||
}
|
||||
|
||||
self.reorderingHapticFeedback.impact()
|
||||
|
||||
if self.currentReorderingScrollDisplayLink == nil {
|
||||
self.currentReorderingScrollDisplayLink = ConstantDisplayLinkAnimator(update: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateReorderingAutoscroll()
|
||||
})
|
||||
self.currentReorderingScrollDisplayLink?.isPaused = false
|
||||
}
|
||||
}
|
||||
|
||||
private func endReordering() {
|
||||
@ -1348,6 +1368,11 @@ final class EntityKeyboardTopPanelComponent: Component {
|
||||
reorderingItemContainerView.removeFromSuperview()
|
||||
}
|
||||
|
||||
if let currentReorderingScrollDisplayLink = self.currentReorderingScrollDisplayLink {
|
||||
self.currentReorderingScrollDisplayLink = nil
|
||||
currentReorderingScrollDisplayLink.invalidate()
|
||||
}
|
||||
|
||||
self.currentReorderingItemId = nil
|
||||
self.temporaryReorderingOrderIndex = nil
|
||||
|
||||
@ -1361,15 +1386,23 @@ final class EntityKeyboardTopPanelComponent: Component {
|
||||
}
|
||||
reorderingItemContainerView.frame = initialReorderingItemFrame.offsetBy(dx: offset, dy: 0.0)
|
||||
|
||||
let localReorderingItemFrame = reorderingItemContainerView.convert(reorderingItemContainerView.bounds, to: self.scrollView)
|
||||
|
||||
for i in 0 ..< self.items.count {
|
||||
if !self.items[i].isReorderable {
|
||||
continue
|
||||
}
|
||||
let containerFrame = itemLayout.containerFrame(at: i)
|
||||
if containerFrame.intersects(reorderingItemContainerView.frame) {
|
||||
if containerFrame.intersects(localReorderingItemFrame) {
|
||||
let temporaryReorderingOrderIndex: (id: AnyHashable, index: Int) = (currentReorderingItemId, i)
|
||||
let hadPrevous = self.temporaryReorderingOrderIndex != nil
|
||||
if self.temporaryReorderingOrderIndex?.id != temporaryReorderingOrderIndex.id || self.temporaryReorderingOrderIndex?.index != temporaryReorderingOrderIndex.index {
|
||||
self.temporaryReorderingOrderIndex = temporaryReorderingOrderIndex
|
||||
|
||||
if hadPrevous {
|
||||
self.reorderingHapticFeedback.tap()
|
||||
}
|
||||
|
||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring)))
|
||||
}
|
||||
break
|
||||
@ -1377,6 +1410,34 @@ final class EntityKeyboardTopPanelComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
private func updateReorderingAutoscroll() {
|
||||
guard let reorderingItemContainerView = self.currentReorderingItemContainerView, let initialReorderingItemFrame = self.initialReorderingItemFrame else {
|
||||
return
|
||||
}
|
||||
|
||||
var bounds = self.scrollView.bounds
|
||||
let delta: CGFloat = 3.0
|
||||
if reorderingItemContainerView.frame.minX < 16.0 {
|
||||
bounds.origin.x -= delta
|
||||
} else if reorderingItemContainerView.frame.maxX > self.scrollView.bounds.width - 16.0 {
|
||||
bounds.origin.x += delta
|
||||
}
|
||||
|
||||
if bounds.origin.x + bounds.size.width > self.scrollView.contentSize.width {
|
||||
bounds.origin.x = self.scrollView.contentSize.width - bounds.size.width
|
||||
}
|
||||
if bounds.origin.x < 0.0 {
|
||||
bounds.origin.x = 0.0
|
||||
}
|
||||
|
||||
if self.scrollView.bounds != bounds {
|
||||
self.scrollView.bounds = bounds
|
||||
|
||||
let offset = reorderingItemContainerView.frame.minX - initialReorderingItemFrame.minX
|
||||
self.updateReordering(offset: offset)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateVisibleItems(attemptSynchronousLoads: Bool, transition: Transition) {
|
||||
guard let itemLayout = self.itemLayout else {
|
||||
return
|
||||
@ -1708,6 +1769,16 @@ final class EntityKeyboardTopPanelComponent: Component {
|
||||
let _ = itemLayout
|
||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||
|
||||
if let component = self.component, let itemLayout = self.itemLayout {
|
||||
for i in 0 ..< component.items.count {
|
||||
if component.items[i].id == itemId {
|
||||
let itemFrame = itemLayout.containerFrame(at: i)
|
||||
self.scrollView.scrollRectToVisible(itemFrame.insetBy(dx: -2.0, dy: 0.0), animated: true)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*var found = false
|
||||
for i in 0 ..< self.items.count {
|
||||
if self.items[i].id == itemId {
|
||||
|
@ -236,7 +236,8 @@ public final class TextNodeWithEntities {
|
||||
if let current = self.inlineStickerItemLayers[id] {
|
||||
itemLayer = current
|
||||
} else {
|
||||
itemLayer = InlineStickerItemLayer(context: context, attemptSynchronousLoad: attemptSynchronousLoad, emoji: stickerItem.emoji, file: stickerItem.file, cache: cache, renderer: renderer, placeholderColor: placeholderColor, pointSize: CGSize(width: floor(itemSize * 1.2), height: floor(itemSize * 1.2)))
|
||||
let pointSize = floor(itemSize * 1.3)
|
||||
itemLayer = InlineStickerItemLayer(context: context, attemptSynchronousLoad: attemptSynchronousLoad, emoji: stickerItem.emoji, file: stickerItem.file, cache: cache, renderer: renderer, placeholderColor: placeholderColor, pointSize: CGSize(width: pointSize, height: pointSize))
|
||||
self.inlineStickerItemLayers[id] = itemLayer
|
||||
self.textNode.layer.addSublayer(itemLayer)
|
||||
|
||||
@ -399,7 +400,8 @@ public class ImmediateTextNodeWithEntities: TextNode {
|
||||
if let current = self.inlineStickerItemLayers[id] {
|
||||
itemLayer = current
|
||||
} else {
|
||||
itemLayer = InlineStickerItemLayer(context: context, attemptSynchronousLoad: false, emoji: stickerItem.emoji, file: stickerItem.file, cache: cache, renderer: renderer, placeholderColor: placeholderColor, pointSize: CGSize(width: itemSize, height: itemSize))
|
||||
let pointSize = floor(itemSize * 1.3)
|
||||
itemLayer = InlineStickerItemLayer(context: context, attemptSynchronousLoad: false, emoji: stickerItem.emoji, file: stickerItem.file, cache: cache, renderer: renderer, placeholderColor: placeholderColor, pointSize: CGSize(width: pointSize, height: pointSize))
|
||||
self.inlineStickerItemLayers[id] = itemLayer
|
||||
self.layer.addSublayer(itemLayer)
|
||||
|
||||
|
@ -496,9 +496,10 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
|
||||
let stickerItems: Signal<EmojiPagerContentComponent, NoError> = combineLatest(
|
||||
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: stickerOrderedItemListCollectionIds, namespaces: stickerNamespaces, aroundIndex: nil, count: 10000000),
|
||||
hasPremium
|
||||
hasPremium,
|
||||
context.account.viewTracker.featuredStickerPacks()
|
||||
)
|
||||
|> map { view, hasPremium -> EmojiPagerContentComponent in
|
||||
|> map { view, hasPremium, featuredStickerPacks -> EmojiPagerContentComponent in
|
||||
struct ItemGroup {
|
||||
var supergroupId: AnyHashable
|
||||
var id: AnyHashable
|
||||
@ -630,6 +631,11 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
}
|
||||
}
|
||||
|
||||
var installedCollectionIds = Set<ItemCollectionId>()
|
||||
for (id, _, _) in view.collectionInfos {
|
||||
installedCollectionIds.insert(id)
|
||||
}
|
||||
|
||||
for entry in view.entries {
|
||||
guard let item = entry.item as? StickerPackItem else {
|
||||
continue
|
||||
@ -656,6 +662,33 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
}
|
||||
}
|
||||
|
||||
for featuredStickerPack in featuredStickerPacks {
|
||||
if installedCollectionIds.contains(featuredStickerPack.info.id) {
|
||||
continue
|
||||
}
|
||||
|
||||
for item in featuredStickerPack.topItems {
|
||||
let resultItem = EmojiPagerContentComponent.Item(
|
||||
file: item.file,
|
||||
staticEmoji: nil,
|
||||
subgroupId: nil
|
||||
)
|
||||
|
||||
let supergroupId = featuredStickerPack.info.id
|
||||
let groupId: AnyHashable = supergroupId
|
||||
let isPremiumLocked: Bool = item.file.isPremiumSticker && !hasPremium
|
||||
if isPremiumLocked && isPremiumDisabled {
|
||||
continue
|
||||
}
|
||||
if let groupIndex = itemGroupIndexById[groupId] {
|
||||
itemGroups[groupIndex].items.append(resultItem)
|
||||
} else {
|
||||
itemGroupIndexById[groupId] = itemGroups.count
|
||||
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: featuredStickerPack.info.title, isPremiumLocked: isPremiumLocked, isFeatured: true, displayPremiumBadges: false, items: [resultItem]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EmojiPagerContentComponent(
|
||||
id: "stickers",
|
||||
context: context,
|
||||
|
@ -135,7 +135,9 @@ func chatHistoryEntriesForView(
|
||||
}
|
||||
|
||||
if presentationData.largeEmoji, message.media.isEmpty {
|
||||
if stickersEnabled && message.text.count == 1, let _ = associatedData.animatedEmojiStickers[message.text.basicEmoji.0], (message.textEntitiesAttribute?.entities.isEmpty ?? true) {
|
||||
if stickersEnabled && message.text.count == 1, let entities = message.textEntitiesAttribute?.entities, entities.count == 1, case let .CustomEmoji(_, fileId) = entities[0].type, let _ = message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] {
|
||||
contentTypeHint = .animatedEmoji
|
||||
} else if stickersEnabled && message.text.count == 1, let _ = associatedData.animatedEmojiStickers[message.text.basicEmoji.0], (message.textEntitiesAttribute?.entities.isEmpty ?? true) {
|
||||
contentTypeHint = .animatedEmoji
|
||||
} else if message.text.count < 10 && messageIsElligibleForLargeEmoji(message) {
|
||||
contentTypeHint = .largeEmoji
|
||||
|
@ -518,9 +518,17 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
} else if self.telegramFile == nil && self.telegramDice == nil {
|
||||
let (emoji, fitz) = item.message.text.basicEmoji
|
||||
var emojiFile: TelegramMediaFile?
|
||||
|
||||
var emojiFile: TelegramMediaFile?
|
||||
if let entities = item.message.textEntitiesAttribute?.entities, entities.count == 1, case let .CustomEmoji(_, fileId) = entities[0].type {
|
||||
if let file = item.message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile {
|
||||
emojiFile = file
|
||||
}
|
||||
}
|
||||
|
||||
if emojiFile == nil {
|
||||
emojiFile = item.associatedData.animatedEmojiStickers[emoji]?.first?.file
|
||||
}
|
||||
if emojiFile == nil {
|
||||
emojiFile = item.associatedData.animatedEmojiStickers[emoji.strippedEmoji]?.first?.file
|
||||
}
|
||||
@ -547,11 +555,14 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
|
||||
var animationItems: [Int: StickerPackItem]?
|
||||
if let emojiFile = emojiFile, emojiFile.isCustomEmoji {
|
||||
} else {
|
||||
if let items = item.associatedData.additionalAnimatedEmojiStickers[textEmoji] {
|
||||
animationItems = items
|
||||
} else if let items = item.associatedData.additionalAnimatedEmojiStickers[additionalTextEmoji] {
|
||||
animationItems = items
|
||||
}
|
||||
}
|
||||
|
||||
if let animationItems = animationItems {
|
||||
for (_, animationItem) in animationItems {
|
||||
@ -580,18 +591,20 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
playbackMode = .once
|
||||
}
|
||||
} else if let emojiFile = self.emojiFile {
|
||||
isEmoji = true
|
||||
file = emojiFile
|
||||
//if alreadySeen && emojiFile.resource is LocalFileReferenceMediaResource {
|
||||
|
||||
if emojiFile.isCustomEmoji {
|
||||
playbackMode = .loop
|
||||
} else {
|
||||
isEmoji = true
|
||||
playbackMode = .still(.end)
|
||||
//} else {
|
||||
// playbackMode = .once
|
||||
//}
|
||||
|
||||
let (_, fitz) = item.message.text.basicEmoji
|
||||
if let fitz = fitz {
|
||||
fitzModifier = EmojiFitzModifier(emoji: fitz)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let isPlaying = self.visibilityStatus == true && !self.forceStopAnimations
|
||||
if !isPlaying {
|
||||
@ -1805,7 +1818,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
return .optionalAction({
|
||||
item.controllerInteraction.displayDiceTooltip(dice)
|
||||
})
|
||||
} else if let _ = self.emojiFile {
|
||||
} else if let emojiFile = self.emojiFile, !emojiFile.isCustomEmoji {
|
||||
if let animationNode = self.animationNode as? AnimatedStickerNode, let _ = recognizer {
|
||||
var shouldPlay = false
|
||||
if !animationNode.isPlaying {
|
||||
|
@ -809,7 +809,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
return UIView()
|
||||
}
|
||||
|
||||
return EmojiTextAttachmentView(context: context, emoji: emoji, file: emoji.file, cache: presentationContext.animationCache, renderer: presentationContext.animationRenderer, placeholderColor: presentationInterfaceState.theme.chat.inputPanel.inputTextColor.withAlphaComponent(0.12), pointSize: CGSize(width: 24.0, height: 24.0))
|
||||
let pointSize = floor(24.0 * 1.3)
|
||||
return EmojiTextAttachmentView(context: context, emoji: emoji, file: emoji.file, cache: presentationContext.animationCache, renderer: presentationContext.animationRenderer, placeholderColor: presentationInterfaceState.theme.chat.inputPanel.inputTextColor.withAlphaComponent(0.12), pointSize: CGSize(width: pointSize, height: pointSize))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,208 @@ public protocol WallpaperBackgroundNode: ASDisplayNode {
|
||||
func makeDimmedNode() -> ASDisplayNode?
|
||||
}
|
||||
|
||||
private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOverlayLayer {
|
||||
enum SoftlightMode {
|
||||
case whileAnimating
|
||||
case always
|
||||
case never
|
||||
}
|
||||
|
||||
var patternContentImage: UIImage? {
|
||||
didSet {
|
||||
if self.patternContentImage !== oldValue {
|
||||
self.updateComposedImage()
|
||||
self.updateContents()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var composedContentImage: UIImage? {
|
||||
didSet {
|
||||
if self.composedContentImage !== oldValue {
|
||||
self.updateContents()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var softlightMode: SoftlightMode = .whileAnimating {
|
||||
didSet {
|
||||
if self.softlightMode != oldValue {
|
||||
self.updateFilters()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isAnimating: Bool = false {
|
||||
didSet {
|
||||
if self.isAnimating != oldValue {
|
||||
self.updateFilters()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var isUsingSoftlight: Bool = false
|
||||
|
||||
var suspendCompositionUpdates: Bool = false
|
||||
private var needsCompositionUpdate: Bool = false
|
||||
|
||||
private func updateFilters() {
|
||||
let useSoftlight: Bool
|
||||
switch self.softlightMode {
|
||||
case .whileAnimating:
|
||||
useSoftlight = self.isAnimating
|
||||
case .always:
|
||||
useSoftlight = true
|
||||
case .never:
|
||||
useSoftlight = false
|
||||
}
|
||||
if self.isUsingSoftlight != useSoftlight {
|
||||
self.isUsingSoftlight = useSoftlight
|
||||
|
||||
if self.isUsingSoftlight {
|
||||
self.compositingFilter = "softLightBlendMode"
|
||||
} else {
|
||||
self.compositingFilter = nil
|
||||
}
|
||||
|
||||
self.updateContents()
|
||||
self.updateOpacity()
|
||||
}
|
||||
}
|
||||
|
||||
private var allowSettingContents: Bool = false
|
||||
private var currentContents: UIImage?
|
||||
|
||||
override var contents: Any? {
|
||||
get {
|
||||
return super.contents
|
||||
} set(value) {
|
||||
if self.allowSettingContents {
|
||||
super.contents = value
|
||||
} else {
|
||||
assert(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var allowSettingOpacity: Bool = false
|
||||
var compositionOpacity: Float = 1.0 {
|
||||
didSet {
|
||||
if self.compositionOpacity != oldValue {
|
||||
self.updateOpacity()
|
||||
self.updateComposedImage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var opacity: Float {
|
||||
get {
|
||||
return super.opacity
|
||||
} set(value) {
|
||||
if self.allowSettingOpacity {
|
||||
super.opacity = value
|
||||
} else {
|
||||
assert(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var compositionData: (size: CGSize, backgroundImage: UIImage, backgroundImageHash: String)?
|
||||
|
||||
func updateCompositionData(size: CGSize, backgroundImage: UIImage, backgroundImageHash: String) {
|
||||
if self.compositionData?.size == size && self.compositionData?.backgroundImage === backgroundImage {
|
||||
return
|
||||
}
|
||||
self.compositionData = (size, backgroundImage, backgroundImageHash)
|
||||
|
||||
self.updateComposedImage()
|
||||
}
|
||||
|
||||
func updateCompositionIfNeeded() {
|
||||
if self.needsCompositionUpdate {
|
||||
self.needsCompositionUpdate = false
|
||||
self.updateComposedImage()
|
||||
}
|
||||
}
|
||||
|
||||
private static var cachedComposedImage: (size: CGSize, patternContentImage: UIImage, backgroundImageHash: String, image: UIImage)?
|
||||
|
||||
private func updateComposedImage() {
|
||||
if self.suspendCompositionUpdates {
|
||||
self.needsCompositionUpdate = true
|
||||
return
|
||||
}
|
||||
|
||||
switch self.softlightMode {
|
||||
case .always:
|
||||
return
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
guard let (size, backgroundImage, backgroundImageHash) = self.compositionData, let patternContentImage = self.patternContentImage else {
|
||||
return
|
||||
}
|
||||
|
||||
if let cachedComposedImage = EffectImageLayer.cachedComposedImage, cachedComposedImage.size == size, cachedComposedImage.backgroundImageHash == backgroundImageHash, cachedComposedImage.patternContentImage === patternContentImage {
|
||||
self.composedContentImage = cachedComposedImage.image
|
||||
return
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
#endif
|
||||
|
||||
let composedContentImage = generateImage(size, contextGenerator: { size, context in
|
||||
context.draw(backgroundImage.cgImage!, in: CGRect(origin: CGPoint(), size: size))
|
||||
context.setBlendMode(.softLight)
|
||||
context.setAlpha(CGFloat(self.compositionOpacity))
|
||||
context.draw(patternContentImage.cgImage!, in: CGRect(origin: CGPoint(), size: size))
|
||||
}, opaque: true, scale: min(UIScreenScale, patternContentImage.scale))
|
||||
self.composedContentImage = composedContentImage
|
||||
|
||||
#if DEBUG
|
||||
print("Wallpaper composed image updated in \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms")
|
||||
#endif
|
||||
|
||||
if self.softlightMode == .whileAnimating, let composedContentImage = composedContentImage {
|
||||
EffectImageLayer.cachedComposedImage = (size, patternContentImage, backgroundImageHash, composedContentImage)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateContents() {
|
||||
var contents: UIImage?
|
||||
|
||||
if self.isUsingSoftlight {
|
||||
contents = self.patternContentImage
|
||||
} else {
|
||||
contents = self.composedContentImage
|
||||
}
|
||||
|
||||
if self.currentContents !== contents {
|
||||
self.currentContents = contents
|
||||
|
||||
self.allowSettingContents = true
|
||||
self.contents = contents?.cgImage
|
||||
self.allowSettingContents = false
|
||||
}
|
||||
}
|
||||
|
||||
private func updateOpacity() {
|
||||
if self.isUsingSoftlight {
|
||||
self.allowSettingOpacity = true
|
||||
self.opacity = self.compositionOpacity
|
||||
self.allowSettingOpacity = false
|
||||
self.isOpaque = false
|
||||
} else {
|
||||
self.allowSettingOpacity = true
|
||||
self.opacity = 1.0
|
||||
self.allowSettingOpacity = false
|
||||
self.isOpaque = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode {
|
||||
final class BubbleBackgroundNodeImpl: ASDisplayNode, WallpaperBubbleBackgroundNode {
|
||||
private let bubbleType: WallpaperBubbleType
|
||||
@ -348,7 +550,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
|
||||
private var gradientBackgroundNode: GradientBackgroundNode?
|
||||
private var outgoingBubbleGradientBackgroundNode: GradientBackgroundNode?
|
||||
private let patternImageNode: ASImageNode
|
||||
private let patternImageLayer: EffectImageLayer
|
||||
private var isGeneratingPatternImage: Bool = false
|
||||
|
||||
private let bakedBackgroundView: UIImageView
|
||||
@ -466,7 +668,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
self.contentNode = ASDisplayNode()
|
||||
self.contentNode.contentMode = self.imageContentMode
|
||||
|
||||
self.patternImageNode = ASImageNode()
|
||||
self.patternImageLayer = EffectImageLayer()
|
||||
|
||||
self.bakedBackgroundView = UIImageView()
|
||||
self.bakedBackgroundView.isHidden = true
|
||||
@ -476,49 +678,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
self.clipsToBounds = true
|
||||
self.contentNode.frame = self.bounds
|
||||
self.addSubnode(self.contentNode)
|
||||
self.addSubnode(self.patternImageNode)
|
||||
|
||||
/*let animationList: [(String, CGPoint)] = [
|
||||
("ptrnCAT_1162_1918", CGPoint(x: 1162 - 256, y: 1918 - 256)),
|
||||
("ptrnDOG_0440_2284", CGPoint(x: 440 - 256, y: 2284 - 256)),
|
||||
("ptrnGLOB_0438_1553", CGPoint(x: 438 - 256, y: 1553 - 256)),
|
||||
("ptrnSLON_0906_1033", CGPoint(x: 906 - 256, y: 1033 - 256))
|
||||
]
|
||||
for (animation, relativePosition) in animationList {
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.automaticallyLoadFirstFrame = true
|
||||
animationNode.autoplay = true
|
||||
//self.inlineAnimationNodes.append((animationNode, relativePosition))
|
||||
self.patternImageNode.addSubnode(animationNode)
|
||||
animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: animation), width: 256, height: 256, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
}
|
||||
|
||||
self.layer.addSublayer(self.hierarchyTrackingLayer)
|
||||
self.hierarchyTrackingLayer.didEnterHierarchy = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
for (animationNode, _) in strongSelf.inlineAnimationNodes {
|
||||
animationNode.visibility = true
|
||||
}
|
||||
strongSelf.activateInlineAnimationTimer = SwiftSignalKit.Timer(timeout: 5.0, repeat: true, completion: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.inlineAnimationNodes[Int.random(in: 0 ..< strongSelf.inlineAnimationNodes.count)].0.play()
|
||||
}, queue: .mainQueue())
|
||||
strongSelf.activateInlineAnimationTimer?.start()
|
||||
}
|
||||
self.hierarchyTrackingLayer.didExitHierarchy = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
for (animationNode, _) in strongSelf.inlineAnimationNodes {
|
||||
animationNode.visibility = false
|
||||
}
|
||||
strongSelf.activateInlineAnimationTimer?.invalidate()
|
||||
}*/
|
||||
self.layer.addSublayer(self.patternImageLayer)
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -556,7 +716,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
let gradientBackgroundNode = createGradientBackgroundNode(colors: mappedColors, useSharedAnimationPhase: self.useSharedAnimationPhase)
|
||||
self.gradientBackgroundNode = gradientBackgroundNode
|
||||
self.insertSubnode(gradientBackgroundNode, aboveSubnode: self.contentNode)
|
||||
gradientBackgroundNode.addSubnode(self.patternImageNode)
|
||||
gradientBackgroundNode.setPatternOverlay(layer: self.patternImageLayer)
|
||||
}
|
||||
self.gradientBackgroundNode?.updateColors(colors: mappedColors)
|
||||
|
||||
@ -569,7 +729,8 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
if let gradientBackgroundNode = self.gradientBackgroundNode {
|
||||
self.gradientBackgroundNode = nil
|
||||
gradientBackgroundNode.removeFromSupernode()
|
||||
self.insertSubnode(self.patternImageNode, aboveSubnode: self.contentNode)
|
||||
gradientBackgroundNode.setPatternOverlay(layer: nil)
|
||||
self.layer.insertSublayer(self.patternImageLayer, above: self.contentNode.layer)
|
||||
}
|
||||
|
||||
self.motionEnabled = wallpaper.settings?.motion ?? false
|
||||
@ -654,40 +815,44 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
|
||||
let intensity = CGFloat(file.settings.intensity ?? 50) / 100.0
|
||||
if intensity < 0 {
|
||||
self.patternImageNode.alpha = 1.0
|
||||
self.patternImageNode.layer.compositingFilter = nil
|
||||
self.patternImageLayer.compositionOpacity = 1.0
|
||||
self.patternImageLayer.softlightMode = .never
|
||||
} else {
|
||||
self.patternImageNode.alpha = intensity
|
||||
self.patternImageLayer.compositionOpacity = Float(intensity)
|
||||
if patternIsBlack {
|
||||
self.patternImageNode.layer.compositingFilter = nil
|
||||
self.patternImageLayer.softlightMode = .never
|
||||
} else {
|
||||
self.patternImageNode.layer.compositingFilter = "softLightBlendMode"
|
||||
if self.useSharedAnimationPhase {
|
||||
self.patternImageLayer.softlightMode = .whileAnimating
|
||||
} else {
|
||||
self.patternImageLayer.softlightMode = .always
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.patternImageNode.isHidden = false
|
||||
self.patternImageLayer.isHidden = false
|
||||
let invertPattern = intensity < 0
|
||||
if invertPattern {
|
||||
self.backgroundColor = .black
|
||||
let contentAlpha = abs(intensity)
|
||||
self.gradientBackgroundNode?.contentView.alpha = contentAlpha
|
||||
self.contentNode.alpha = contentAlpha
|
||||
if self.patternImageNode.image != nil {
|
||||
self.patternImageNode.backgroundColor = nil
|
||||
if self.patternImageLayer.contents != nil {
|
||||
self.patternImageLayer.backgroundColor = nil
|
||||
} else {
|
||||
self.patternImageNode.backgroundColor = .black
|
||||
self.patternImageLayer.backgroundColor = UIColor.black.cgColor
|
||||
}
|
||||
} else {
|
||||
self.backgroundColor = nil
|
||||
self.gradientBackgroundNode?.contentView.alpha = 1.0
|
||||
self.contentNode.alpha = 1.0
|
||||
self.patternImageNode.backgroundColor = nil
|
||||
self.patternImageLayer.backgroundColor = nil
|
||||
}
|
||||
default:
|
||||
self.patternImageDisposable.set(nil)
|
||||
self.validPatternImage = nil
|
||||
self.patternImageNode.isHidden = true
|
||||
self.patternImageNode.backgroundColor = nil
|
||||
self.patternImageLayer.isHidden = true
|
||||
self.patternImageLayer.backgroundColor = nil
|
||||
self.backgroundColor = nil
|
||||
self.gradientBackgroundNode?.contentView.alpha = 1.0
|
||||
self.contentNode.alpha = 1.0
|
||||
@ -784,10 +949,10 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
if invertPattern {
|
||||
patternColor = .clear
|
||||
patternBackgroundColor = .clear
|
||||
if self.patternImageNode.image == nil {
|
||||
self.patternImageNode.backgroundColor = .black
|
||||
if self.patternImageLayer.contents == nil {
|
||||
self.patternImageLayer.backgroundColor = UIColor.black.cgColor
|
||||
} else {
|
||||
self.patternImageNode.backgroundColor = nil
|
||||
self.patternImageLayer.backgroundColor = nil
|
||||
}
|
||||
} else {
|
||||
if patternIsLight {
|
||||
@ -796,7 +961,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
patternColor = .white
|
||||
}
|
||||
patternBackgroundColor = .clear
|
||||
self.patternImageNode.backgroundColor = nil
|
||||
self.patternImageLayer.backgroundColor = nil
|
||||
}
|
||||
|
||||
let updatedGeneratedImage = ValidPatternGeneratedImage(wallpaper: validPatternImage.wallpaper, size: size, patternColor: patternColor.rgb, backgroundColor: patternBackgroundColor.rgb, invertPattern: invertPattern)
|
||||
@ -805,15 +970,21 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
self.validPatternGeneratedImage = updatedGeneratedImage
|
||||
|
||||
if let cachedValidPatternImage = WallpaperBackgroundNodeImpl.cachedValidPatternImage, cachedValidPatternImage.generated == updatedGeneratedImage {
|
||||
self.patternImageNode.image = cachedValidPatternImage.image
|
||||
self.patternImageLayer.suspendCompositionUpdates = true
|
||||
self.updatePatternPresentation()
|
||||
self.patternImageLayer.patternContentImage = cachedValidPatternImage.image
|
||||
self.patternImageLayer.suspendCompositionUpdates = false
|
||||
self.patternImageLayer.updateCompositionIfNeeded()
|
||||
} else {
|
||||
let patternArguments = TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets(), custom: PatternWallpaperArguments(colors: [patternBackgroundColor], rotation: nil, customPatternColor: patternColor, preview: false), scale: min(2.0, UIScreenScale))
|
||||
if self.useSharedAnimationPhase || self.patternImageNode.image == nil {
|
||||
if self.useSharedAnimationPhase || self.patternImageLayer.contents == nil {
|
||||
if let drawingContext = validPatternImage.generate(patternArguments) {
|
||||
if let image = drawingContext.generateImage() {
|
||||
self.patternImageNode.image = image
|
||||
self.patternImageLayer.suspendCompositionUpdates = true
|
||||
self.updatePatternPresentation()
|
||||
self.patternImageLayer.patternContentImage = image
|
||||
self.patternImageLayer.suspendCompositionUpdates = false
|
||||
self.patternImageLayer.updateCompositionIfNeeded()
|
||||
|
||||
if self.useSharedAnimationPhase {
|
||||
WallpaperBackgroundNodeImpl.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image)
|
||||
@ -833,7 +1004,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
return
|
||||
}
|
||||
strongSelf.isGeneratingPatternImage = false
|
||||
strongSelf.patternImageNode.image = image
|
||||
strongSelf.patternImageLayer.patternContentImage = image
|
||||
strongSelf.updatePatternPresentation()
|
||||
|
||||
if let image = image, strongSelf.useSharedAnimationPhase {
|
||||
@ -856,7 +1027,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
}
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.patternImageNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateFrame(layer: self.patternImageLayer, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user