Various improvements

This commit is contained in:
Isaac 2025-09-23 20:49:12 +08:00
parent bf3ec930bc
commit 3684c6ca15
34 changed files with 593 additions and 339 deletions

View File

@ -309,6 +309,8 @@ alternate_icon_folders = [
"PremiumTurbo", "PremiumTurbo",
] ]
composer_icon_folders = ["Telegram"]
[ [
filegroup( filegroup(
name = "{}".format(name), name = "{}".format(name),
@ -318,6 +320,15 @@ alternate_icon_folders = [
) for name in alternate_icon_folders ) for name in alternate_icon_folders
] ]
[
filegroup(
name = "{}_icon".format(name),
srcs = glob([
"Telegram-iOS/{}.icon/**/*".format(name),
]),
) for name in composer_icon_folders
]
filegroup( filegroup(
name = "LaunchScreen", name = "LaunchScreen",
srcs = glob([ srcs = glob([
@ -1699,12 +1710,13 @@ ios_application(
":RequiredDeviceCapabilitiesPlist", ":RequiredDeviceCapabilitiesPlist",
":UrlTypesInfoPlist", ":UrlTypesInfoPlist",
], ],
app_icons = [ ":{}_icon".format(name) for name in composer_icon_folders ],
alternate_icons = [ alternate_icons = [
":{}".format(name) for name in alternate_icon_folders ":{}".format(name) for name in alternate_icon_folders
], ],
resources = [ resources = [
":LaunchScreen", ":LaunchScreen",
":DefaultAppIcon", #":DefaultAppIcon",
], ],
frameworks = [ frameworks = [
":MtProtoKitFramework", ":MtProtoKitFramework",

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1024px" height="1024px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Oval</title>
<g id="Oval" stroke="none" fill="none" fill-rule="nonzero">
<circle fill="#000000" cx="512" cy="512" r="410.5"></circle>
</g>
</svg>

After

Width:  |  Height:  |  Size: 364 B

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1024px" height="1024px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Plane</title>
<g id="Plane" stroke="none" fill="none" fill-rule="nonzero">
<path d="M287.315632,507.666819 C406.984718,455.528898 486.782853,421.15639 526.710034,404.549294 C640.710251,357.13266 664.39852,348.895883 679.838202,348.621 C683.234017,348.56408 690.826804,349.405665 695.745092,353.396516 C699.897998,356.766313 701.040629,361.31843 701.587414,364.513367 C702.1342,367.708304 702.815079,374.986453 702.273832,380.673387 C696.09612,445.583141 669.36526,603.101997 655.766116,675.802034 C650.011808,706.564145 638.681397,716.878524 627.712247,717.887922 C603.873746,720.081578 585.77187,702.133777 562.683116,686.998808 C526.553773,663.315568 506.142993,648.572678 471.073211,625.462211 C430.544009,598.754068 456.817404,584.074808 479.914878,560.08479 C485.959597,553.806488 590.992581,458.270869 593.025497,449.604291 C593.279747,448.520391 593.515704,444.480123 591.115452,442.346714 C588.715199,440.213304 585.172634,440.942847 582.61622,441.52306 C578.99258,442.345492 521.275488,480.49435 409.464943,555.969631 C393.082138,567.219329 378.243103,572.700559 364.947836,572.413323 C350.290871,572.096667 322.096751,564.126052 301.137321,557.312967 C275.429726,548.956441 254.997841,544.538286 256.777,530.34632 C257.703696,522.954266 267.883239,515.394432 287.315632,507.666819 Z" id="Path-3" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,122 @@
{
"fill" : "system-light",
"groups" : [
{
"blend-mode" : "normal",
"blur-material" : 1,
"layers" : [
{
"blend-mode-specializations" : [
{
"value" : "normal"
},
{
"appearance" : "dark",
"value" : "normal"
}
],
"fill-specializations" : [
{
"appearance" : "dark",
"value" : "automatic"
}
],
"glass" : true,
"image-name" : "Plane.svg",
"name" : "Plane",
"position-specializations" : [
{
"idiom" : "watchOS",
"value" : {
"scale" : 1.35,
"translation-in-points" : [
0,
0
]
}
}
]
},
{
"blend-mode-specializations" : [
{
"value" : "normal"
},
{
"appearance" : "dark",
"value" : "normal"
}
],
"fill-specializations" : [
{
"value" : {
"linear-gradient" : [
"display-p3:0.21569,0.68627,0.89020,1.00000",
"srgb:0.11373,0.57647,0.82353,1.00000"
]
}
},
{
"appearance" : "dark",
"value" : {
"automatic-gradient" : "srgb:0.00000,0.47843,1.00000,1.00000"
}
}
],
"glass" : true,
"hidden-specializations" : [
{
"value" : false
},
{
"idiom" : "watchOS",
"value" : false
}
],
"image-name" : "Oval.svg",
"name" : "Oval",
"position-specializations" : [
{
"idiom" : "watchOS",
"value" : {
"scale" : 2,
"translation-in-points" : [
0,
0
]
}
}
]
}
],
"lighting" : "combined",
"position-specializations" : [
{
"idiom" : "watchOS",
"value" : {
"scale" : 1,
"translation-in-points" : [
0,
0
]
}
}
],
"shadow" : {
"kind" : "layer-color",
"opacity" : 1
},
"specular" : true,
"translucency" : {
"enabled" : false,
"value" : 1
}
}
],
"supported-platforms" : {
"circles" : [
"watchOS"
],
"squares" : "shared"
}
}

View File

@ -15077,3 +15077,14 @@ Error: %8$@";
"Conversation.SuggestedBirthdate.View" = "View"; "Conversation.SuggestedBirthdate.View" = "View";
"PeerInfo.ChangeProfileColor" = "Change Profile Color"; "PeerInfo.ChangeProfileColor" = "Change Profile Color";
"PUSH_MESSAGE_TODO" = "%1$@|sent you a todo-list %2$@";
"PUSH_CHANNEL_MESSAGE_TODO" = "%1$@|posted a todo-list %2$@";
"PUSH_CHANNEL_MESSAGE_TODO_DONE" = "%1$@|toggled %2$@ tasks";
"PUSH_CHANNEL_MESSAGE_TODO_APPEND" = "%1$@|added %2$@ tasks";
"PUSH_CHAT_MESSAGE_TODO" = "%2$@|%1$@ sent a todo-list %3$@";
"PUSH_CHAT_MESSAGE_TODO_DONE" = "%2$@|%1$@ toggled %3$@ tasks";
"PUSH_CHAT_MESSAGE_TODO_APPEND" = "%2$@|%1$@ added %3$@ tasks";
"PUSH_PINNED_TODO" = "%1$@|pinned a todo-list %2$@";
"PUSH_REACT_TODO" = "%1$@|reacted %2$@ to your todo %3$@";
"PUSH_CHAT_REACT_TODO" = "%2$@|%1$@ reacted %3$@ to your todo %4$@";

View File

@ -700,6 +700,9 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
let hadLayout = self.validLayout != nil let hadLayout = self.validLayout != nil
let previousAdditionalSideInsets = self.validLayout?.3 let previousAdditionalSideInsets = self.validLayout?.3
self.validLayout = (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary) self.validLayout = (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary)
let leftInset = leftInset + 8.0
let rightInset = rightInset + 8.0
var transition = transition var transition = transition
if let previousAdditionalSideInsets = previousAdditionalSideInsets, previousAdditionalSideInsets.right != additionalSideInsets.right { if let previousAdditionalSideInsets = previousAdditionalSideInsets, previousAdditionalSideInsets.right != additionalSideInsets.right {
@ -933,9 +936,13 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
} }
private func updateFieldAndButtonsLayout(inputHasText: Bool, panelHeight: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { private func updateFieldAndButtonsLayout(inputHasText: Bool, panelHeight: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
guard let (width, leftInset, rightInset, additionalSideInsets, _, metrics, _) = self.validLayout else { guard let (width, leftInsetValue, rightInsetValue, additionalSideInsets, _, metrics, _) = self.validLayout else {
return 0.0 return 0.0
} }
let leftInset = leftInsetValue + 8.0
let rightInset = rightInsetValue + 8.0
var textFieldMinHeight: CGFloat = 33.0 var textFieldMinHeight: CGFloat = 33.0
if let presentationInterfaceState = self.presentationInterfaceState { if let presentationInterfaceState = self.presentationInterfaceState {
textFieldMinHeight = calclulateTextFieldMinHeight(presentationInterfaceState, metrics: metrics) textFieldMinHeight = calclulateTextFieldMinHeight(presentationInterfaceState, metrics: metrics)
@ -1280,6 +1287,9 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
if let (width, leftInset, rightInset, _, maxHeight, metrics, _) = self.validLayout { if let (width, leftInset, rightInset, _, maxHeight, metrics, _) = self.validLayout {
let composeButtonsOffset: CGFloat = 0.0 let composeButtonsOffset: CGFloat = 0.0
let leftInset = leftInset + 8.0
let rightInset = rightInset + 8.0
let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset, maxHeight: maxHeight, metrics: metrics) let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset, maxHeight: maxHeight, metrics: metrics)
let panelHeight = self.panelHeight(textFieldHeight: textFieldHeight, metrics: metrics) let panelHeight = self.panelHeight(textFieldHeight: textFieldHeight, metrics: metrics)
var textFieldMinHeight: CGFloat = 33.0 var textFieldMinHeight: CGFloat = 33.0
@ -1421,6 +1431,9 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
private func updateTextHeight(animated: Bool) -> CGFloat? { private func updateTextHeight(animated: Bool) -> CGFloat? {
if let (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, _) = self.validLayout { if let (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, _) = self.validLayout {
let leftInset = leftInset + 8.0
let rightInset = rightInset + 8.0
let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset - additionalSideInsets.right, maxHeight: maxHeight, metrics: metrics) let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset - additionalSideInsets.right, maxHeight: maxHeight, metrics: metrics)
let panelHeight = self.panelHeight(textFieldHeight: textFieldHeight, metrics: metrics) let panelHeight = self.panelHeight(textFieldHeight: textFieldHeight, metrics: metrics)
if !self.bounds.size.height.isEqual(to: panelHeight) { if !self.bounds.size.height.isEqual(to: panelHeight) {

View File

@ -124,14 +124,16 @@ public enum ChatListItemContent {
public var hideSeparator: Bool public var hideSeparator: Bool
public var hideDate: Bool public var hideDate: Bool
public var hidePeerStatus: Bool public var hidePeerStatus: Bool
public var isInTransparentContainer: Bool
public init(commandPrefix: String?, searchQuery: String?, messageCount: Int?, hideSeparator: Bool, hideDate: Bool, hidePeerStatus: Bool) { public init(commandPrefix: String?, searchQuery: String?, messageCount: Int?, hideSeparator: Bool, hideDate: Bool, hidePeerStatus: Bool, isInTransparentContainer: Bool = false) {
self.commandPrefix = commandPrefix self.commandPrefix = commandPrefix
self.searchQuery = searchQuery self.searchQuery = searchQuery
self.messageCount = messageCount self.messageCount = messageCount
self.hideSeparator = hideSeparator self.hideSeparator = hideSeparator
self.hideDate = hideDate self.hideDate = hideDate
self.hidePeerStatus = hidePeerStatus self.hidePeerStatus = hidePeerStatus
self.isInTransparentContainer = isInTransparentContainer
} }
} }
@ -1966,6 +1968,11 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
reallyHighlighted = true reallyHighlighted = true
} }
} }
if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData {
if customMessageListData.isInTransparentContainer {
reallyHighlighted = false
}
}
} }
return reallyHighlighted return reallyHighlighted
} }

View File

@ -104,7 +104,34 @@ public extension CALayer {
return animation return animation
} else if timingFunction == kCAMediaTimingFunctionSpring { } else if timingFunction == kCAMediaTimingFunctionSpring {
if duration == 0.5 { if #available(iOS 26.0, *) {
let animation = make26SpringAnimationImpl(keyPath, duration)
animation.fromValue = from
animation.toValue = to
animation.isRemovedOnCompletion = removeOnCompletion
animation.fillMode = .forwards
if let completion {
animation.delegate = CALayerAnimationDelegate(animation: animation, completion: completion)
}
let k = Float(UIView.animationDurationFactor())
var speed: Float = 1.0
if k != 0 && k != 1 {
speed = Float(1.0) / k
}
animation.speed = speed * Float(animation.duration / duration)
animation.isAdditive = additive
if !delay.isZero {
animation.beginTime = self.convertTime(CACurrentMediaTime(), from: nil) + delay * UIView.animationDurationFactor()
animation.fillMode = .both
}
adjustFrameRate(animation: animation)
return animation
} else if duration == 0.5 {
let animation = makeSpringAnimation(keyPath) let animation = makeSpringAnimation(keyPath)
animation.fromValue = from animation.fromValue = from
animation.toValue = to animation.toValue = to

View File

@ -900,3 +900,54 @@ public extension CGPoint {
return CGPoint(x: self.x + dx, y: self.y + dy) return CGPoint(x: self.x + dx, y: self.y + dy)
} }
} }
public extension UIView {
func setMonochromaticEffect(tintColor: UIColor?) {
var overrideUserInterfaceStyle: UIUserInterfaceStyle = .unspecified
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
var alpha: CGFloat = 1.0
if let tintColor {
if tintColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
if red == 0.0 && green == 0.0 && blue == 0.0 && alpha == 1.0 {
overrideUserInterfaceStyle = .light
}
} else {
if red == 1.0 && green == 1.0 && blue == 1.0 && alpha == 1.0 {
overrideUserInterfaceStyle = .dark
}
}
}
if self.overrideUserInterfaceStyle != overrideUserInterfaceStyle {
self.overrideUserInterfaceStyle = overrideUserInterfaceStyle
setMonochromaticEffectImpl(self, overrideUserInterfaceStyle != .unspecified)
}
}
func setMonochromaticEffectAndAlpha(tintColor: UIColor?, transition: ContainedViewLayoutTransition) {
var overrideUserInterfaceStyle: UIUserInterfaceStyle = .unspecified
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
var alpha: CGFloat = 1.0
if let tintColor {
if tintColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
if red == 0.0 && green == 0.0 && blue == 0.0 {
overrideUserInterfaceStyle = .light
}
} else {
if red == 1.0 && green == 1.0 && blue == 1.0 {
overrideUserInterfaceStyle = .dark
}
}
}
if self.overrideUserInterfaceStyle != overrideUserInterfaceStyle {
self.overrideUserInterfaceStyle = overrideUserInterfaceStyle
setMonochromaticEffectImpl(self, overrideUserInterfaceStyle != .unspecified)
}
transition.updateAlpha(layer: self.layer, alpha: alpha)
}
}

View File

@ -604,7 +604,10 @@ public class Window1 {
var duration: Double = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0.0 var duration: Double = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0.0
if duration > Double.ulpOfOne { if duration > Double.ulpOfOne {
duration = 0.5 if #available(iOS 26.0, *) {
} else {
duration = 0.5
}
} }
let curve: UInt = (notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber)?.uintValue ?? 7 let curve: UInt = (notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber)?.uintValue ?? 7

View File

@ -2232,7 +2232,7 @@ final class VideoChatScreenComponent: Component {
color: .white color: .white
)), )),
background: AnyComponent( background: AnyComponent(
GlassBackgroundComponent(size: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter), tintColor: .init(kind: .custom, color: UIColor(rgb: 0x101014))) GlassBackgroundComponent(size: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter), isDark: true, tintColor: .init(kind: .custom, color: UIColor(rgb: 0x101014)))
), ),
effectAlignment: .center, effectAlignment: .center,
minSize: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter), minSize: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter),
@ -2254,7 +2254,7 @@ final class VideoChatScreenComponent: Component {
image: closeButtonImage(dark: false) image: closeButtonImage(dark: false)
)), )),
background: AnyComponent( background: AnyComponent(
GlassBackgroundComponent(size: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter), tintColor: .init(kind: .custom, color: UIColor(rgb: 0x101014))) GlassBackgroundComponent(size: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter), isDark: true, tintColor: .init(kind: .custom, color: UIColor(rgb: 0x101014)))
), ),
effectAlignment: .center, effectAlignment: .center,
minSize: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter), minSize: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter),

View File

@ -663,14 +663,14 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
panelBackgroundColorNoWallpaper: UIColor(rgb: 0x000000), panelBackgroundColorNoWallpaper: UIColor(rgb: 0x000000),
panelSeparatorColor: UIColor(rgb: 0x545458, alpha: 0.55), panelSeparatorColor: UIColor(rgb: 0x545458, alpha: 0.55),
panelControlAccentColor: UIColor(rgb: 0xffffff), panelControlAccentColor: UIColor(rgb: 0xffffff),
panelControlColor: UIColor(rgb: 0x808080), panelControlColor: UIColor(rgb: 0xffffff),
panelControlDisabledColor: UIColor(rgb: 0x808080, alpha: 0.5), panelControlDisabledColor: UIColor(rgb: 0x808080, alpha: 0.5),
panelControlDestructiveColor: UIColor(rgb: 0xff3b30), panelControlDestructiveColor: UIColor(rgb: 0xff3b30),
inputBackgroundColor: UIColor(rgb: 0x060606), inputBackgroundColor: UIColor(rgb: 0x060606),
inputStrokeColor: UIColor(rgb: 0xffffff, alpha: 0.1), inputStrokeColor: UIColor(rgb: 0xffffff, alpha: 0.1),
inputPlaceholderColor: UIColor(rgb: 0x7b7b7b), inputPlaceholderColor: UIColor(rgb: 0x7b7b7b),
inputTextColor: UIColor(rgb: 0xffffff), inputTextColor: UIColor(rgb: 0xffffff),
inputControlColor: UIColor(rgb: 0x7b7b7b), inputControlColor: UIColor(rgb: 0xffffff),
actionControlFillColor: UIColor(rgb: 0xffffff), actionControlFillColor: UIColor(rgb: 0xffffff),
actionControlForegroundColor: UIColor(rgb: 0x000000), actionControlForegroundColor: UIColor(rgb: 0x000000),
primaryTextColor: UIColor(rgb: 0xffffff), primaryTextColor: UIColor(rgb: 0xffffff),

View File

@ -943,17 +943,17 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
panelBackgroundColorNoWallpaper: UIColor(rgb: 0xffffff), panelBackgroundColorNoWallpaper: UIColor(rgb: 0xffffff),
panelSeparatorColor: UIColor(white: 1.0, alpha: 0.5), panelSeparatorColor: UIColor(white: 1.0, alpha: 0.5),
panelControlAccentColor: defaultDayAccentColor, panelControlAccentColor: defaultDayAccentColor,
panelControlColor: UIColor(rgb: 0x858e99), panelControlColor: UIColor(rgb: 0x000000, alpha: 1.0),
panelControlDisabledColor: UIColor(rgb: 0x727b87, alpha: 0.5), panelControlDisabledColor: UIColor(rgb: 0x727b87, alpha: 0.5),
panelControlDestructiveColor: UIColor(rgb: 0xff3b30), panelControlDestructiveColor: UIColor(rgb: 0xff3b30),
inputBackgroundColor: UIColor(rgb: 0xffffff), inputBackgroundColor: UIColor(rgb: 0xffffff),
inputStrokeColor: UIColor(rgb: 0x000000, alpha: 0.1), inputStrokeColor: UIColor(rgb: 0x000000, alpha: 0.1),
inputPlaceholderColor: UIColor(rgb: 0x202020, alpha: 0.4), inputPlaceholderColor: UIColor(rgb: 0x000000, alpha: 0.4),
inputTextColor: UIColor(rgb: 0x000000), inputTextColor: UIColor(rgb: 0x000000),
inputControlColor: UIColor(rgb: 0x202020, alpha: 0.6), inputControlColor: UIColor(rgb: 0x000000, alpha: 1.0),
actionControlFillColor: defaultDayAccentColor, actionControlFillColor: defaultDayAccentColor,
actionControlForegroundColor: UIColor(rgb: 0xffffff), actionControlForegroundColor: UIColor(rgb: 0xffffff),
primaryTextColor: UIColor(rgb: 0x000000, alpha: 0.9), primaryTextColor: UIColor(rgb: 0x000000, alpha: 1.0),
secondaryTextColor: UIColor(rgb: 0x202020, alpha: 0.6), secondaryTextColor: UIColor(rgb: 0x202020, alpha: 0.6),
mediaRecordingDotColor: UIColor(rgb: 0xed2521), mediaRecordingDotColor: UIColor(rgb: 0xed2521),
mediaRecordingControl: inputPanelMediaRecordingControl mediaRecordingControl: inputPanelMediaRecordingControl

View File

@ -57,6 +57,10 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
private var cachedMaskBackgroundImage: (CGPoint, UIImage, [CGRect])? private var cachedMaskBackgroundImage: (CGPoint, UIImage, [CGRect])?
private var absoluteRect: (CGRect, CGSize)? private var absoluteRect: (CGRect, CGSize)?
override public var disablesClipping: Bool {
return true
}
override public var visibility: ListViewItemNodeVisibility { override public var visibility: ListViewItemNodeVisibility {
didSet { didSet {
if oldValue != self.visibility { if oldValue != self.visibility {

View File

@ -3442,14 +3442,14 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
contentContainerNodeFrames: contentContainerNodeFrames.map { containerGroupId, containerFrame, currentItemSelection, currentContainerGroupOverlap in contentContainerNodeFrames: contentContainerNodeFrames.map { containerGroupId, containerFrame, currentItemSelection, currentContainerGroupOverlap in
return (containerGroupId, containerFrame.offsetBy(dx: 0.0, dy: layoutInsets.top), currentItemSelection, currentContainerGroupOverlap) return (containerGroupId, containerFrame.offsetBy(dx: 0.0, dy: layoutInsets.top), currentItemSelection, currentContainerGroupOverlap)
}, },
mosaicStatusOrigin: mosaicStatusOrigin?.offsetBy(dx: 0.0, dy: layoutInsets.top), mosaicStatusOrigin: mosaicStatusOrigin,
mosaicStatusSizeAndApply: mosaicStatusSizeAndApply, mosaicStatusSizeAndApply: mosaicStatusSizeAndApply,
unlockButtonPosition: unlockButtonPosition?.offsetBy(dx: 0.0, dy: layoutInsets.top), unlockButtonPosition: unlockButtonPosition?.offsetBy(dx: 0.0, dy: layoutInsets.top),
unlockButtonSizeAndApply: unlockButtonSizeApply, unlockButtonSizeAndApply: unlockButtonSizeApply,
mediaInfoOrigin: mediaInfoOrigin?.offsetBy(dx: 0.0, dy: layoutInsets.top), mediaInfoOrigin: mediaInfoOrigin?.offsetBy(dx: 0.0, dy: layoutInsets.top),
mediaInfoSizeAndApply: mediaInfoSizeApply, mediaInfoSizeAndApply: mediaInfoSizeApply,
needsShareButton: needsShareButton, needsShareButton: needsShareButton,
shareButtonOffset: shareButtonOffset?.offsetBy(dx: 0.0, dy: layoutInsets.top), shareButtonOffset: shareButtonOffset,
avatarOffset: avatarOffset, avatarOffset: avatarOffset,
hidesHeaders: hidesHeaders, hidesHeaders: hidesHeaders,
disablesComments: disablesComments, disablesComments: disablesComments,

View File

@ -86,6 +86,10 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
private var currentProgressDisposable: Disposable? private var currentProgressDisposable: Disposable?
override public var disablesClipping: Bool {
return true
}
override public var visibility: ListViewItemNodeVisibility { override public var visibility: ListViewItemNodeVisibility {
didSet { didSet {
let wasVisible = oldValue != .none let wasVisible = oldValue != .none

View File

@ -449,10 +449,10 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, maxOverlayHeight, metrics, isSecondary, isMediaInputExpanded) self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, maxOverlayHeight, metrics, isSecondary, isMediaInputExpanded)
var leftInset = leftInset var leftInset = leftInset
leftInset += 8.0 leftInset += 16.0
var rightInset = rightInset var rightInset = rightInset
rightInset += 8.0 rightInset += 16.0
let panelHeight = defaultHeight(metrics: metrics) let panelHeight = defaultHeight(metrics: metrics)

View File

@ -197,6 +197,7 @@ public final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessag
self.expandMediaInputButtonBackgroundView.contentView.addSubview(self.expandMediaInputButtonIcon) self.expandMediaInputButtonBackgroundView.contentView.addSubview(self.expandMediaInputButtonIcon)
self.expandMediaInputButtonIcon.image = PresentationResourcesChat.chatInputPanelExpandButtonImage(presentationInterfaceState.theme) self.expandMediaInputButtonIcon.image = PresentationResourcesChat.chatInputPanelExpandButtonImage(presentationInterfaceState.theme)
self.expandMediaInputButtonIcon.tintColor = theme.chat.inputPanel.inputControlColor self.expandMediaInputButtonIcon.tintColor = theme.chat.inputPanel.inputControlColor
self.expandMediaInputButtonIcon.setMonochromaticEffect(tintColor: theme.chat.inputPanel.inputControlColor)
super.init() super.init()
@ -265,6 +266,7 @@ public final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessag
public func updateTheme(theme: PresentationTheme, wallpaper: TelegramWallpaper) { public func updateTheme(theme: PresentationTheme, wallpaper: TelegramWallpaper) {
self.micButton.updateTheme(theme: theme) self.micButton.updateTheme(theme: theme)
self.expandMediaInputButtonIcon.tintColor = theme.chat.inputPanel.inputControlColor self.expandMediaInputButtonIcon.tintColor = theme.chat.inputPanel.inputControlColor
self.expandMediaInputButtonIcon.setMonochromaticEffect(tintColor: theme.chat.inputPanel.inputControlColor)
} }
private var absoluteRect: (CGRect, CGSize)? private var absoluteRect: (CGRect, CGSize)?
@ -352,8 +354,8 @@ public final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessag
self.expandMediaInputButtonBackgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: ComponentTransition(transition)) self.expandMediaInputButtonBackgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: ComponentTransition(transition))
if let image = self.expandMediaInputButtonIcon.image { if let image = self.expandMediaInputButtonIcon.image {
let expandIconFrame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) * 0.5), y: floor((size.height - image.size.height) * 0.5)), size: image.size) let expandIconFrame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) * 0.5), y: floor((size.height - image.size.height) * 0.5)), size: image.size)
transition.updatePosition(layer: self.expandMediaInputButtonIcon.layer, position: expandIconFrame.center) self.expandMediaInputButtonIcon.center = expandIconFrame.center
transition.updateBounds(layer: self.expandMediaInputButtonIcon.layer, bounds: CGRect(origin: CGPoint(), size: expandIconFrame.size)) self.expandMediaInputButtonIcon.bounds = CGRect(origin: CGPoint(), size: expandIconFrame.size)
transition.updateTransformScale(layer: self.expandMediaInputButtonIcon.layer, scale: CGPoint(x: 1.0, y: isMediaInputExpanded ? 1.0 : -1.0)) transition.updateTransformScale(layer: self.expandMediaInputButtonIcon.layer, scale: CGPoint(x: 1.0, y: isMediaInputExpanded ? 1.0 : -1.0))
} }

View File

@ -16,8 +16,7 @@ final class AccessoryItemIconButton: HighlightTrackingButton, GlassBackgroundVie
private var theme: PresentationTheme private var theme: PresentationTheme
private var strings: PresentationStrings private var strings: PresentationStrings
private var width: CGFloat private var width: CGFloat
private let iconImageView: UIImageView private let iconImageView: GlassBackgroundView.ContentImageView
private let tintMaskIconImageView: UIImageView
private var textView: ImmediateTextView? private var textView: ImmediateTextView?
private var tintMaskTextView: ImmediateTextView? private var tintMaskTextView: ImmediateTextView?
private var animationView: ComponentView<Empty>? private var animationView: ComponentView<Empty>?
@ -34,8 +33,7 @@ final class AccessoryItemIconButton: HighlightTrackingButton, GlassBackgroundVie
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
self.iconImageView = UIImageView() self.iconImageView = GlassBackgroundView.ContentImageView()
self.tintMaskIconImageView = UIImageView()
let (image, text, accessibilityLabel, alpha, _) = AccessoryItemIconButton.imageAndInsets(item: item, theme: theme, strings: strings) let (image, text, accessibilityLabel, alpha, _) = AccessoryItemIconButton.imageAndInsets(item: item, theme: theme, strings: strings)
@ -51,12 +49,11 @@ final class AccessoryItemIconButton: HighlightTrackingButton, GlassBackgroundVie
self.iconImageView.isUserInteractionEnabled = false self.iconImageView.isUserInteractionEnabled = false
self.addSubview(self.iconImageView) self.addSubview(self.iconImageView)
self.tintMask.addSubview(self.tintMaskIconImageView) self.tintMask.addSubview(self.iconImageView.tintMask)
switch item { switch item {
case .input, .botInput, .silentPost: case .input, .botInput, .silentPost:
self.iconImageView.isHidden = true self.iconImageView.isHidden = true
self.tintMaskIconImageView.isHidden = self.iconImageView.isHidden
self.animationView = ComponentView<Empty>() self.animationView = ComponentView<Empty>()
self.tintMaskAnimationView = UIImageView() self.tintMaskAnimationView = UIImageView()
default: default:
@ -93,10 +90,6 @@ final class AccessoryItemIconButton: HighlightTrackingButton, GlassBackgroundVie
self.iconImageView.tintColor = theme.chat.inputPanel.inputControlColor self.iconImageView.tintColor = theme.chat.inputPanel.inputControlColor
self.iconImageView.alpha = alpha self.iconImageView.alpha = alpha
self.tintMaskIconImageView.image = self.iconImageView.image
self.tintMaskIconImageView.tintColor = .black
self.tintMaskIconImageView.alpha = self.iconImageView.alpha
self.accessibilityLabel = accessibilityLabel self.accessibilityLabel = accessibilityLabel
self.highligthedChanged = { [weak self] highlighted in self.highligthedChanged = { [weak self] highlighted in
@ -138,10 +131,6 @@ final class AccessoryItemIconButton: HighlightTrackingButton, GlassBackgroundVie
self.iconImageView.tintColor = theme.chat.inputPanel.inputControlColor self.iconImageView.tintColor = theme.chat.inputPanel.inputControlColor
self.iconImageView.alpha = alpha self.iconImageView.alpha = alpha
self.tintMaskIconImageView.image = self.iconImageView.image
self.tintMaskIconImageView.tintColor = .black
self.tintMaskIconImageView.alpha = self.iconImageView.alpha
self.accessibilityLabel = accessibilityLabel self.accessibilityLabel = accessibilityLabel
} }
@ -205,12 +194,13 @@ final class AccessoryItemIconButton: HighlightTrackingButton, GlassBackgroundVie
if let image = self.iconImageView.image { if let image = self.iconImageView.image {
self.iconImageView.image = updatedImage self.iconImageView.image = updatedImage
self.tintMaskIconImageView.image = updatedImage
let bottomInset: CGFloat = 0.0 let bottomInset: CGFloat = 0.0
let imageFrame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0) - bottomInset), size: image.size) var imageFrame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0) - bottomInset), size: image.size)
if case .scheduledMessages = item {
imageFrame.origin.y += 1.0
}
self.iconImageView.frame = imageFrame self.iconImageView.frame = imageFrame
self.tintMaskIconImageView.frame = imageFrame
if let animationView = self.animationView { if let animationView = self.animationView {
let width = AccessoryItemIconButton.calculateWidth(item: item, image: image, text: "", strings: self.strings) let width = AccessoryItemIconButton.calculateWidth(item: item, image: image, text: "", strings: self.strings)
@ -332,6 +322,7 @@ final class AccessoryItemIconButton: HighlightTrackingButton, GlassBackgroundVie
self.tintMask.addSubview(tintMaskAnimationView) self.tintMask.addSubview(tintMaskAnimationView)
} }
} }
view.setMonochromaticEffect(tintColor: self.theme.chat.inputPanel.inputControlColor)
let animationFrameValue = CGRect(origin: CGPoint(x: animationFrame.minX + floor((animationFrame.width - animationSize.width) / 2.0), y: animationFrame.minY + floor((animationFrame.height - animationSize.height) / 2.0)), size: animationSize) let animationFrameValue = CGRect(origin: CGPoint(x: animationFrame.minX + floor((animationFrame.width - animationSize.width) / 2.0), y: animationFrame.minY + floor((animationFrame.height - animationSize.height) / 2.0)), size: animationSize)
view.frame = animationFrameValue view.frame = animationFrameValue
if let tintMaskAnimationView = self.tintMaskAnimationView { if let tintMaskAnimationView = self.tintMaskAnimationView {

View File

@ -214,11 +214,9 @@ private func makeTextInputTheme(context: AccountContext, interfaceState: ChatPre
public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, ChatInputTextNodeDelegate { public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, ChatInputTextNodeDelegate {
public let clippingNode: ASDisplayNode public let clippingNode: ASDisplayNode
public let textPlaceholderNode: ImmediateTextNodeWithEntities public let textPlaceholderNode: ImmediateTextNodeWithEntities
public let tintMaskTextPlaceholderNode: ImmediateTextNodeWithEntities
public var textLockIconNode: ASImageNode? public var textLockIconNode: ASImageNode?
public var contextPlaceholderNode: TextNode? public var contextPlaceholderNode: TextNode?
public var tintContextPlaceholderNode: TextNode?
public var slowmodePlaceholderNode: ChatTextInputSlowmodePlaceholderNode? public var slowmodePlaceholderNode: ChatTextInputSlowmodePlaceholderNode?
public let textInputContainerBackgroundView: GlassBackgroundView public let textInputContainerBackgroundView: GlassBackgroundView
public let textInputContainer: ASDisplayNode public let textInputContainer: ASDisplayNode
@ -521,20 +519,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
self.textPlaceholderNode.contentsScale = UIScreenScale self.textPlaceholderNode.contentsScale = UIScreenScale
self.textPlaceholderNode.maximumNumberOfLines = 1 self.textPlaceholderNode.maximumNumberOfLines = 1
self.textPlaceholderNode.isUserInteractionEnabled = false self.textPlaceholderNode.isUserInteractionEnabled = false
//TODO:release add tinted output instead
self.tintMaskTextPlaceholderNode = ImmediateTextNodeWithEntities()
self.tintMaskTextPlaceholderNode.arguments = TextNodeWithEntities.Arguments(
context: context,
cache: context.animationCache,
renderer: context.animationRenderer,
placeholderColor: .clear,
attemptSynchronous: true
)
self.tintMaskTextPlaceholderNode.contentMode = .topLeft
self.tintMaskTextPlaceholderNode.contentsScale = UIScreenScale
self.tintMaskTextPlaceholderNode.maximumNumberOfLines = 1
self.menuButton = HighlightTrackingButtonNode() self.menuButton = HighlightTrackingButtonNode()
self.menuButton.clipsToBounds = true self.menuButton.clipsToBounds = true
self.menuButton.cornerRadius = 16.0 self.menuButton.cornerRadius = 16.0
@ -566,7 +551,6 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
self.attachmentButton.isAccessibilityElement = true self.attachmentButton.isAccessibilityElement = true
self.attachmentButtonBackground = GlassBackgroundView(frame: CGRect()) self.attachmentButtonBackground = GlassBackgroundView(frame: CGRect())
self.attachmentButtonBackground.isUserInteractionEnabled = false
self.attachmentButton.addSubview(self.attachmentButtonBackground) self.attachmentButton.addSubview(self.attachmentButtonBackground)
self.attachmentButtonIcon = GlassBackgroundView.ContentImageView() self.attachmentButtonIcon = GlassBackgroundView.ContentImageView()
@ -785,7 +769,6 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
self.clippingNode.addSubnode(self.textInputBackgroundNode) self.clippingNode.addSubnode(self.textInputBackgroundNode)
self.textInputContainerBackgroundView.contentView.addSubview(self.textPlaceholderNode.view) self.textInputContainerBackgroundView.contentView.addSubview(self.textPlaceholderNode.view)
self.textInputContainerBackgroundView.maskContentView.addSubview(self.tintMaskTextPlaceholderNode.view)
self.menuButton.view.addSubview(self.menuButtonBackgroundView) self.menuButton.view.addSubview(self.menuButtonBackgroundView)
self.menuButton.addSubnode(self.menuButtonClippingNode) self.menuButton.addSubnode(self.menuButtonClippingNode)
@ -1039,6 +1022,8 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
} }
private func calculateTextFieldMetrics(width: CGFloat, maxHeight: CGFloat, metrics: LayoutMetrics) -> (accessoryButtonsWidth: CGFloat, textFieldHeight: CGFloat, isOverflow: Bool) { private func calculateTextFieldMetrics(width: CGFloat, maxHeight: CGFloat, metrics: LayoutMetrics) -> (accessoryButtonsWidth: CGFloat, textFieldHeight: CGFloat, isOverflow: Bool) {
let maxHeight = max(maxHeight, 40.0)
var textFieldInsets = self.textFieldInsets(metrics: metrics) var textFieldInsets = self.textFieldInsets(metrics: metrics)
if self.actionButtons.frame.width > 40.0 { if self.actionButtons.frame.width > 40.0 {
textFieldInsets.right = self.actionButtons.frame.width - 2.0 textFieldInsets.right = self.actionButtons.frame.width - 2.0
@ -1076,7 +1061,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
let unboundTextFieldHeight = max(textFieldMinHeight, ceil(measuredHeight)) let unboundTextFieldHeight = max(textFieldMinHeight, ceil(measuredHeight))
let maxNumberOfLines = min(12, (Int(fieldMaxHeight - 11.0) - 33) / 22) let maxNumberOfLines = max(1, min(12, (Int(fieldMaxHeight - 11.0) - 33) / 22))
let updatedMaxHeight = (CGFloat(maxNumberOfLines) * (22.0 + 2.0) + 10.0) let updatedMaxHeight = (CGFloat(maxNumberOfLines) * (22.0 + 2.0) + 10.0)
@ -1256,6 +1241,8 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
) -> CGFloat { ) -> CGFloat {
let previousAdditionalSideInsets = self.validLayout?.4 let previousAdditionalSideInsets = self.validLayout?.4
self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, maxOverlayHeight, metrics, isSecondary, isMediaInputExpanded) self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, maxOverlayHeight, metrics, isSecondary, isMediaInputExpanded)
let placeholderColor: UIColor = interfaceState.theme.chat.inputPanel.inputPlaceholderColor
var transition = transition var transition = transition
var additionalOffset: CGFloat = 0.0 var additionalOffset: CGFloat = 0.0
@ -2423,21 +2410,16 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
if interfaceState.slowmodeState == nil || isScheduledMessages, let contextPlaceholder = interfaceState.inputTextPanelState.contextPlaceholder { if interfaceState.slowmodeState == nil || isScheduledMessages, let contextPlaceholder = interfaceState.inputTextPanelState.contextPlaceholder {
let placeholderLayout = TextNode.asyncLayout(self.contextPlaceholderNode) let placeholderLayout = TextNode.asyncLayout(self.contextPlaceholderNode)
let tintPlaceholderLayout = TextNode.asyncLayout(self.tintContextPlaceholderNode) let contextPlaceholder = NSMutableAttributedString(attributedString: contextPlaceholder)
contextPlaceholder.addAttribute(.foregroundColor, value: placeholderColor.withAlphaComponent(1.0), range: NSRange(location: 0, length: contextPlaceholder.length))
let (placeholderSize, placeholderApply) = placeholderLayout(TextNodeLayoutArguments(attributedString: contextPlaceholder, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - leftInset - rightInset - textFieldInsets.left - textFieldInsets.right - self.textInputViewInternalInsets.left - self.textInputViewInternalInsets.right - accessoryButtonsWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let (placeholderSize, placeholderApply) = placeholderLayout(TextNodeLayoutArguments(attributedString: contextPlaceholder, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - leftInset - rightInset - textFieldInsets.left - textFieldInsets.right - self.textInputViewInternalInsets.left - self.textInputViewInternalInsets.right - accessoryButtonsWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let tintContextPlaceholder = NSMutableAttributedString(attributedString: contextPlaceholder) let tintContextPlaceholder = NSMutableAttributedString(attributedString: contextPlaceholder)
tintContextPlaceholder.addAttribute(.foregroundColor, value: UIColor.black, range: NSRange(location: 0, length: tintContextPlaceholder.length)) tintContextPlaceholder.addAttribute(.foregroundColor, value: UIColor.black, range: NSRange(location: 0, length: tintContextPlaceholder.length))
let (_, tintPlaceholderApply) = tintPlaceholderLayout(TextNodeLayoutArguments(attributedString: tintContextPlaceholder, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - leftInset - rightInset - textFieldInsets.left - textFieldInsets.right - self.textInputViewInternalInsets.left - self.textInputViewInternalInsets.right - accessoryButtonsWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let contextPlaceholderNode = placeholderApply() let contextPlaceholderNode = placeholderApply()
let tintContextPlaceholderNode = tintPlaceholderApply()
if let currentContextPlaceholderNode = self.contextPlaceholderNode, currentContextPlaceholderNode !== contextPlaceholderNode { if let currentContextPlaceholderNode = self.contextPlaceholderNode, currentContextPlaceholderNode !== contextPlaceholderNode {
self.contextPlaceholderNode = nil self.contextPlaceholderNode = nil
currentContextPlaceholderNode.removeFromSupernode() currentContextPlaceholderNode.removeFromSupernode()
} }
if let currentTintContextPlaceholderNode = self.tintContextPlaceholderNode, currentTintContextPlaceholderNode !== tintContextPlaceholderNode {
self.tintContextPlaceholderNode = nil
currentTintContextPlaceholderNode.removeFromSupernode()
}
if self.contextPlaceholderNode !== contextPlaceholderNode { if self.contextPlaceholderNode !== contextPlaceholderNode {
contextPlaceholderNode.displaysAsynchronously = false contextPlaceholderNode.displaysAsynchronously = false
@ -2445,12 +2427,6 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
self.contextPlaceholderNode = contextPlaceholderNode self.contextPlaceholderNode = contextPlaceholderNode
self.textInputContainerBackgroundView.contentView.insertSubview(contextPlaceholderNode.view, aboveSubview: self.textPlaceholderNode.view) self.textInputContainerBackgroundView.contentView.insertSubview(contextPlaceholderNode.view, aboveSubview: self.textPlaceholderNode.view)
} }
if self.tintContextPlaceholderNode !== tintContextPlaceholderNode {
tintContextPlaceholderNode.displaysAsynchronously = false
tintContextPlaceholderNode.isUserInteractionEnabled = false
self.tintContextPlaceholderNode = tintContextPlaceholderNode
self.textInputContainerBackgroundView.maskContentView.insertSubview(tintContextPlaceholderNode.view, aboveSubview: self.tintMaskTextPlaceholderNode.view)
}
let _ = placeholderApply() let _ = placeholderApply()
@ -2460,21 +2436,14 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
} else { } else {
placeholderTransition = .immediate placeholderTransition = .immediate
} }
placeholderTransition.updateFrame(node: contextPlaceholderNode, frame: CGRect(origin: CGPoint(x: self.textInputViewInternalInsets.left, y: hideOffset.y + textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: placeholderSize.size)) placeholderTransition.updateFrame(node: contextPlaceholderNode, frame: CGRect(origin: CGPoint(x: self.textInputViewInternalInsets.left, y: hideOffset.y + textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel + (accessoryPanel != nil ? 52.0 : 0.0)), size: placeholderSize.size))
contextPlaceholderNode.alpha = audioRecordingItemsAlpha contextPlaceholderNode.view.setMonochromaticEffect(tintColor: placeholderColor)
contextPlaceholderNode.alpha = audioRecordingItemsAlpha * placeholderColor.alpha
placeholderTransition.updateFrame(node: tintContextPlaceholderNode, frame: CGRect(origin: CGPoint(x: self.textInputViewInternalInsets.left, y: hideOffset.y + textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: placeholderSize.size))
tintContextPlaceholderNode.alpha = audioRecordingItemsAlpha
} else { } else {
if let contextPlaceholderNode = self.contextPlaceholderNode { if let contextPlaceholderNode = self.contextPlaceholderNode {
self.contextPlaceholderNode = nil self.contextPlaceholderNode = nil
contextPlaceholderNode.removeFromSupernode() contextPlaceholderNode.removeFromSupernode()
self.textPlaceholderNode.alpha = 1.0 self.textPlaceholderNode.alpha = 1.0 * placeholderColor.alpha
self.tintMaskTextPlaceholderNode.alpha = 1.0
}
if let tintContextPlaceholderNode = self.tintContextPlaceholderNode {
self.tintContextPlaceholderNode = nil
tintContextPlaceholderNode.removeFromSupernode()
} }
} }
@ -2499,11 +2468,9 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
if (interfaceState.slowmodeState != nil && rightSlowModeInset.isZero && !isScheduledMessages && interfaceState.editMessageState == nil) || interfaceState.inputTextPanelState.contextPlaceholder != nil { if (interfaceState.slowmodeState != nil && rightSlowModeInset.isZero && !isScheduledMessages && interfaceState.editMessageState == nil) || interfaceState.inputTextPanelState.contextPlaceholder != nil {
self.textPlaceholderNode.isHidden = true self.textPlaceholderNode.isHidden = true
self.tintMaskTextPlaceholderNode.isHidden = true
self.slowmodePlaceholderNode?.isHidden = inputHasText self.slowmodePlaceholderNode?.isHidden = inputHasText
} else { } else {
self.textPlaceholderNode.isHidden = inputHasText self.textPlaceholderNode.isHidden = inputHasText
self.tintMaskTextPlaceholderNode.isHidden = inputHasText
self.slowmodePlaceholderNode?.isHidden = true self.slowmodePlaceholderNode?.isHidden = true
} }
@ -2539,38 +2506,24 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
let textPlaceholderSize: CGSize let textPlaceholderSize: CGSize
let textPlaceholderMaxWidth: CGFloat = max(1.0, nextButtonTopRight.x - 12.0) let textPlaceholderMaxWidth: CGFloat = max(1.0, nextButtonTopRight.x - 12.0)
let placeholderColor: UIColor = interfaceState.theme.chat.inputPanel.inputPlaceholderColor
if #available(iOS 26.0, *) {
//placeholderColor = placeholderColor.withProminence(.tertiary)
}
//self.textPlaceholderNode.view.tintColor = .red
if (updatedPlaceholder != nil && self.currentPlaceholder != updatedPlaceholder) || themeUpdated { if (updatedPlaceholder != nil && self.currentPlaceholder != updatedPlaceholder) || themeUpdated {
let currentPlaceholder = updatedPlaceholder ?? self.currentPlaceholder ?? "" let currentPlaceholder = updatedPlaceholder ?? self.currentPlaceholder ?? ""
self.currentPlaceholder = currentPlaceholder self.currentPlaceholder = currentPlaceholder
let baseFontSize = max(minInputFontSize, interfaceState.fontSize.baseDisplaySize) let baseFontSize = max(minInputFontSize, interfaceState.fontSize.baseDisplaySize)
let attributedPlaceholder = NSMutableAttributedString(string: currentPlaceholder, font: Font.regular(baseFontSize), textColor: placeholderColor) let attributedPlaceholder = NSMutableAttributedString(string: currentPlaceholder, font: Font.regular(baseFontSize), textColor: placeholderColor.withAlphaComponent(1.0))
if placeholderHasStar, let range = attributedPlaceholder.string.range(of: "#") { if placeholderHasStar, let range = attributedPlaceholder.string.range(of: "#") {
attributedPlaceholder.addAttribute(.attachment, value: PresentationResourcesChat.chatPlaceholderStarIcon(interfaceState.theme)!, range: NSRange(range, in: attributedPlaceholder.string)) attributedPlaceholder.addAttribute(.attachment, value: PresentationResourcesChat.chatPlaceholderStarIcon(interfaceState.theme)!, range: NSRange(range, in: attributedPlaceholder.string))
attributedPlaceholder.addAttribute(.foregroundColor, value: placeholderColor, range: NSRange(range, in: attributedPlaceholder.string)) attributedPlaceholder.addAttribute(.foregroundColor, value: placeholderColor.withAlphaComponent(1.0), range: NSRange(range, in: attributedPlaceholder.string))
attributedPlaceholder.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedPlaceholder.string)) attributedPlaceholder.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedPlaceholder.string))
} }
let attributedTintMaskPlaceholder = NSMutableAttributedString(string: currentPlaceholder, font: Font.regular(baseFontSize), textColor: UIColor(white: 0.0, alpha: placeholderColor.alpha))
if placeholderHasStar, let range = attributedPlaceholder.string.range(of: "#") {
attributedTintMaskPlaceholder.addAttribute(.attachment, value: PresentationResourcesChat.chatPlaceholderStarIcon(interfaceState.theme)!, range: NSRange(range, in: attributedPlaceholder.string))
attributedTintMaskPlaceholder.addAttribute(.foregroundColor, value: UIColor.black, range: NSRange(range, in: attributedPlaceholder.string))
attributedTintMaskPlaceholder.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedPlaceholder.string))
}
self.textPlaceholderNode.attributedText = attributedPlaceholder self.textPlaceholderNode.attributedText = attributedPlaceholder
self.tintMaskTextPlaceholderNode.attributedText = attributedTintMaskPlaceholder self.textPlaceholderNode.view.setMonochromaticEffect(tintColor: placeholderColor)
self.textInputNode?.textView.accessibilityHint = currentPlaceholder self.textInputNode?.textView.accessibilityHint = currentPlaceholder
let placeholderSize = self.textPlaceholderNode.updateLayout(CGSize(width: textPlaceholderMaxWidth, height: CGFloat.greatestFiniteMagnitude)) let placeholderSize = self.textPlaceholderNode.updateLayout(CGSize(width: textPlaceholderMaxWidth, height: CGFloat.greatestFiniteMagnitude))
let _ = self.tintMaskTextPlaceholderNode.updateLayout(CGSize(width: textPlaceholderMaxWidth, height: CGFloat.greatestFiniteMagnitude))
if transition.isAnimated, let snapshotLayer = self.textPlaceholderNode.layer.snapshotContentTree() { if transition.isAnimated, let snapshotLayer = self.textPlaceholderNode.layer.snapshotContentTree() {
self.textPlaceholderNode.supernode?.layer.insertSublayer(snapshotLayer, above: self.textPlaceholderNode.layer) self.textPlaceholderNode.supernode?.layer.insertSublayer(snapshotLayer, above: self.textPlaceholderNode.layer)
@ -2580,15 +2533,6 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
self.textPlaceholderNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18) self.textPlaceholderNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
} }
let _ = self.tintMaskTextPlaceholderNode.updateLayout(CGSize(width: textPlaceholderMaxWidth, height: CGFloat.greatestFiniteMagnitude))
if transition.isAnimated, let snapshotLayer = self.tintMaskTextPlaceholderNode.layer.snapshotContentTree() {
self.tintMaskTextPlaceholderNode.supernode?.layer.insertSublayer(snapshotLayer, above: self.tintMaskTextPlaceholderNode.layer)
snapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.22, removeOnCompletion: false, completion: { [weak snapshotLayer] _ in
snapshotLayer?.removeFromSuperlayer()
})
self.tintMaskTextPlaceholderNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
}
textPlaceholderSize = placeholderSize textPlaceholderSize = placeholderSize
} else { } else {
textPlaceholderSize = self.textPlaceholderNode.bounds.size textPlaceholderSize = self.textPlaceholderNode.bounds.size
@ -2623,13 +2567,11 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
} }
} }
transition.updateFrame(node: self.textPlaceholderNode, frame: textPlaceholderFrame) transition.updateFrame(node: self.textPlaceholderNode, frame: textPlaceholderFrame)
transition.updateFrame(node: self.tintMaskTextPlaceholderNode, frame: textPlaceholderFrame)
let textPlaceholderAlpha: CGFloat = audioRecordingItemsAlpha let textPlaceholderAlpha: CGFloat = audioRecordingItemsAlpha * placeholderColor.alpha
transition.updateAlpha(node: self.textPlaceholderNode, alpha: textPlaceholderAlpha) transition.updateAlpha(node: self.textPlaceholderNode, alpha: textPlaceholderAlpha)
transition.updateAlpha(node: self.tintMaskTextPlaceholderNode, alpha: textPlaceholderAlpha)
if let removeAccessoryButtons = removeAccessoryButtons { if let removeAccessoryButtons {
for button in removeAccessoryButtons { for button in removeAccessoryButtons {
let buttonFrame = CGRect(origin: CGPoint(x: button.frame.origin.x + additionalOffset, y: textInputFrame.maxY - minimalInputHeight), size: button.frame.size) let buttonFrame = CGRect(origin: CGPoint(x: button.frame.origin.x + additionalOffset, y: textInputFrame.maxY - minimalInputHeight), size: button.frame.size)
transition.updateFrame(layer: button.layer, frame: buttonFrame) transition.updateFrame(layer: button.layer, frame: buttonFrame)
@ -2700,7 +2642,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
let attachmentButtonFrame = CGRect(origin: CGPoint(x: attachmentButtonX, y: textInputFrame.maxY - 40.0), size: CGSize(width: 40.0, height: 40.0)) let attachmentButtonFrame = CGRect(origin: CGPoint(x: attachmentButtonX, y: textInputFrame.maxY - 40.0), size: CGSize(width: 40.0, height: 40.0))
self.attachmentButtonBackground.frame = CGRect(origin: CGPoint(), size: attachmentButtonFrame.size) self.attachmentButtonBackground.frame = CGRect(origin: CGPoint(), size: attachmentButtonFrame.size)
self.attachmentButtonBackground.update(size: attachmentButtonFrame.size, cornerRadius: attachmentButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: isEditingMedia ? .init(kind: .custom, color: interfaceState.theme.chat.inputPanel.actionControlFillColor) : .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: ComponentTransition(transition)) self.attachmentButtonBackground.update(size: attachmentButtonFrame.size, cornerRadius: attachmentButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: isEditingMedia ? .init(kind: .custom, color: interfaceState.theme.chat.inputPanel.actionControlFillColor) : .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), isInteractive: true, transition: ComponentTransition(transition))
transition.updateFrame(layer: self.attachmentButton.layer, frame: attachmentButtonFrame) transition.updateFrame(layer: self.attachmentButton.layer, frame: attachmentButtonFrame)
transition.updateFrame(node: self.attachmentButtonDisabledNode, frame: self.attachmentButton.frame) transition.updateFrame(node: self.attachmentButtonDisabledNode, frame: self.attachmentButton.frame)
@ -3667,11 +3609,9 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
if let interfaceState = self.presentationInterfaceState { if let interfaceState = self.presentationInterfaceState {
if (interfaceState.slowmodeState != nil && !isScheduledMessages && interfaceState.editMessageState == nil) || interfaceState.inputTextPanelState.contextPlaceholder != nil { if (interfaceState.slowmodeState != nil && !isScheduledMessages && interfaceState.editMessageState == nil) || interfaceState.inputTextPanelState.contextPlaceholder != nil {
self.textPlaceholderNode.isHidden = true self.textPlaceholderNode.isHidden = true
self.tintMaskTextPlaceholderNode.isHidden = true
self.slowmodePlaceholderNode?.isHidden = inputHasText self.slowmodePlaceholderNode?.isHidden = inputHasText
} else { } else {
self.textPlaceholderNode.isHidden = inputHasText self.textPlaceholderNode.isHidden = inputHasText
self.tintMaskTextPlaceholderNode.isHidden = inputHasText
self.slowmodePlaceholderNode?.isHidden = true self.slowmodePlaceholderNode?.isHidden = true
} }
} }
@ -4721,7 +4661,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
public func frameForAccessoryButton(_ item: ChatTextInputAccessoryItem) -> CGRect? { public func frameForAccessoryButton(_ item: ChatTextInputAccessoryItem) -> CGRect? {
for (buttonItem, buttonNode) in self.accessoryItemButtons { for (buttonItem, buttonNode) in self.accessoryItemButtons {
if buttonItem == item { if buttonItem == item {
return buttonNode.frame return self.view.convert(buttonNode.bounds.insetBy(dx: 0.0, dy: 6.0), from: buttonNode)
} }
} }
return nil return nil
@ -4744,9 +4684,9 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
public func frameForInputActionButton() -> CGRect? { public func frameForInputActionButton() -> CGRect? {
if !self.actionButtons.alpha.isZero { if !self.actionButtons.alpha.isZero {
if self.actionButtons.micButton.alpha.isZero { if self.actionButtons.micButton.alpha.isZero {
return self.actionButtons.frame.insetBy(dx: 0.0, dy: 6.0).offsetBy(dx: 4.0, dy: 0.0) return self.actionButtons.frame.insetBy(dx: 0.0, dy: -4.0).offsetBy(dx: 0.0, dy: 0.0)
} else { } else {
return self.actionButtons.frame.insetBy(dx: 0.0, dy: 6.0).offsetBy(dx: 2.0, dy: 0.0) return self.actionButtons.frame.insetBy(dx: 0.0, dy: -4.0).offsetBy(dx: 0.0, dy: 0.0)
} }
} }
return nil return nil
@ -4755,7 +4695,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
public func frameForStickersButton() -> CGRect? { public func frameForStickersButton() -> CGRect? {
for (item, button) in self.accessoryItemButtons { for (item, button) in self.accessoryItemButtons {
if case let .input(_, inputMode) = item, case .stickers = inputMode { if case let .input(_, inputMode) = item, case .stickers = inputMode {
return button.frame.insetBy(dx: 0.0, dy: 6.0) return self.view.convert(button.bounds.insetBy(dx: 0.0, dy: 6.0), from: button)
} }
} }
return nil return nil
@ -4764,7 +4704,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
public func frameForEmojiButton() -> CGRect? { public func frameForEmojiButton() -> CGRect? {
for (item, button) in self.accessoryItemButtons { for (item, button) in self.accessoryItemButtons {
if case let .input(_, inputMode) = item, case .emoji = inputMode { if case let .input(_, inputMode) = item, case .emoji = inputMode {
return button.frame.insetBy(dx: 0.0, dy: 6.0) return self.view.convert(button.bounds.insetBy(dx: 0.0, dy: 6.0), from: button)
} }
} }
return nil return nil
@ -4773,7 +4713,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
public func frameForGiftButton() -> CGRect? { public func frameForGiftButton() -> CGRect? {
for (item, button) in self.accessoryItemButtons { for (item, button) in self.accessoryItemButtons {
if case .gift = item { if case .gift = item {
return button.frame.insetBy(dx: 0.0, dy: 6.0) return self.view.convert(button.bounds.insetBy(dx: 0.0, dy: 6.0), from: button)
} }
} }
return nil return nil

View File

@ -420,11 +420,12 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM
animationName = "anim_micToVideo" animationName = "anim_micToVideo"
} }
let animationTintColor = self.useDarkTheme ? .white : self.theme.chat.inputPanel.inputControlColor
let _ = self.animationView.update( let _ = self.animationView.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(LottieComponent( component: AnyComponent(LottieComponent(
content: LottieComponent.AppBundleContent(name: animationName), content: LottieComponent.AppBundleContent(name: animationName),
color: self.useDarkTheme ? .white : self.theme.chat.inputPanel.inputControlColor color: animationTintColor
)), )),
environment: {}, environment: {},
containerSize: animationFrame.size containerSize: animationFrame.size
@ -437,6 +438,7 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM
view.output = self.animationOutput view.output = self.animationOutput
self.updateShadow() self.updateShadow()
} }
view.setMonochromaticEffect(tintColor: animationTintColor)
view.frame = animationFrame view.frame = animationFrame
if previousMode != mode { if previousMode != mode {

View File

@ -65,22 +65,12 @@ final class LockView: UIButton, TGModernConversationInputMicButtonLock {
} }
func updateTheme(_ theme: PresentationTheme) { func updateTheme(_ theme: PresentationTheme) {
// [
// "Rectangle.Заливка 1": theme.chat.inputPanel.panelBackgroundColor,
// "Rectangle.Rectangle.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
// "Rectangle 2.Rectangle.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
// "Path.Path.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
// "Path 4.Path 4.Обводка 1": theme.chat.inputPanel.panelControlAccentColor
// ].forEach { key, value in
// idleView.setValueProvider(ColorValueProvider(value.lottieColorValue), keypath: AnimationKeypath(keypath: "\(key).Color"))
// }
//
for keypath in idleView.allKeypaths(predicate: { $0.keys.last == "Color" }) { for keypath in idleView.allKeypaths(predicate: { $0.keys.last == "Color" }) {
idleView.setValueProvider(ColorValueProvider(theme.chat.inputPanel.panelControlAccentColor.lottieColorValue), keypath: AnimationKeypath(keypath: keypath)) idleView.setValueProvider(ColorValueProvider(theme.chat.inputPanel.panelControlColor.lottieColorValue), keypath: AnimationKeypath(keypath: keypath))
} }
for keypath in lockingView.allKeypaths(predicate: { $0.keys.last == "Color" }) { for keypath in lockingView.allKeypaths(predicate: { $0.keys.last == "Color" }) {
lockingView.setValueProvider(ColorValueProvider(theme.chat.inputPanel.panelControlAccentColor.lottieColorValue), keypath: AnimationKeypath(keypath: keypath)) lockingView.setValueProvider(ColorValueProvider(theme.chat.inputPanel.panelControlColor.lottieColorValue), keypath: AnimationKeypath(keypath: keypath))
} }
// //
// [ // [

View File

@ -202,6 +202,14 @@ public class GlassBackgroundView: UIView {
} }
} }
override public var tintColor: UIColor? {
didSet {
if self.tintColor != oldValue {
self.setMonochromaticEffect(tintColor: self.tintColor)
}
}
}
override public init(frame: CGRect) { override public init(frame: CGRect) {
self.tintImageView = UIImageView() self.tintImageView = UIImageView()
@ -252,16 +260,19 @@ public class GlassBackgroundView: UIView {
let cornerRadius: CGFloat let cornerRadius: CGFloat
let isDark: Bool let isDark: Bool
let tintColor: TintColor let tintColor: TintColor
let isInteractive: Bool
init(cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor) { init(cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor, isInteractive: Bool) {
self.cornerRadius = cornerRadius self.cornerRadius = cornerRadius
self.isDark = isDark self.isDark = isDark
self.tintColor = tintColor self.tintColor = tintColor
self.isInteractive = isInteractive
} }
} }
private let backgroundNode: NavigationBackgroundNode? private let backgroundNode: NavigationBackgroundNode?
private let nativeView: UIVisualEffectView? private let nativeView: UIVisualEffectView?
private let nativeContainerView: UIVisualEffectView?
private let foregroundView: UIImageView? private let foregroundView: UIImageView?
private let shadowView: UIImageView? private let shadowView: UIImageView?
@ -283,19 +294,23 @@ public class GlassBackgroundView: UIView {
public override init(frame: CGRect) { public override init(frame: CGRect) {
if #available(iOS 26.0, *) { if #available(iOS 26.0, *) {
self.backgroundNode = nil self.backgroundNode = nil
let glassEffect = UIGlassEffect(style: .clear)
let glassEffect = UIGlassEffect(style: .regular)
glassEffect.isInteractive = false glassEffect.isInteractive = false
let nativeView = UIVisualEffectView(effect: glassEffect) let nativeView = UIVisualEffectView(effect: glassEffect)
nativeView.layer.cornerCurve = .circular
self.nativeView = nativeView self.nativeView = nativeView
nativeView.overrideUserInterfaceStyle = .light
nativeView.traitOverrides.userInterfaceStyle = .light let glassContainerEffect = UIGlassContainerEffect()
//self.foregroundView = UIImageView() let nativeContainerView = UIVisualEffectView(effect: glassContainerEffect)
self.nativeContainerView = nativeContainerView
nativeContainerView.contentView.addSubview(nativeView)
self.foregroundView = nil self.foregroundView = nil
self.shadowView = UIImageView() self.shadowView = nil
} else { } else {
self.backgroundNode = NavigationBackgroundNode(color: .black, enableBlur: true, customBlurRadius: 5.0) self.backgroundNode = NavigationBackgroundNode(color: .black, enableBlur: true, customBlurRadius: 5.0)
self.nativeView = nil self.nativeView = nil
self.nativeContainerView = nil
self.foregroundView = UIImageView() self.foregroundView = UIImageView()
self.shadowView = UIImageView() self.shadowView = UIImageView()
} }
@ -316,8 +331,8 @@ public class GlassBackgroundView: UIView {
if let shadowView = self.shadowView { if let shadowView = self.shadowView {
self.addSubview(shadowView) self.addSubview(shadowView)
} }
if let nativeView = self.nativeView { if let nativeContainerView = self.nativeContainerView {
self.addSubview(nativeView) self.addSubview(nativeContainerView)
} }
if let backgroundNode = self.backgroundNode { if let backgroundNode = self.backgroundNode {
self.addSubview(backgroundNode.view) self.addSubview(backgroundNode.view)
@ -333,8 +348,17 @@ public class GlassBackgroundView: UIView {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
public func update(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor, transition: ComponentTransition) { override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let nativeView = self.nativeView { /*if let nativeContainerView = self.nativeContainerView {
if let result = nativeContainerView.hitTest(self.convert(point, to: nativeContainerView), with: event) {
return result
}
}*/
return nil
}
public func update(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, transition: ComponentTransition) {
if let nativeContainerView = self.nativeContainerView, let nativeView = self.nativeView {
let previousFrame = nativeView.frame let previousFrame = nativeView.frame
if transition.animation.isImmediate { if transition.animation.isImmediate {
@ -347,6 +371,8 @@ public class GlassBackgroundView: UIView {
} }
nativeView.layer.animateFrame(from: previousFrame, to: CGRect(origin: CGPoint(), size: size), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring) nativeView.layer.animateFrame(from: previousFrame, to: CGRect(origin: CGPoint(), size: size), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
} }
transition.setFrame(view: nativeContainerView, frame: CGRect(origin: CGPoint(), size: size))
} }
if let backgroundNode = self.backgroundNode { if let backgroundNode = self.backgroundNode {
backgroundNode.updateColor(color: .clear, forceKeepBlur: tintColor.color.alpha != 1.0, transition: transition.containedViewLayoutTransition) backgroundNode.updateColor(color: .clear, forceKeepBlur: tintColor.color.alpha != 1.0, transition: transition.containedViewLayoutTransition)
@ -356,7 +382,7 @@ public class GlassBackgroundView: UIView {
let shadowInset: CGFloat = 32.0 let shadowInset: CGFloat = 32.0
let params = Params(cornerRadius: cornerRadius, isDark: isDark, tintColor: tintColor) let params = Params(cornerRadius: cornerRadius, isDark: isDark, tintColor: tintColor, isInteractive: isInteractive)
if self.params != params { if self.params != params {
self.params = params self.params = params
@ -378,18 +404,21 @@ public class GlassBackgroundView: UIView {
if let foregroundView = self.foregroundView { if let foregroundView = self.foregroundView {
foregroundView.image = GlassBackgroundView.generateLegacyGlassImage(size: CGSize(width: cornerRadius * 2.0, height: cornerRadius * 2.0), inset: shadowInset, isDark: isDark, fillColor: tintColor.color) foregroundView.image = GlassBackgroundView.generateLegacyGlassImage(size: CGSize(width: cornerRadius * 2.0, height: cornerRadius * 2.0), inset: shadowInset, isDark: isDark, fillColor: tintColor.color)
} else { } else {
if let nativeView { if let nativeContainerView = self.nativeContainerView, let nativeView {
if #available(iOS 26.0, *) { if #available(iOS 26.0, *) {
let glassEffect = UIGlassEffect(style: .clear) let glassEffect = UIGlassEffect(style: .regular)
switch tintColor.kind { switch tintColor.kind {
case .panel: case .panel:
glassEffect.tintColor = tintColor.color.withMultipliedAlpha(1.2) glassEffect.tintColor = nil
case .custom: case .custom:
glassEffect.tintColor = tintColor.color glassEffect.tintColor = tintColor.color
} }
glassEffect.isInteractive = false glassEffect.isInteractive = params.isInteractive
nativeView.effect = glassEffect nativeView.effect = glassEffect
let _ = nativeContainerView
//nativeContainerView.overrideUserInterfaceStyle = .light// isDark ? .dark : .light
self.overrideUserInterfaceStyle = isDark ? .dark : .light
} }
} }
} }
@ -407,6 +436,27 @@ public class GlassBackgroundView: UIView {
} }
} }
public final class GlassBackgroundContainerView: UIView {
private final class ContentView: UIView {
}
private let contentViewImpl: ContentView
public var contentView: UIView {
return self.contentViewImpl
}
public override init(frame: CGRect) {
self.contentViewImpl = ContentView()
super.init(frame: frame)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
public final class VariableBlurView: UIVisualEffectView { public final class VariableBlurView: UIVisualEffectView {
public let maxBlurRadius: CGFloat public let maxBlurRadius: CGFloat
@ -536,8 +586,7 @@ public extension GlassBackgroundView {
return result return result
} }
// Your requested closure: let addShadow: (Bool, CGPoint, CGFloat, CGFloat, UIColor, Bool) -> Void = { isOuter, position, blur, spread, shadowColor, isMultiply in
let addShadow: (Bool, CGPoint, CGFloat, CGFloat, UIColor) -> Void = { isOuter, position, blur, spread, shadowColor in
var blur = blur var blur = blur
blur += abs(spread) blur += abs(spread)
@ -571,132 +620,70 @@ public extension GlassBackgroundView {
context.fillPath() context.fillPath()
context.setBlendMode(.normal) context.setBlendMode(.normal)
} else { } else {
context.beginTransparencyLayer(auxiliaryInfo: nil) if let image = generateImage(size, rotatedContext: { size, context in
context.saveGState() context.clear(CGRect(origin: CGPoint(), size: size))
defer { let spreadRect = CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize).insetBy(dx: -0.25, dy: -0.25)
context.restoreGState() let spreadPath = UIBezierPath(
context.endTransparencyLayer() roundedRect: spreadRect,
cornerRadius: min(spreadRect.width, spreadRect.height) * 0.5
).cgPath
context.setShadow(offset: CGSize(width: position.x, height: position.y), blur: blur, color: shadowColor.cgColor)
context.setFillColor(UIColor.black.withAlphaComponent(1.0).cgColor)
let enclosingRect = spreadRect.insetBy(dx: -10000.0, dy: -10000.0)
context.addPath(UIBezierPath(rect: enclosingRect).cgPath)
context.addPath(spreadPath)
context.fillPath(using: .evenOdd)
let cleanRect = CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize)
let cleanPath = UIBezierPath(
roundedRect: cleanRect,
cornerRadius: min(cleanRect.width, cleanRect.height) * 0.5
).cgPath
context.setBlendMode(.copy)
context.setFillColor(UIColor.clear.cgColor)
context.addPath(UIBezierPath(rect: enclosingRect).cgPath)
context.addPath(cleanPath)
context.fillPath(using: .evenOdd)
context.setBlendMode(.normal)
}) {
UIGraphicsPushContext(context)
image.draw(in: CGRect(origin: .zero, size: size), blendMode: isMultiply ? .destinationOut : .normal, alpha: 1.0)
UIGraphicsPopContext()
} }
let spreadRect = CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize).insetBy(dx: -0.25, dy: -0.25)
let spreadPath = UIBezierPath(
roundedRect: spreadRect,
cornerRadius: min(spreadRect.width, spreadRect.height) * 0.5
).cgPath
context.setShadow(offset: CGSize(width: position.x, height: position.y), blur: blur, color: shadowColor.cgColor)
context.setFillColor(UIColor.black.withAlphaComponent(1.0).cgColor)
let enclosingRect = spreadRect.insetBy(dx: -10000.0, dy: -10000.0)
context.addPath(UIBezierPath(rect: enclosingRect).cgPath)
context.addPath(spreadPath)
context.fillPath(using: .evenOdd)
let cleanRect = CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize)
let cleanPath = UIBezierPath(
roundedRect: cleanRect,
cornerRadius: min(cleanRect.width, cleanRect.height) * 0.5
).cgPath
context.setBlendMode(.copy)
context.setFillColor(UIColor.clear.cgColor)
context.addPath(UIBezierPath(rect: enclosingRect).cgPath)
context.addPath(cleanPath)
context.fillPath(using: .evenOdd)
context.setBlendMode(.normal)
} }
} }
if isDark { if isDark {
addShadow(true, CGPoint(), 16.0, 0.0, UIColor(white: 0.0, alpha: 0.12)) addShadow(true, CGPoint(), 16.0, 0.0, UIColor(white: 0.0, alpha: 0.12), false)
addShadow(true, CGPoint(), 8.0, 0.0, UIColor(white: 0.0, alpha: 0.1)) addShadow(true, CGPoint(), 8.0, 0.0, UIColor(white: 0.0, alpha: 0.1), false)
context.setFillColor(fillColor.cgColor) context.setFillColor(fillColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset)) context.fillEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset))
addShadow(false, CGPoint(x: 0.0, y: 0.0), 3.0, 0.0, UIColor(white: 1.0, alpha: 0.5)) addShadow(false, CGPoint(x: 0.0, y: 0.0), 3.0, 0.0, UIColor(white: 1.0, alpha: 0.5), false)
addShadow(false, CGPoint(x: 3.0, y: -3.0), 2.0, 0.0, UIColor(white: 1.0, alpha: 0.25)) addShadow(false, CGPoint(x: 3.0, y: -3.0), 2.0, 0.0, UIColor(white: 1.0, alpha: 0.25), false)
addShadow(false, CGPoint(x: -3.0, y: 3.0), 2.0, 0.0, UIColor(white: 1.0, alpha: 0.25)) addShadow(false, CGPoint(x: -3.0, y: 3.0), 2.0, 0.0, UIColor(white: 1.0, alpha: 0.25), false)
} else { } else {
addShadow(true, CGPoint(), 16.0, 0.0, UIColor(white: 0.0, alpha: 0.08)) addShadow(true, CGPoint(), 32.0, 0.0, UIColor(white: 0.0, alpha: 0.08), false)
addShadow(true, CGPoint(), 8.0, 0.0, UIColor(white: 0.0, alpha: 0.08)) addShadow(true, CGPoint(), 16.0, 0.0, UIColor(white: 0.0, alpha: 0.08), false)
context.setFillColor(fillColor.cgColor) context.setFillColor(fillColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset)) context.fillEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset))
addShadow(false, CGPoint(x: 3.0, y: -3.0), 0.5, 0.0, fillColor.withMultiplied(hue: 1.0, saturation: 2.0, brightness: 1.0).adjustedPerceivedBrightness(3.0).withMultipliedAlpha(1.0)) let highlightColor: UIColor
addShadow(false, CGPoint(x: -2.0, y: 2.0), 0.5, 0.0, UIColor.black.withMultipliedAlpha(0.15)) if fillColor.hsb.s > 0.5 {
} highlightColor = fillColor.withMultiplied(hue: 1.0, saturation: 2.0, brightness: 1.0).adjustedPerceivedBrightness(2.0)
if "".isEmpty { let shadowColor = fillColor.withMultiplied(hue: 1.0, saturation: 2.0, brightness: 1.0).adjustedPerceivedBrightness(0.5).withMultipliedAlpha(0.2)
return addShadow(false, CGPoint(x: -2.0, y: 2.0), 0.5, 0.0, shadowColor, false)
} } else {
highlightColor = UIColor(white: 1.0, alpha: 0.4)
let maxColor = UIColor(white: 1.0, alpha: isDark ? 0.25 : 0.9) addShadow(false, CGPoint(x: -2.0, y: 2.0), 0.5, 0.0, UIColor.black.withMultipliedAlpha(0.15), true)
let minColor = UIColor(white: 1.0, alpha: 0.0) addShadow(false, CGPoint(x: -2.0, y: 2.0), 0.6, 0.0, UIColor(white: 0.0, alpha: 0.1), false)
}
context.setFillColor(fillColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
let lineWidth: CGFloat = isDark ? 0.66 : 0.66
context.saveGState()
let darkShadeColor = UIColor(white: isDark ? 1.0 : 0.0, alpha: 0.035)
let lightShadeColor = UIColor(white: isDark ? 0.0 : 1.0, alpha: 0.035)
let innerShadowBlur: CGFloat = 24.0
context.resetClip()
context.addEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5))
context.clip()
context.addRect(CGRect(origin: CGPoint(), size: size).insetBy(dx: -100.0, dy: -100.0))
context.addEllipse(in: CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.black.cgColor)
context.setShadow(offset: CGSize(width: 10.0, height: -10.0), blur: innerShadowBlur, color: darkShadeColor.cgColor)
context.fillPath(using: .evenOdd)
context.resetClip()
context.addEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5))
context.clip()
context.addRect(CGRect(origin: CGPoint(), size: size).insetBy(dx: -100.0, dy: -100.0))
context.addEllipse(in: CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.black.cgColor)
context.setShadow(offset: CGSize(width: -10.0, height: 10.0), blur: innerShadowBlur, color: lightShadeColor.cgColor)
context.fillPath(using: .evenOdd)
context.restoreGState()
context.setLineWidth(lineWidth)
context.addRect(CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width * 0.5, height: size.height)))
context.clip()
context.addEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5))
context.replacePathWithStrokedPath()
context.clip()
do {
var locations: [CGFloat] = [0.0, 0.5, 0.5 + 0.2, 1.0 - 0.1, 1.0]
let colors: [CGColor] = [maxColor.cgColor, maxColor.cgColor, minColor.cgColor, minColor.cgColor, maxColor.cgColor]
let colorSpace = CGColorSpaceCreateDeviceRGB() addShadow(false, CGPoint(x: 2.0, y: -2.0), 0.5, 0.0, highlightColor, false)
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
}
context.resetClip()
context.addRect(CGRect(origin: CGPoint(x: size.width - size.width * 0.5, y: 0.0), size: CGSize(width: size.width * 0.5, height: size.height)))
context.clip()
context.addEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5))
context.replacePathWithStrokedPath()
context.clip()
do {
var locations: [CGFloat] = [0.0, 0.1, 0.5 - 0.2, 0.5, 1.0]
let colors: [CGColor] = [maxColor.cgColor, minColor.cgColor, minColor.cgColor, maxColor.cgColor, maxColor.cgColor]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
} }
})!.stretchableImage(withLeftCapWidth: Int(size.width * 0.5), topCapHeight: Int(size.height * 0.5)) })!.stretchableImage(withLeftCapWidth: Int(size.width * 0.5), topCapHeight: Int(size.height * 0.5))
} }
@ -784,10 +771,12 @@ public extension GlassBackgroundView {
public final class GlassBackgroundComponent: Component { public final class GlassBackgroundComponent: Component {
private let size: CGSize private let size: CGSize
private let isDark: Bool
private let tintColor: GlassBackgroundView.TintColor private let tintColor: GlassBackgroundView.TintColor
public init(size: CGSize, tintColor: GlassBackgroundView.TintColor) { public init(size: CGSize, isDark: Bool, tintColor: GlassBackgroundView.TintColor) {
self.size = size self.size = size
self.isDark = isDark
self.tintColor = tintColor self.tintColor = tintColor
} }
@ -795,6 +784,9 @@ public final class GlassBackgroundComponent: Component {
if lhs.size != rhs.size { if lhs.size != rhs.size {
return false return false
} }
if lhs.isDark != rhs.isDark {
return false
}
if lhs.tintColor != rhs.tintColor { if lhs.tintColor != rhs.tintColor {
return false return false
} }
@ -803,7 +795,7 @@ public final class GlassBackgroundComponent: Component {
public final class View: GlassBackgroundView { public final class View: GlassBackgroundView {
func update(component: GlassBackgroundComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize { func update(component: GlassBackgroundComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
self.update(size: component.size, cornerRadius: component.size.height / 2.0, isDark: true, tintColor: component.tintColor, transition: transition) self.update(size: component.size, cornerRadius: component.size.height / 2.0, isDark: component.isDark, tintColor: component.tintColor, transition: transition)
self.frame = CGRect(origin: .zero, size: component.size) self.frame = CGRect(origin: .zero, size: component.size)
return component.size return component.size

View File

@ -315,6 +315,8 @@ public final class TabBarComponent: Component {
self.component = component self.component = component
self.state = state self.state = state
self.overrideUserInterfaceStyle = component.theme.overallDarkAppearance ? .dark : .light
if let nativeTabBar = self.nativeTabBar { if let nativeTabBar = self.nativeTabBar {
if nativeTabBar.items?.count != component.items.count { if nativeTabBar.items?.count != component.items.count {
nativeTabBar.items = (0 ..< component.items.count).map { i in nativeTabBar.items = (0 ..< component.items.count).map { i in

View File

@ -191,7 +191,6 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent {
enum ImageKey: Hashable { enum ImageKey: Hashable {
case flip case flip
case flash case flash
case buttonBackground
case flashImage case flashImage
} }
private var cachedImages: [ImageKey: UIImage] = [:] private var cachedImages: [ImageKey: UIImage] = [:]
@ -205,9 +204,6 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent {
image = UIImage(bundleImageName: "Camera/VideoMessageFlip")!.withRenderingMode(.alwaysTemplate) image = UIImage(bundleImageName: "Camera/VideoMessageFlip")!.withRenderingMode(.alwaysTemplate)
case .flash: case .flash:
image = UIImage(bundleImageName: "Camera/VideoMessageFlash")!.withRenderingMode(.alwaysTemplate) image = UIImage(bundleImageName: "Camera/VideoMessageFlash")!.withRenderingMode(.alwaysTemplate)
case .buttonBackground:
let innerSize = CGSize(width: 40.0, height: 40.0)
image = generateFilledCircleImage(diameter: innerSize.width, color: theme.rootController.navigationBar.opaqueBackgroundColor, strokeColor: theme.chat.inputPanel.panelSeparatorColor, strokeWidth: 0.5, backgroundColor: nil)!
case .flashImage: case .flashImage:
image = generateImage(CGSize(width: 393.0, height: 852.0), rotatedContext: { size, context in image = generateImage(CGSize(width: 393.0, height: 852.0), rotatedContext: { size, context in
context.clear(CGRect(origin: .zero, size: size)) context.clear(CGRect(origin: .zero, size: size))
@ -590,6 +586,7 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent {
let flipButtonBackground = flipButtonBackground.update( let flipButtonBackground = flipButtonBackground.update(
component: GlassBackgroundComponent( component: GlassBackgroundComponent(
size: CGSize(width: 40.0, height: 40.0), size: CGSize(width: 40.0, height: 40.0),
isDark: environment.theme.overallDarkAppearance,
tintColor: .init(kind: .panel, color: environment.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)) tintColor: .init(kind: .panel, color: environment.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7))
), ),
availableSize: CGSize(width: 40.0, height: 40.0), availableSize: CGSize(width: 40.0, height: 40.0),
@ -691,6 +688,7 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent {
let flashButtonBackground = flashButtonBackground.update( let flashButtonBackground = flashButtonBackground.update(
component: GlassBackgroundComponent( component: GlassBackgroundComponent(
size: CGSize(width: 40.0, height: 40.0), size: CGSize(width: 40.0, height: 40.0),
isDark: environment.theme.overallDarkAppearance,
tintColor: .init(kind: .panel, color: environment.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)) tintColor: .init(kind: .panel, color: environment.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7))
), ),
availableSize: CGSize(width: 40.0, height: 40.0), availableSize: CGSize(width: 40.0, height: 40.0),
@ -719,9 +717,10 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent {
AnyComponentWithIdentity( AnyComponentWithIdentity(
id: "background", id: "background",
component: AnyComponent( component: AnyComponent(
Image( GlassBackgroundComponent(
image: state.image(.buttonBackground, theme: environment.theme), size: CGSize(width: 40.0, height: 40.0),
size: CGSize(width: 40.0, height: 40.0) isDark: environment.theme.overallDarkAppearance,
tintColor: .init(kind: .panel, color: environment.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7))
) )
) )
), ),
@ -749,7 +748,7 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent {
transition: context.transition transition: context.transition
) )
context.add(viewOnceButton context.add(viewOnceButton
.position(CGPoint(x: availableSize.width - viewOnceButton.size.width / 2.0 - 2.0 - UIScreenPixel, y: availableSize.height - viewOnceButton.size.height / 2.0 - 8.0 - viewOnceOffset)) .position(CGPoint(x: availableSize.width - viewOnceButton.size.width / 2.0 - 8.0, y: availableSize.height - viewOnceButton.size.height / 2.0 - 8.0 - viewOnceOffset))
.appear(.default(scale: true, alpha: true)) .appear(.default(scale: true, alpha: true))
.disappear(.default(scale: true, alpha: true)) .disappear(.default(scale: true, alpha: true))
) )
@ -764,6 +763,7 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent {
id: "background", id: "background",
component: AnyComponent(GlassBackgroundComponent( component: AnyComponent(GlassBackgroundComponent(
size: CGSize(width: 40.0, height: 40.0), size: CGSize(width: 40.0, height: 40.0),
isDark: environment.theme.overallDarkAppearance,
tintColor: .init(kind: .panel, color: environment.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)) tintColor: .init(kind: .panel, color: environment.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7))
)) ))
), ),
@ -1466,7 +1466,7 @@ public class VideoMessageCameraScreen: ViewController {
var backgroundFrame = CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: controller.inputPanelFrame.0.minY)) var backgroundFrame = CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: controller.inputPanelFrame.0.minY))
let actualBackgroundFrame = CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: layout.size.height)) let actualBackgroundFrame = CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: layout.size.height))
if backgroundFrame.maxY < layout.size.height - 100.0 && (layout.inputHeight ?? 0.0).isZero && !controller.inputPanelFrame.1 && layout.additionalInsets.bottom.isZero { if backgroundFrame.maxY < layout.size.height - 100.0 && (layout.inputHeight ?? 0.0).isZero && !controller.inputPanelFrame.1 && layout.additionalInsets.bottom.isZero {
backgroundFrame = CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: layout.size.height - layout.intrinsicInsets.bottom - controller.inputPanelFrame.0.height)) backgroundFrame = CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: layout.size.height - layout.intrinsicInsets.bottom - controller.inputPanelFrame.0.height - 8.0))
} }
transition.setPosition(view: self.backgroundView, position: actualBackgroundFrame.center) transition.setPosition(view: self.backgroundView, position: actualBackgroundFrame.center)

View File

@ -330,12 +330,6 @@ extension ChatControllerImpl {
self?.canReadHistory.set(true) self?.canReadHistory.set(true)
} }
controller.immediateItemsTransitionAnimation = disableTransitionAnimations controller.immediateItemsTransitionAnimation = disableTransitionAnimations
controller.getOverlayViews = { [weak self] in
guard let self else {
return []
}
return [self.chatDisplayNode.navigateButtons.view]
}
self.currentContextController = controller self.currentContextController = controller
controller.premiumReactionsSelected = { [weak self, weak controller] in controller.premiumReactionsSelected = { [weak self, weak controller] in

View File

@ -1311,6 +1311,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
previousInputPanelOrigin.y -= inputPanelNode.bounds.size.height previousInputPanelOrigin.y -= inputPanelNode.bounds.size.height
} }
if let secondaryInputPanelNode = self.secondaryInputPanelNode { if let secondaryInputPanelNode = self.secondaryInputPanelNode {
previousInputPanelOrigin.y -= 8.0
previousInputPanelOrigin.y -= secondaryInputPanelNode.bounds.size.height previousInputPanelOrigin.y -= secondaryInputPanelNode.bounds.size.height
} }
self.containerLayoutAndNavigationBarHeight = (layout, navigationBarHeight) self.containerLayoutAndNavigationBarHeight = (layout, navigationBarHeight)
@ -1568,6 +1569,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
inputPanelNodeBaseHeight += inputPanelNode.minimalHeight(interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics) inputPanelNodeBaseHeight += inputPanelNode.minimalHeight(interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
} }
if let secondaryInputPanelNode = self.secondaryInputPanelNode { if let secondaryInputPanelNode = self.secondaryInputPanelNode {
inputPanelNodeBaseHeight += 8.0
inputPanelNodeBaseHeight += secondaryInputPanelNode.minimalHeight(interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics) inputPanelNodeBaseHeight += secondaryInputPanelNode.minimalHeight(interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
} }
@ -1739,6 +1741,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
} }
} }
if let secondaryInputPanelSize = secondaryInputPanelSize { if let secondaryInputPanelSize = secondaryInputPanelSize {
maximumInputNodeHeight -= 8.0
maximumInputNodeHeight -= secondaryInputPanelSize.height maximumInputNodeHeight -= secondaryInputPanelSize.height
} }
if let accessoryPanelSize = accessoryPanelSize { if let accessoryPanelSize = accessoryPanelSize {
@ -2128,8 +2131,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
if overlayContextPanelNode !== self.overlayContextPanelNode { if overlayContextPanelNode !== self.overlayContextPanelNode {
dismissedOverlayContextPanelNode = self.overlayContextPanelNode dismissedOverlayContextPanelNode = self.overlayContextPanelNode
self.overlayContextPanelNode = overlayContextPanelNode self.overlayContextPanelNode = overlayContextPanelNode
if let navigationBar = self.navigationBar {
self.contentContainerNode.contentNode.addSubnode(overlayContextPanelNode) self.contentContainerNode.contentNode.insertSubnode(overlayContextPanelNode, belowSubnode: navigationBar)
} else {
self.contentContainerNode.contentNode.addSubnode(overlayContextPanelNode)
}
immediatelyLayoutOverlayContextPanelAndAnimateAppearance = true immediatelyLayoutOverlayContextPanelAndAnimateAppearance = true
} }
} else if let overlayContextPanelNode = self.overlayContextPanelNode { } else if let overlayContextPanelNode = self.overlayContextPanelNode {
@ -2147,10 +2153,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
var inputPanelHideOffset: CGFloat = 0.0 var inputPanelHideOffset: CGFloat = 0.0
if let inputNode = self.inputNode, inputNode.hideInput { if let inputNode = self.inputNode, inputNode.hideInput {
if let inputPanelSize = inputPanelSize { if let inputPanelSize = inputPanelSize {
inputPanelHideOffset += -inputPanelSize.height inputPanelHideOffset += -inputPanelSize.height - 80.0
} }
if let accessoryPanelSize = accessoryPanelSize { if let accessoryPanelSize = accessoryPanelSize {
inputPanelHideOffset += -accessoryPanelSize.height inputPanelHideOffset += -accessoryPanelSize.height - 80.0
} }
} }
@ -2164,11 +2170,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
} }
if self.secondaryInputPanelNode != nil { if self.secondaryInputPanelNode != nil {
secondaryInputPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - bottomOverflowOffset - inputPanelsHeight - secondaryInputPanelSize!.height), size: CGSize(width: layout.size.width, height: secondaryInputPanelSize!.height)) secondaryInputPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - bottomOverflowOffset - inputPanelsHeight - secondaryInputPanelSize!.height - 8.0), size: CGSize(width: layout.size.width, height: secondaryInputPanelSize!.height))
if self.dismissedAsOverlay { if self.dismissedAsOverlay {
secondaryInputPanelFrame!.origin.y = layout.size.height secondaryInputPanelFrame!.origin.y = layout.size.height
} }
inputPanelsHeight += secondaryInputPanelSize!.height inputPanelsHeight += 8.0 + secondaryInputPanelSize!.height
} }
var accessoryPanelFrame: CGRect? var accessoryPanelFrame: CGRect?

View File

@ -161,6 +161,7 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
let subtitleSize = self.subtitleNode.updateLayout(CGSize(width: width - leftInset - rightInset - 8.0 * 2.0, height: panelHeight)) let subtitleSize = self.subtitleNode.updateLayout(CGSize(width: width - leftInset - rightInset - 8.0 * 2.0, height: panelHeight))
var originX: CGFloat = leftInset + floor((width - leftInset - rightInset - textSize.width) / 2.0) var originX: CGFloat = leftInset + floor((width - leftInset - rightInset - textSize.width) / 2.0)
var totalWidth = textSize.width
if let iconImage { if let iconImage {
let iconView: UIImageView let iconView: UIImageView
@ -173,7 +174,7 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
} }
iconView.image = iconImage iconView.image = iconImage
let totalWidth = textSize.width + iconImage.size.width + iconSpacing totalWidth += iconImage.size.width + iconSpacing
iconView.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - totalWidth) / 2.0), y: floor((panelHeight - textSize.height) / 2.0) + UIScreenPixel + floorToScreenPixels((textSize.height - iconImage.size.height) / 2.0)), size: iconImage.size) iconView.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - totalWidth) / 2.0), y: floor((panelHeight - textSize.height) / 2.0) + UIScreenPixel + floorToScreenPixels((textSize.height - iconImage.size.height) / 2.0)), size: iconImage.size)
originX += iconImage.size.width + iconSpacing originX += iconImage.size.width + iconSpacing
@ -190,7 +191,11 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
let subtitleFrame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - subtitleSize.width) / 2.0), y: floor((panelHeight + combinedHeight) / 2.0) - subtitleSize.height), size: subtitleSize) let subtitleFrame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - subtitleSize.width) / 2.0), y: floor((panelHeight + combinedHeight) / 2.0) - subtitleSize.height), size: subtitleSize)
var combinedFrame = textFrame.union(subtitleFrame).insetBy(dx: -12.0, dy: -6.0) var combinedFrame = textFrame.union(subtitleFrame)
if let iconView {
combinedFrame = combinedFrame.union(iconView.frame)
}
combinedFrame = combinedFrame.insetBy(dx: -12.0, dy: -6.0)
combinedFrame.origin.y += 1.0 combinedFrame.origin.y += 1.0
self.textNode.frame = textFrame.offsetBy(dx: -combinedFrame.minX, dy: -combinedFrame.minY) self.textNode.frame = textFrame.offsetBy(dx: -combinedFrame.minX, dy: -combinedFrame.minY)

View File

@ -243,7 +243,8 @@ private struct CommandChatInputContextPanelEntry: Comparable, Identifiable {
messageCount: shortcut.totalCount, messageCount: shortcut.totalCount,
hideSeparator: false, hideSeparator: false,
hideDate: true, hideDate: true,
hidePeerStatus: true hidePeerStatus: true,
isInTransparentContainer: true
) )
)), )),
editing: false, editing: false,

View File

@ -84,7 +84,6 @@ private func preparedTransition(from fromEntries: [HorizontalListContextResultsC
final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputContextPanelNode { final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputContextPanelNode {
private let listView: ListView private let listView: ListView
private let separatorNode: ASDisplayNode
private var currentExternalResults: ChatContextResultCollection? private var currentExternalResults: ChatContextResultCollection?
private var currentProcessedResults: ChatContextResultCollection? private var currentProcessedResults: ChatContextResultCollection?
private var currentEntries: [HorizontalListContextResultsChatInputContextPanelEntry]? private var currentEntries: [HorizontalListContextResultsChatInputContextPanelEntry]?
@ -97,11 +96,6 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
private let batchVideoContext: QueueLocalObject<BatchVideoRenderingContext> private let batchVideoContext: QueueLocalObject<BatchVideoRenderingContext>
override init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, fontSize: PresentationFontSize, chatPresentationContext: ChatPresentationContext) { override init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, fontSize: PresentationFontSize, chatPresentationContext: ChatPresentationContext) {
self.separatorNode = ASDisplayNode()
self.separatorNode.isLayerBacked = true
self.separatorNode.backgroundColor = theme.list.itemPlainSeparatorColor
self.separatorNode.isHidden = true
self.listView = ListView() self.listView = ListView()
self.listView.isOpaque = true self.listView.isOpaque = true
self.listView.backgroundColor = theme.list.plainBackgroundColor self.listView.backgroundColor = theme.list.plainBackgroundColor
@ -121,7 +115,6 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
self.clipsToBounds = true self.clipsToBounds = true
self.addSubnode(self.listView) self.addSubnode(self.listView)
self.addSubnode(self.separatorNode)
self.listView.displayedItemRangeChanged = { [weak self] displayedRange, opaqueTransactionState in self.listView.displayedItemRangeChanged = { [weak self] displayedRange, opaqueTransactionState in
if let strongSelf = self, let state = opaqueTransactionState as? HorizontalListContextResultsOpaqueState { if let strongSelf = self, let state = opaqueTransactionState as? HorizontalListContextResultsOpaqueState {
@ -358,18 +351,10 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: HorizontalListContextResultsOpaqueState(entryCount: transition.entryCount, hasMore: transition.hasMore), completion: { [weak self] _ in self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: HorizontalListContextResultsOpaqueState(entryCount: transition.entryCount, hasMore: transition.hasMore), completion: { [weak self] _ in
if let strongSelf = self, firstTime { if let strongSelf = self, firstTime {
let position = strongSelf.listView.position
let separatorPosition = strongSelf.separatorNode.layer.position
strongSelf.listView.isHidden = false strongSelf.listView.isHidden = false
strongSelf.separatorNode.isHidden = false
strongSelf.listView.position = CGPoint(x: position.x, y: position.y + strongSelf.listView.bounds.size.width) strongSelf.layer.allowsGroupOpacity = true
strongSelf.separatorNode.position = CGPoint(x: separatorPosition.x, y: separatorPosition.y + strongSelf.listView.bounds.size.width) strongSelf.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
ContainedViewLayoutTransition.animated(duration: 0.3, curve: .spring).animateView {
strongSelf.listView.position = position
strongSelf.separatorNode.position = separatorPosition
}
} }
}) })
} }
@ -378,7 +363,6 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) { override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) {
let listHeight: CGFloat = 105.0 let listHeight: CGFloat = 105.0
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height - bottomInset - 8.0 - listHeight), size: CGSize(width: size.width, height: UIScreenPixel)))
self.listView.bounds = CGRect(x: 0.0, y: 0.0, width: listHeight, height: size.width) self.listView.bounds = CGRect(x: 0.0, y: 0.0, width: listHeight, height: size.width)
//transition.updateFrame(node: self.listView, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)) //transition.updateFrame(node: self.listView, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
@ -403,18 +387,18 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
if self.theme !== interfaceState.theme { if self.theme !== interfaceState.theme {
self.theme = interfaceState.theme self.theme = interfaceState.theme
self.separatorNode.backgroundColor = theme.list.itemPlainSeparatorColor self.listView.backgroundColor = self.theme.list.plainBackgroundColor
self.listView.backgroundColor = theme.list.plainBackgroundColor
} }
} }
override func animateOut(completion: @escaping () -> Void) { override func animateOut(completion: @escaping () -> Void) {
let position = self.listView.layer.position /*let position = self.listView.layer.position
self.listView.layer.animatePosition(from: position, to: CGPoint(x: position.x, y: position.y + self.listView.bounds.size.width), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in self.listView.layer.animatePosition(from: position, to: CGPoint(x: position.x, y: position.y + self.listView.bounds.size.width), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
completion() completion()
})*/
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
completion()
}) })
let separatorPosition = self.separatorNode.layer.position
self.separatorNode.layer.animatePosition(from: separatorPosition, to: CGPoint(x: separatorPosition.x, y: separatorPosition.y + listView.bounds.size.width), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
} }
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

View File

@ -8,8 +8,13 @@ import SwiftSignalKit
import LocalizedPeerData import LocalizedPeerData
import ChatPresentationInterfaceState import ChatPresentationInterfaceState
import ChatInputPanelNode import ChatInputPanelNode
import ComponentFlow
import MultilineTextComponent
import GlassBackgroundComponent
final class SecretChatHandshakeStatusInputPanelNode: ChatInputPanelNode { final class SecretChatHandshakeStatusInputPanelNode: ChatInputPanelNode {
private let titleBackground: GlassBackgroundView
private let title = ComponentView<Empty>()
private let button: HighlightableButtonNode private let button: HighlightableButtonNode
private var statusDisposable: Disposable? private var statusDisposable: Disposable?
@ -22,9 +27,12 @@ final class SecretChatHandshakeStatusInputPanelNode: ChatInputPanelNode {
self.button.titleNode.maximumNumberOfLines = 2 self.button.titleNode.maximumNumberOfLines = 2
self.button.titleNode.truncationMode = .byTruncatingMiddle self.button.titleNode.truncationMode = .byTruncatingMiddle
self.titleBackground = GlassBackgroundView()
super.init() super.init()
self.addSubnode(self.button) self.addSubnode(self.button)
self.button.view.addSubview(self.titleBackground)
self.button.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: [.touchUpInside]) self.button.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: [.touchUpInside])
} }
@ -46,31 +54,48 @@ final class SecretChatHandshakeStatusInputPanelNode: ChatInputPanelNode {
} }
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, maxOverlayHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat { override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, maxOverlayHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat {
if self.presentationInterfaceState != interfaceState { self.presentationInterfaceState = interfaceState
self.presentationInterfaceState = interfaceState
var text: String?
if let renderedPeer = interfaceState.renderedPeer, let peer = renderedPeer.peer as? TelegramSecretChat, let userPeer = renderedPeer.peers[peer.regularPeerId] {
switch peer.embeddedState { if let renderedPeer = interfaceState.renderedPeer, let peer = renderedPeer.peer as? TelegramSecretChat, let userPeer = renderedPeer.peers[peer.regularPeerId] {
case .handshake: switch peer.embeddedState {
let text: String case .handshake:
switch peer.role { switch peer.role {
case .creator: case .creator:
text = interfaceState.strings.DialogList_AwaitingEncryption(EnginePeer(userPeer).compactDisplayTitle).string text = interfaceState.strings.DialogList_AwaitingEncryption(EnginePeer(userPeer).compactDisplayTitle).string
case .participant: case .participant:
text = interfaceState.strings.Conversation_EncryptionProcessing text = interfaceState.strings.Conversation_EncryptionProcessing
}
self.button.setAttributedTitle(NSAttributedString(string: text, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.primaryTextColor, paragraphAlignment: .center), for: [])
case .active, .terminated:
break
} }
case .active, .terminated:
break
} }
} }
let buttonSize = self.button.measure(CGSize(width: width - 10.0, height: 100.0)) let titleSize = self.title.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: text ?? " ", font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.primaryTextColor, paragraphAlignment: .center))
)),
environment: {},
containerSize: CGSize(width: width - 16.0 * 2.0, height: 100.0)
)
let panelHeight = defaultHeight(metrics: metrics) let panelHeight = defaultHeight(metrics: metrics)
self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) / 2.0)), size: buttonSize) let backgroundSize = CGSize(width: titleSize.width + 16.0 * 2.0, height: 40.0)
let backgroundFrame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - backgroundSize.width) * 0.5), y: floor((panelHeight - backgroundSize.height) / 2.0)), size: backgroundSize)
transition.updateFrame(node: self.button, frame: backgroundFrame)
transition.updateFrame(view: self.titleBackground, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
self.titleBackground.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: .immediate)
let titleFrame = CGRect(origin: CGPoint(x: floor((backgroundFrame.width - titleSize.width) * 0.5), y: floor((backgroundFrame.height - titleSize.height) * 0.5)), size: titleSize)
if let titleView = self.title.view {
if titleView.superview == nil {
titleView.setMonochromaticEffect(tintColor: interfaceState.theme.chat.inputPanel.primaryTextColor)
self.titleBackground.contentView.addSubview(titleView)
}
titleView.frame = titleFrame
}
return panelHeight return panelHeight
} }

View File

@ -84,17 +84,13 @@ final class VerticalListContextResultsChatInputPanelButtonItemNode: ListViewItem
private let buttonNode: HighlightTrackingButtonNode private let buttonNode: HighlightTrackingButtonNode
private let titleNode: TextNode private let titleNode: TextNode
private let topSeparatorNode: ASDisplayNode
private let separatorNode: ASDisplayNode private let separatorNode: ASDisplayNode
private var item: VerticalListContextResultsChatInputPanelButtonItem? private var item: VerticalListContextResultsChatInputPanelButtonItem?
init() { init() {
self.buttonNode = HighlightTrackingButtonNode() self.buttonNode = HighlightTrackingButtonNode()
self.topSeparatorNode = ASDisplayNode()
self.topSeparatorNode.isLayerBacked = true
self.separatorNode = ASDisplayNode() self.separatorNode = ASDisplayNode()
self.separatorNode.isLayerBacked = true self.separatorNode.isLayerBacked = true
@ -102,7 +98,6 @@ final class VerticalListContextResultsChatInputPanelButtonItemNode: ListViewItem
super.init(layerBacked: false, dynamicBounce: false) super.init(layerBacked: false, dynamicBounce: false)
self.addSubnode(self.topSeparatorNode)
self.addSubnode(self.separatorNode) self.addSubnode(self.separatorNode)
self.addSubnode(self.titleNode) self.addSubnode(self.titleNode)
@ -145,7 +140,7 @@ final class VerticalListContextResultsChatInputPanelButtonItemNode: ListViewItem
titleFont = Font.regular(17.0) titleFont = Font.regular(17.0)
} }
let titleString = NSAttributedString(string: item.title, font: titleFont, textColor: item.theme.list.itemAccentColor) let titleString = NSAttributedString(string: item.title, font: titleFont, textColor: item.theme.chat.inputPanel.panelControlColor)
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - 16.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - 16.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
@ -156,7 +151,6 @@ final class VerticalListContextResultsChatInputPanelButtonItemNode: ListViewItem
strongSelf.item = item strongSelf.item = item
strongSelf.separatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor strongSelf.separatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor
strongSelf.topSeparatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor
let titleOffsetY: CGFloat let titleOffsetY: CGFloat
switch item.style { switch item.style {
@ -172,7 +166,6 @@ final class VerticalListContextResultsChatInputPanelButtonItemNode: ListViewItem
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: floor((params.width - titleLayout.size.width) / 2.0), y: floor((nodeLayout.contentSize.height - titleLayout.size.height) / 2.0) + titleOffsetY), size: titleLayout.size) strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: floor((params.width - titleLayout.size.width) / 2.0), y: floor((nodeLayout.contentSize.height - titleLayout.size.height) / 2.0) + titleOffsetY), size: titleLayout.size)
strongSelf.topSeparatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: UIScreenPixel))
strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: nodeLayout.contentSize.height - UIScreenPixel), size: CGSize(width: params.width, height: UIScreenPixel)) strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: nodeLayout.contentSize.height - UIScreenPixel), size: CGSize(width: params.width, height: UIScreenPixel))
strongSelf.buttonNode.frame = CGRect(origin: CGPoint(), size: nodeLayout.contentSize) strongSelf.buttonNode.frame = CGRect(origin: CGPoint(), size: nodeLayout.contentSize)

View File

@ -4,6 +4,7 @@
double animationDurationFactorImpl(); double animationDurationFactorImpl();
CABasicAnimation * _Nonnull makeSpringAnimationImpl(NSString * _Nonnull keyPath); CABasicAnimation * _Nonnull makeSpringAnimationImpl(NSString * _Nonnull keyPath);
CABasicAnimation * _Nonnull make26SpringAnimationImpl(NSString * _Nonnull keyPath, double duration);
CABasicAnimation * _Nonnull makeSpringBounceAnimationImpl(NSString * _Nonnull keyPath, CGFloat initialVelocity, CGFloat damping); CABasicAnimation * _Nonnull makeSpringBounceAnimationImpl(NSString * _Nonnull keyPath, CGFloat initialVelocity, CGFloat damping);
CGFloat springAnimationValueAtImpl(CABasicAnimation * _Nonnull animation, CGFloat t); CGFloat springAnimationValueAtImpl(CABasicAnimation * _Nonnull animation, CGFloat t);
@ -36,3 +37,5 @@ void setLayerDisableScreenshots(CALayer * _Nonnull layer, bool disableScreenshot
bool getLayerDisableScreenshots(CALayer * _Nonnull layer); bool getLayerDisableScreenshots(CALayer * _Nonnull layer);
void setLayerContentsMaskMode(CALayer * _Nonnull layer, bool maskMode); void setLayerContentsMaskMode(CALayer * _Nonnull layer, bool maskMode);
void setMonochromaticEffectImpl(UIView * _Nonnull view, bool isEnabled);

View File

@ -61,6 +61,24 @@ CABasicAnimation * _Nonnull makeSpringAnimationImpl(NSString * _Nonnull keyPath)
return springAnimation; return springAnimation;
} }
CABasicAnimation * _Nonnull make26SpringAnimationImpl(NSString * _Nonnull keyPath, double duration) {
CASpringAnimation *springAnimation = [CASpringAnimation animationWithKeyPath:keyPath];
springAnimation.mass = 1.0f;
springAnimation.stiffness = 555.027;
springAnimation.damping = 47.118;
springAnimation.duration = duration;
springAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
if (@available(iOS 17.0, *)) {
springAnimation.allowsOverdamping = false;
}
if (@available(iOS 15.0, *)) {
[springAnimation setValue:@(1048619) forKey:@"highFrameRateReason"];
springAnimation.preferredFrameRateRange = CAFrameRateRangeMake(80.0, 120.0, 120.0);
}
return springAnimation;
}
CABasicAnimation * _Nonnull makeSpringBounceAnimationImpl(NSString * _Nonnull keyPath, CGFloat initialVelocity, CGFloat damping) { CABasicAnimation * _Nonnull makeSpringBounceAnimationImpl(NSString * _Nonnull keyPath, CGFloat initialVelocity, CGFloat damping) {
CASpringAnimation *springAnimation = [CASpringAnimation animationWithKeyPath:keyPath]; CASpringAnimation *springAnimation = [CASpringAnimation animationWithKeyPath:keyPath];
springAnimation.mass = 5.0f; springAnimation.mass = 5.0f;
@ -134,6 +152,20 @@ static void setBoolField(NSObject *object, NSString *name, BOOL value) {
[inv invoke]; [inv invoke];
} }
static void setLongLongField(NSObject *object, NSString *name, long long value) {
SEL selector = NSSelectorFromString(name);
NSMethodSignature *signature = [[object class] instanceMethodSignatureForSelector:selector];
if (signature == nil) {
return;
}
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:signature];
[inv setSelector:selector];
[inv setArgument:&value atIndex:2];
[inv setTarget:object];
[inv invoke];
}
UIBlurEffect *makeCustomZoomBlurEffectImpl(bool isLight) { UIBlurEffect *makeCustomZoomBlurEffectImpl(bool isLight) {
if (@available(iOS 13.0, *)) { if (@available(iOS 13.0, *)) {
if (isLight) { if (isLight) {
@ -299,3 +331,27 @@ void setLayerContentsMaskMode(CALayer * _Nonnull layer, bool maskMode) {
[layer setValue:@"RGBA" forKey:key]; [layer setValue:@"RGBA" forKey:key];
} }
} }
void setMonochromaticEffectImpl(UIView * _Nonnull view, bool isEnabled) {
if (@available(iOS 26.0, *)) {
static NSString *key1 = nil;
static NSString *key2 = nil;
static NSString *key3 = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
key1 = [[@"_" stringByAppendingString:@"setAllows"] stringByAppendingString:@"MonochromaticTreatment:"];
key2 = [[@"_" stringByAppendingString:@"setEnable"] stringByAppendingString:@"MonochromaticTreatment:"];
key3 = [[@"_" stringByAppendingString:@"set"] stringByAppendingString:@"MonochromaticTreatment:"];
});
if (isEnabled) {
setBoolField(view, key1, true);
setBoolField(view, key2, true);
setLongLongField(view, key3, 2);
} else {
setBoolField(view, key1, false);
setBoolField(view, key2, false);
setLongLongField(view, key3, 0);
}
}
}