Various fixes

This commit is contained in:
Ilya Laktyushin 2025-04-08 20:36:32 +04:00
parent 55953feeb1
commit 3cc5231185
10 changed files with 150 additions and 242 deletions

View File

@ -86,6 +86,7 @@ swift_library(
"//submodules/UndoUI", "//submodules/UndoUI",
"//submodules/ContextUI", "//submodules/ContextUI",
"//submodules/AvatarNode", "//submodules/AvatarNode",
"//submodules/TelegramUI/Components/Utils/AnimatableProperty",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -4,125 +4,7 @@ import MetalKit
import ComponentFlow import ComponentFlow
import Display import Display
import MetalImageView import MetalImageView
import AnimatableProperty
private final class PropertyAnimation<T: Interpolatable> {
let from: T
let to: T
let animation: ComponentTransition.Animation
let startTimestamp: Double
private let interpolator: (Interpolatable, Interpolatable, CGFloat) -> Interpolatable
init(fromValue: T, toValue: T, animation: ComponentTransition.Animation, startTimestamp: Double) {
self.from = fromValue
self.to = toValue
self.animation = animation
self.startTimestamp = startTimestamp
self.interpolator = T.interpolator()
}
func valueAt(_ t: CGFloat) -> Interpolatable {
if t <= 0.0 {
return self.from
} else if t >= 1.0 {
return self.to
} else {
return self.interpolator(self.from, self.to, t)
}
}
}
private final class AnimatableProperty<T: Interpolatable> {
var presentationValue: T
var value: T
private var animation: PropertyAnimation<T>?
init(value: T) {
self.value = value
self.presentationValue = value
}
func update(value: T, transition: ComponentTransition = .immediate) {
let currentTimestamp = CACurrentMediaTime()
if case .none = transition.animation {
if let animation = self.animation, case let .curve(duration, curve) = animation.animation {
self.value = value
let elapsed = duration - (currentTimestamp - animation.startTimestamp)
if let presentationValue = self.presentationValue as? CGFloat, let newValue = value as? CGFloat, abs(presentationValue - newValue) > 0.56 {
self.animation = PropertyAnimation(fromValue: self.presentationValue, toValue: value, animation: .curve(duration: elapsed * 0.8, curve: curve), startTimestamp: currentTimestamp)
} else {
self.animation = PropertyAnimation(fromValue: self.presentationValue, toValue: value, animation: .curve(duration: elapsed, curve: curve), startTimestamp: currentTimestamp)
}
} else {
self.value = value
self.presentationValue = value
self.animation = nil
}
} else {
self.value = value
self.animation = PropertyAnimation(fromValue: self.presentationValue, toValue: value, animation: transition.animation, startTimestamp: currentTimestamp)
}
}
func tick(timestamp: Double) -> Bool {
guard let animation = self.animation, case let .curve(duration, curve) = animation.animation else {
return false
}
let timeFromStart = timestamp - animation.startTimestamp
var t = max(0.0, timeFromStart / duration)
switch curve {
case .linear:
break
case .easeInOut:
t = listViewAnimationCurveEaseInOut(t)
case .spring:
t = lookupSpringValue(t)
case let .custom(x1, y1, x2, y2):
t = bezierPoint(CGFloat(x1), CGFloat(y1), CGFloat(x2), CGFloat(y2), t)
}
self.presentationValue = animation.valueAt(t) as! T
if timeFromStart <= duration {
return true
}
self.animation = nil
return false
}
}
private func lookupSpringValue(_ t: CGFloat) -> CGFloat {
let table: [(CGFloat, CGFloat)] = [
(0.0, 0.0),
(0.0625, 0.1123005598783493),
(0.125, 0.31598418951034546),
(0.1875, 0.5103585720062256),
(0.25, 0.6650152802467346),
(0.3125, 0.777747631072998),
(0.375, 0.8557760119438171),
(0.4375, 0.9079672694206238),
(0.5, 0.942038357257843),
(0.5625, 0.9638798832893372),
(0.625, 0.9776856303215027),
(0.6875, 0.9863143563270569),
(0.75, 0.991658091545105),
(0.8125, 0.9949421286582947),
(0.875, 0.9969474077224731),
(0.9375, 0.9981651306152344),
(1.0, 1.0)
]
for i in 0 ..< table.count - 2 {
let lhs = table[i]
let rhs = table[i + 1]
if t >= lhs.0 && t <= rhs.0 {
let fraction = (t - lhs.0) / (rhs.0 - lhs.0)
let value = lhs.1 + fraction * (rhs.1 - lhs.1)
return value
}
}
return 1.0
}
private class ShutterBlobLayer: MetalImageLayer { private class ShutterBlobLayer: MetalImageLayer {
override public init() { override public init() {

View File

@ -504,7 +504,6 @@ final class GiftSetupScreenComponent: Component {
self.state?.updated() self.state?.updated()
starsContext.add(balance: StarsAmount(value: stars, nanos: 0)) starsContext.add(balance: StarsAmount(value: stars, nanos: 0))
let _ = (starsContext.onUpdate let _ = (starsContext.onUpdate
|> deliverOnMainQueue).start(next: { |> deliverOnMainQueue).start(next: {
proceed() proceed()

View File

@ -32,7 +32,7 @@ final class FlipButtonContentComponent: Component {
private let icon = SimpleLayer() private let icon = SimpleLayer()
init() { init() {
self.backgroundView = BlurredBackgroundView(color: UIColor(white: 0.0, alpha: 0.5), enableBlur: true) self.backgroundView = BlurredBackgroundView(color: UIColor(white: 0.2, alpha: 0.45), enableBlur: true)
super.init(frame: CGRect()) super.init(frame: CGRect())

View File

@ -1001,9 +1001,9 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
})) }))
} }
if let starsState = data.starsState { if let starsState = data.starsState {
if !isPremiumDisabled || starsState.balance > StarsAmount.zero { if !isPremiumDisabled || abs(starsState.balance.value) > 0 {
let balanceText: NSAttributedString let balanceText: NSAttributedString
if starsState.balance > StarsAmount.zero { if abs(starsState.balance.value) > 0 {
let formattedLabel = formatStarsAmountText(starsState.balance, dateTimeFormat: presentationData.dateTimeFormat) let formattedLabel = formatStarsAmountText(starsState.balance, dateTimeFormat: presentationData.dateTimeFormat)
let smallLabelFont = Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize / 17.0 * 13.0)) let smallLabelFont = Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize / 17.0 * 13.0))
let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize)

View File

@ -69,6 +69,7 @@ swift_library(
"//submodules/TelegramUI/Components/EmojiStatusComponent", "//submodules/TelegramUI/Components/EmojiStatusComponent",
"//submodules/Components/HierarchyTrackingLayer", "//submodules/Components/HierarchyTrackingLayer",
"//submodules/TelegramUI/Components/ChatListTitleView", "//submodules/TelegramUI/Components/ChatListTitleView",
"//submodules/TelegramUI/Components/Utils/AnimatableProperty",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -87,7 +87,7 @@ fragment half4 cameraBlobFragment(
} else if (primaryParameters.y < 0) { } else if (primaryParameters.y < 0) {
float cutRadius = abs(primaryParameters.y); float cutRadius = abs(primaryParameters.y);
float2 primaryFeatheredOffset = primaryOffset; float2 primaryFeatheredOffset = primaryOffset;
primaryFeatheredOffset.x *= 1.129; primaryFeatheredOffset.x *= 1.131;
float distFromCenter = length(uv - primaryFeatheredOffset); float distFromCenter = length(uv - primaryFeatheredOffset);

View File

@ -5,7 +5,7 @@ import MetalKit
import MetalEngine import MetalEngine
import ComponentFlow import ComponentFlow
import TelegramPresentationData import TelegramPresentationData
import AnimatableProperty
private var metalLibraryValue: MTLLibrary? private var metalLibraryValue: MTLLibrary?
func metalLibrary(device: MTLDevice) -> MTLLibrary? { func metalLibrary(device: MTLDevice) -> MTLLibrary? {
@ -28,124 +28,7 @@ func metalLibrary(device: MTLDevice) -> MTLLibrary? {
return library return library
} }
private final class PropertyAnimation<T: Interpolatable> {
let from: T
let to: T
let animation: ComponentTransition.Animation
let startTimestamp: Double
private let interpolator: (Interpolatable, Interpolatable, CGFloat) -> Interpolatable
init(fromValue: T, toValue: T, animation: ComponentTransition.Animation, startTimestamp: Double) {
self.from = fromValue
self.to = toValue
self.animation = animation
self.startTimestamp = startTimestamp
self.interpolator = T.interpolator()
}
func valueAt(_ t: CGFloat) -> Interpolatable {
if t <= 0.0 {
return self.from
} else if t >= 1.0 {
return self.to
} else {
return self.interpolator(self.from, self.to, t)
}
}
}
private final class AnimatableProperty<T: Interpolatable> {
var presentationValue: T
var value: T
private var animation: PropertyAnimation<T>?
init(value: T) {
self.value = value
self.presentationValue = value
}
func update(value: T, transition: ComponentTransition = .immediate) {
let currentTimestamp = CACurrentMediaTime()
if case .none = transition.animation {
if let animation = self.animation, case let .curve(duration, curve) = animation.animation {
self.value = value
let elapsed = duration - (currentTimestamp - animation.startTimestamp)
if let presentationValue = self.presentationValue as? CGFloat, let newValue = value as? CGFloat, abs(presentationValue - newValue) > 0.56 {
self.animation = PropertyAnimation(fromValue: self.presentationValue, toValue: value, animation: .curve(duration: elapsed * 0.8, curve: curve), startTimestamp: currentTimestamp)
} else {
self.animation = PropertyAnimation(fromValue: self.presentationValue, toValue: value, animation: .curve(duration: elapsed, curve: curve), startTimestamp: currentTimestamp)
}
} else {
self.value = value
self.presentationValue = value
self.animation = nil
}
} else {
self.value = value
self.animation = PropertyAnimation(fromValue: self.presentationValue, toValue: value, animation: transition.animation, startTimestamp: currentTimestamp)
}
}
func tick(timestamp: Double) -> Bool {
guard let animation = self.animation, case let .curve(duration, curve) = animation.animation else {
return false
}
let timeFromStart = timestamp - animation.startTimestamp
var t = max(0.0, timeFromStart / duration)
switch curve {
case .linear:
break
case .easeInOut:
t = listViewAnimationCurveEaseInOut(t)
case .spring:
t = lookupSpringValue(t)
case let .custom(x1, y1, x2, y2):
t = bezierPoint(CGFloat(x1), CGFloat(y1), CGFloat(x2), CGFloat(y2), t)
}
self.presentationValue = animation.valueAt(t) as! T
if timeFromStart <= duration {
return true
}
self.animation = nil
return false
}
}
private func lookupSpringValue(_ t: CGFloat) -> CGFloat {
let table: [(CGFloat, CGFloat)] = [
(0.0, 0.0),
(0.0625, 0.1123005598783493),
(0.125, 0.31598418951034546),
(0.1875, 0.5103585720062256),
(0.25, 0.6650152802467346),
(0.3125, 0.777747631072998),
(0.375, 0.8557760119438171),
(0.4375, 0.9079672694206238),
(0.5, 0.942038357257843),
(0.5625, 0.9638798832893372),
(0.625, 0.9776856303215027),
(0.6875, 0.9863143563270569),
(0.75, 0.991658091545105),
(0.8125, 0.9949421286582947),
(0.875, 0.9969474077224731),
(0.9375, 0.9981651306152344),
(1.0, 1.0)
]
for i in 0 ..< table.count - 2 {
let lhs = table[i]
let rhs = table[i + 1]
if t >= lhs.0 && t <= rhs.0 {
let fraction = (t - lhs.0) / (rhs.0 - lhs.0)
let value = lhs.1 + fraction * (rhs.1 - lhs.1)
return value
}
}
return 1.0
}
final class StoryBlobLayer: MetalEngineSubjectLayer, MetalEngineSubject { final class StoryBlobLayer: MetalEngineSubjectLayer, MetalEngineSubject {
var internalData: MetalEngineSubjectInternalData? var internalData: MetalEngineSubjectInternalData?

View File

@ -0,0 +1,19 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "AnimatableProperty",
module_name = "AnimatableProperty",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Display",
"//submodules/ComponentFlow",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,123 @@
import Foundation
import UIKit
import ComponentFlow
import Display
private final class PropertyAnimation<T: Interpolatable> {
let from: T
let to: T
let animation: ComponentTransition.Animation
let startTimestamp: Double
private let interpolator: (Interpolatable, Interpolatable, CGFloat) -> Interpolatable
init(fromValue: T, toValue: T, animation: ComponentTransition.Animation, startTimestamp: Double) {
self.from = fromValue
self.to = toValue
self.animation = animation
self.startTimestamp = startTimestamp
self.interpolator = T.interpolator()
}
func valueAt(_ t: CGFloat) -> Interpolatable {
if t <= 0.0 {
return self.from
} else if t >= 1.0 {
return self.to
} else {
return self.interpolator(self.from, self.to, t)
}
}
}
public final class AnimatableProperty<T: Interpolatable> {
public private(set) var presentationValue: T
public private(set) var value: T
private var animation: PropertyAnimation<T>?
public init(value: T) {
self.value = value
self.presentationValue = value
}
public func update(value: T, transition: ComponentTransition = .immediate) {
let currentTimestamp = CACurrentMediaTime()
if case .none = transition.animation {
if let animation = self.animation, case let .curve(duration, curve) = animation.animation {
self.value = value
let elapsed = duration - (currentTimestamp - animation.startTimestamp)
if let presentationValue = self.presentationValue as? CGFloat, let newValue = value as? CGFloat, abs(presentationValue - newValue) > 0.56 {
self.animation = PropertyAnimation(fromValue: self.presentationValue, toValue: value, animation: .curve(duration: elapsed * 0.8, curve: curve), startTimestamp: currentTimestamp)
} else {
self.animation = PropertyAnimation(fromValue: self.presentationValue, toValue: value, animation: .curve(duration: elapsed, curve: curve), startTimestamp: currentTimestamp)
}
} else {
self.value = value
self.presentationValue = value
self.animation = nil
}
} else {
self.value = value
self.animation = PropertyAnimation(fromValue: self.presentationValue, toValue: value, animation: transition.animation, startTimestamp: currentTimestamp)
}
}
public func tick(timestamp: Double) -> Bool {
guard let animation = self.animation, case let .curve(duration, curve) = animation.animation else {
return false
}
let timeFromStart = timestamp - animation.startTimestamp
var t = max(0.0, timeFromStart / duration)
switch curve {
case .linear:
break
case .easeInOut:
t = listViewAnimationCurveEaseInOut(t)
case .spring:
t = lookupSpringValue(t)
case let .custom(x1, y1, x2, y2):
t = bezierPoint(CGFloat(x1), CGFloat(y1), CGFloat(x2), CGFloat(y2), t)
}
self.presentationValue = animation.valueAt(t) as! T
if timeFromStart <= duration {
return true
}
self.animation = nil
return false
}
}
private func lookupSpringValue(_ t: CGFloat) -> CGFloat {
let table: [(CGFloat, CGFloat)] = [
(0.0, 0.0),
(0.0625, 0.1123005598783493),
(0.125, 0.31598418951034546),
(0.1875, 0.5103585720062256),
(0.25, 0.6650152802467346),
(0.3125, 0.777747631072998),
(0.375, 0.8557760119438171),
(0.4375, 0.9079672694206238),
(0.5, 0.942038357257843),
(0.5625, 0.9638798832893372),
(0.625, 0.9776856303215027),
(0.6875, 0.9863143563270569),
(0.75, 0.991658091545105),
(0.8125, 0.9949421286582947),
(0.875, 0.9969474077224731),
(0.9375, 0.9981651306152344),
(1.0, 1.0)
]
for i in 0 ..< table.count - 2 {
let lhs = table[i]
let rhs = table[i + 1]
if t >= lhs.0 && t <= rhs.0 {
let fraction = (t - lhs.0) / (rhs.0 - lhs.0)
let value = lhs.1 + fraction * (rhs.1 - lhs.1)
return value
}
}
return 1.0
}