This commit is contained in:
Isaac 2025-09-16 10:11:54 +04:00
parent e0d511165c
commit 5a8bd1d98d
29 changed files with 342 additions and 179 deletions

View File

@ -14,7 +14,7 @@ open class PagerExternalTopPanelContainer: SparseContainerView {
} }
public protocol PagerContentViewWithBackground: UIView { public protocol PagerContentViewWithBackground: UIView {
func pagerUpdateBackground(backgroundFrame: CGRect, topPanelHeight: CGFloat, transition: ComponentTransition) func pagerUpdateBackground(backgroundFrame: CGRect, topPanelHeight: CGFloat, externalTintMaskContainer: UIView?, transition: ComponentTransition)
} }
public final class PagerComponentChildEnvironment: Equatable { public final class PagerComponentChildEnvironment: Equatable {
@ -206,6 +206,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
public let externalTopPanelContainer: PagerExternalTopPanelContainer? public let externalTopPanelContainer: PagerExternalTopPanelContainer?
public let bottomPanel: AnyComponent<PagerComponentPanelEnvironment<TopPanelEnvironment>>? public let bottomPanel: AnyComponent<PagerComponentPanelEnvironment<TopPanelEnvironment>>?
public let externalBottomPanelContainer: PagerExternalTopPanelContainer? public let externalBottomPanelContainer: PagerExternalTopPanelContainer?
public let externalTintMaskContainer: UIView?
public let panelStateUpdated: ((PagerComponentPanelState, ComponentTransition) -> Void)? public let panelStateUpdated: ((PagerComponentPanelState, ComponentTransition) -> Void)?
public let isTopPanelExpandedUpdated: (Bool, ComponentTransition) -> Void public let isTopPanelExpandedUpdated: (Bool, ComponentTransition) -> Void
public let isTopPanelHiddenUpdated: (Bool, ComponentTransition) -> Void public let isTopPanelHiddenUpdated: (Bool, ComponentTransition) -> Void
@ -228,6 +229,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
externalTopPanelContainer: PagerExternalTopPanelContainer?, externalTopPanelContainer: PagerExternalTopPanelContainer?,
bottomPanel: AnyComponent<PagerComponentPanelEnvironment<TopPanelEnvironment>>?, bottomPanel: AnyComponent<PagerComponentPanelEnvironment<TopPanelEnvironment>>?,
externalBottomPanelContainer: PagerExternalTopPanelContainer?, externalBottomPanelContainer: PagerExternalTopPanelContainer?,
externalTintMaskContainer: UIView?,
panelStateUpdated: ((PagerComponentPanelState, ComponentTransition) -> Void)?, panelStateUpdated: ((PagerComponentPanelState, ComponentTransition) -> Void)?,
isTopPanelExpandedUpdated: @escaping (Bool, ComponentTransition) -> Void, isTopPanelExpandedUpdated: @escaping (Bool, ComponentTransition) -> Void,
isTopPanelHiddenUpdated: @escaping (Bool, ComponentTransition) -> Void, isTopPanelHiddenUpdated: @escaping (Bool, ComponentTransition) -> Void,
@ -249,6 +251,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
self.externalTopPanelContainer = externalTopPanelContainer self.externalTopPanelContainer = externalTopPanelContainer
self.bottomPanel = bottomPanel self.bottomPanel = bottomPanel
self.externalBottomPanelContainer = externalBottomPanelContainer self.externalBottomPanelContainer = externalBottomPanelContainer
self.externalTintMaskContainer = externalTintMaskContainer
self.panelStateUpdated = panelStateUpdated self.panelStateUpdated = panelStateUpdated
self.isTopPanelExpandedUpdated = isTopPanelExpandedUpdated self.isTopPanelExpandedUpdated = isTopPanelExpandedUpdated
self.isTopPanelHiddenUpdated = isTopPanelHiddenUpdated self.isTopPanelHiddenUpdated = isTopPanelHiddenUpdated
@ -292,6 +295,9 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
if lhs.externalBottomPanelContainer !== rhs.externalBottomPanelContainer { if lhs.externalBottomPanelContainer !== rhs.externalBottomPanelContainer {
return false return false
} }
if lhs.externalTintMaskContainer !== rhs.externalTintMaskContainer {
return false
}
if lhs.panelHideBehavior != rhs.panelHideBehavior { if lhs.panelHideBehavior != rhs.panelHideBehavior {
return false return false
} }
@ -307,6 +313,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
public final class View: UIView, ComponentTaggedView { public final class View: UIView, ComponentTaggedView {
private final class ContentView { private final class ContentView {
let view: ComponentHostView<(ChildEnvironmentType, PagerComponentChildEnvironment)> let view: ComponentHostView<(ChildEnvironmentType, PagerComponentChildEnvironment)>
let tintMaskContainer: UIView
var scrollingPanelOffsetToTopEdge: CGFloat = 0.0 var scrollingPanelOffsetToTopEdge: CGFloat = 0.0
var scrollingPanelOffsetToBottomEdge: CGFloat = .greatestFiniteMagnitude var scrollingPanelOffsetToBottomEdge: CGFloat = .greatestFiniteMagnitude
var scrollingPanelOffsetFraction: CGFloat = 0.0 var scrollingPanelOffsetFraction: CGFloat = 0.0
@ -315,6 +322,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
init(view: ComponentHostView<(ChildEnvironmentType, PagerComponentChildEnvironment)>) { init(view: ComponentHostView<(ChildEnvironmentType, PagerComponentChildEnvironment)>) {
self.view = view self.view = view
self.tintMaskContainer = UIView()
} }
} }
@ -847,6 +855,9 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
} else { } else {
self.contentClippingView.insertSubview(contentView.view, at: 0) self.contentClippingView.insertSubview(contentView.view, at: 0)
} }
if let externalTintMaskContainer = component.externalTintMaskContainer {
externalTintMaskContainer.addSubview(contentView.tintMaskContainer)
}
} }
let childContentInsets = contentInsets let childContentInsets = contentInsets
@ -890,6 +901,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
if wasAdded { if wasAdded {
if case .none = transition.animation { if case .none = transition.animation {
contentView.view.frame = contentFrame contentView.view.frame = contentFrame
contentView.tintMaskContainer.frame = contentFrame
} else { } else {
var referenceDirectionIsRight: Bool? var referenceDirectionIsRight: Bool?
for (previousId, previousFrame) in referenceFrames { for (previousId, previousFrame) in referenceFrames {
@ -906,6 +918,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
} }
if let referenceDirectionIsRight = referenceDirectionIsRight { if let referenceDirectionIsRight = referenceDirectionIsRight {
contentView.view.frame = contentFrame.offsetBy(dx: referenceDirectionIsRight ? contentFrame.width : (-contentFrame.width), dy: 0.0) contentView.view.frame = contentFrame.offsetBy(dx: referenceDirectionIsRight ? contentFrame.width : (-contentFrame.width), dy: 0.0)
contentView.tintMaskContainer.frame = contentView.view.frame
transition.setFrame(view: contentView.view, frame: contentFrame, completion: { [weak self] completed in transition.setFrame(view: contentView.view, frame: contentFrame, completion: { [weak self] completed in
if completed && !isInBounds && isPartOfTransition { if completed && !isInBounds && isPartOfTransition {
DispatchQueue.main.async { DispatchQueue.main.async {
@ -913,6 +926,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
} }
} }
}) })
transition.setFrame(view: contentView.tintMaskContainer, frame: contentFrame)
} else { } else {
contentView.view.frame = contentFrame contentView.view.frame = contentFrame
} }
@ -925,10 +939,11 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
} }
} }
}) })
transition.setFrame(view: contentView.tintMaskContainer, frame: contentFrame)
} }
if let contentViewWithBackground = contentView.view.componentView as? PagerContentViewWithBackground { if let contentViewWithBackground = contentView.view.componentView as? PagerContentViewWithBackground {
contentViewWithBackground.pagerUpdateBackground(backgroundFrame: backgroundFrame, topPanelHeight: topPanelHeight, transition: contentTransition) contentViewWithBackground.pagerUpdateBackground(backgroundFrame: backgroundFrame, topPanelHeight: topPanelHeight, externalTintMaskContainer: component.externalTintMaskContainer == nil ? nil : contentView.tintMaskContainer, transition: contentTransition)
} }
} }
} }
@ -941,7 +956,10 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
} }
} }
for id in removedIds { for id in removedIds {
self.contentViews.removeValue(forKey: id)?.view.removeFromSuperview() if let contentView = self.contentViews.removeValue(forKey: id) {
contentView.view.removeFromSuperview()
contentView.tintMaskContainer.removeFromSuperview()
}
} }
if let panelStateUpdated = component.panelStateUpdated { if let panelStateUpdated = component.panelStateUpdated {

View File

@ -707,6 +707,7 @@ final class ComposePollScreenComponent: Component {
hasStickers: false, hasStickers: false,
hasGifs: false, hasGifs: false,
hideBackground: true, hideBackground: true,
maskEdge: true,
sendGif: nil sendGif: nil
) )
) )

View File

@ -237,7 +237,11 @@ public enum DeviceMetrics: CaseIterable, Equatable {
public func onScreenNavigationHeight(inLandscape: Bool, systemOnScreenNavigationHeight: CGFloat?) -> CGFloat? { public func onScreenNavigationHeight(inLandscape: Bool, systemOnScreenNavigationHeight: CGFloat?) -> CGFloat? {
switch self { switch self {
case .iPhoneX, .iPhoneXSMax, .iPhoneXr, .iPhone12Mini, .iPhone12, .iPhone12ProMax, .iPhone13Mini, .iPhone13, .iPhone13Pro, .iPhone13ProMax, .iPhone14Pro, .iPhone14ProMax, .iPhone16Pro, .iPhone16ProMax: case .iPhoneX, .iPhoneXSMax, .iPhoneXr, .iPhone12Mini, .iPhone12, .iPhone12ProMax, .iPhone13Mini, .iPhone13, .iPhone13Pro, .iPhone13ProMax, .iPhone14Pro, .iPhone14ProMax, .iPhone16Pro, .iPhone16ProMax:
return inLandscape ? 21.0 : 34.0 if #available(iOS 26.0, *) {
return 20.0
} else {
return inLandscape ? 21.0 : 34.0
}
case .iPhone14ProZoomed: case .iPhone14ProZoomed:
return inLandscape ? 21.0 : 28.0 return inLandscape ? 21.0 : 28.0
case .iPhone14ProMaxZoomed: case .iPhone14ProMaxZoomed:

View File

@ -312,6 +312,7 @@ open class BlurredBackgroundView: UIView {
private var _color: UIColor? private var _color: UIColor?
private var enableBlur: Bool private var enableBlur: Bool
private var customBlurRadius: CGFloat?
public private(set) var effectView: UIVisualEffectView? public private(set) var effectView: UIVisualEffectView?
private let backgroundView: UIView private let backgroundView: UIView
@ -326,9 +327,10 @@ open class BlurredBackgroundView: UIView {
} }
} }
public init(color: UIColor?, enableBlur: Bool = true) { public init(color: UIColor?, enableBlur: Bool = true, customBlurRadius: CGFloat? = nil) {
self._color = nil self._color = nil
self.enableBlur = enableBlur self.enableBlur = enableBlur
self.customBlurRadius = customBlurRadius
self.backgroundView = UIView() self.backgroundView = UIView()
@ -349,7 +351,6 @@ open class BlurredBackgroundView: UIView {
if let color = self._color, self.enableBlur && !sharedIsReduceTransparencyEnabled && ((color.alpha > .ulpOfOne && color.alpha < 0.95) || forceKeepBlur) { if let color = self._color, self.enableBlur && !sharedIsReduceTransparencyEnabled && ((color.alpha > .ulpOfOne && color.alpha < 0.95) || forceKeepBlur) {
if self.effectView == nil { if self.effectView == nil {
let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light)) let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
//effectView.isHidden = true
for subview in effectView.subviews { for subview in effectView.subviews {
if subview.description.contains("VisualEffectSubview") { if subview.description.contains("VisualEffectSubview") {
@ -373,6 +374,9 @@ open class BlurredBackgroundView: UIView {
if !allowedKeys.contains(filterName) { if !allowedKeys.contains(filterName) {
return false return false
} }
if let customBlurRadius = self.customBlurRadius, filterName == "gaussianBlur" {
filter.setValue(customBlurRadius as NSNumber, forKey: "inputRadius")
}
return true return true
} }
} }

View File

@ -179,9 +179,9 @@ final class TabBarControllerNode: ASDisplayNode {
if bottomInset == 0.0 { if bottomInset == 0.0 {
bottomInset = 8.0 bottomInset = 8.0
} else { } else {
bottomInset = max(bottomInset - 13.0, 8.0) bottomInset = max(bottomInset, 8.0)
} }
let sideInset: CGFloat = 21.0 let sideInset: CGFloat = 20.0
var selectedId: AnyHashable? var selectedId: AnyHashable?
if self.selectedIndex < self.tabBarItems.count { if self.selectedIndex < self.tabBarItems.count {

View File

@ -941,7 +941,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
let inputPanel = PresentationThemeChatInputPanel( let inputPanel = PresentationThemeChatInputPanel(
panelBackgroundColor: rootNavigationBar.blurredBackgroundColor, panelBackgroundColor: rootNavigationBar.blurredBackgroundColor,
panelBackgroundColorNoWallpaper: UIColor(rgb: 0xffffff), panelBackgroundColorNoWallpaper: UIColor(rgb: 0xffffff),
panelSeparatorColor: UIColor(rgb: 0xb2b2b2), panelSeparatorColor: UIColor(white: 1.0, alpha: 0.5),
panelControlAccentColor: defaultDayAccentColor, panelControlAccentColor: defaultDayAccentColor,
panelControlColor: UIColor(rgb: 0x858e99), panelControlColor: UIColor(rgb: 0x858e99),
panelControlDisabledColor: UIColor(rgb: 0x727b87, alpha: 0.5), panelControlDisabledColor: UIColor(rgb: 0x727b87, alpha: 0.5),

View File

@ -1219,6 +1219,7 @@ final class AvatarEditorScreenComponent: Component {
defaultToEmojiTab: true, defaultToEmojiTab: true,
externalTopPanelContainer: self.panelHostView, externalTopPanelContainer: self.panelHostView,
externalBottomPanelContainer: nil, externalBottomPanelContainer: nil,
externalTintMaskContainer: nil,
displayTopPanelBackground: .blur, displayTopPanelBackground: .blur,
topPanelExtensionUpdated: { _, _ in }, topPanelExtensionUpdated: { _, _ in },
topPanelScrollingOffset: { _, _ in }, topPanelScrollingOffset: { _, _ in },

View File

@ -99,7 +99,7 @@ public final class ChatRecordingViewOnceButtonNode: HighlightTrackingButtonNode
let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(size.width / 2.0 - innerSize.width / 2.0), y: floorToScreenPixels(size.height / 2.0 - innerSize.height / 2.0)), size: innerSize) let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(size.width / 2.0 - innerSize.width / 2.0), y: floorToScreenPixels(size.height / 2.0 - innerSize.height / 2.0)), size: innerSize)
self.backgroundView.frame = backgroundFrame self.backgroundView.frame = backgroundFrame
self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65)), transition: .immediate) self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: .immediate)
if let iconImage = self.iconNode.image { if let iconImage = self.iconNode.image {
let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(size.width / 2.0 - iconImage.size.width / 2.0), y: floorToScreenPixels(size.height / 2.0 - iconImage.size.height / 2.0)), size: iconImage.size) let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(size.width / 2.0 - iconImage.size.width / 2.0), y: floorToScreenPixels(size.height / 2.0 - iconImage.size.height / 2.0)), size: iconImage.size)

View File

@ -334,7 +334,7 @@ public final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessag
} }
transition.updateFrame(view: self.micButtonBackgroundView, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(view: self.micButtonBackgroundView, frame: CGRect(origin: CGPoint(), size: size))
self.micButtonBackgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65)), transition: ComponentTransition(transition)) self.micButtonBackgroundView.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))
transition.updateFrame(layer: self.micButton.layer, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(layer: self.micButton.layer, frame: CGRect(origin: CGPoint(), size: size))
self.micButton.layoutItems() self.micButton.layoutItems()
@ -349,7 +349,7 @@ public final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessag
transition.updateFrame(view: self.expandMediaInputButton, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(view: self.expandMediaInputButton, frame: CGRect(origin: CGPoint(), size: size))
transition.updateFrame(view: self.expandMediaInputButtonBackgroundView, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(view: self.expandMediaInputButtonBackgroundView, frame: CGRect(origin: CGPoint(), size: size))
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.65)), 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) transition.updatePosition(layer: self.expandMediaInputButtonIcon.layer, position: expandIconFrame.center)

View File

@ -2213,7 +2213,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
let alphaTransitionIn: ContainedViewLayoutTransition = transition.isAnimated ? ContainedViewLayoutTransition.animated(duration: 0.15, curve: .easeInOut) : .immediate let alphaTransitionIn: ContainedViewLayoutTransition = transition.isAnimated ? ContainedViewLayoutTransition.animated(duration: 0.15, curve: .easeInOut) : .immediate
let alphaTransitionOut: ContainedViewLayoutTransition = transition.isAnimated ? ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut) : .immediate let alphaTransitionOut: ContainedViewLayoutTransition = transition.isAnimated ? ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut) : .immediate
let accessoryPanelAnimationBlurRadius: CGFloat = 10.0 let accessoryPanelAnimationBlurRadius: CGFloat = 20.0
var removedAccessoryPanelView: UIView? var removedAccessoryPanelView: UIView?
if let currentAccessoryPanel = self.accessoryPanel, currentAccessoryPanel.component.id != accessoryPanel?.id { if let currentAccessoryPanel = self.accessoryPanel, currentAccessoryPanel.component.id != accessoryPanel?.id {
@ -2334,7 +2334,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
transition.updateFrame(node: self.textInputContainer, frame: textInputContainerBackgroundFrame) transition.updateFrame(node: self.textInputContainer, frame: textInputContainerBackgroundFrame)
transition.updateFrame(view: self.textInputContainerBackgroundView, frame: CGRect(origin: CGPoint(), size: textInputContainerBackgroundFrame.size)) transition.updateFrame(view: self.textInputContainerBackgroundView, frame: CGRect(origin: CGPoint(), size: textInputContainerBackgroundFrame.size))
self.textInputContainerBackgroundView.update(size: textInputContainerBackgroundFrame.size, cornerRadius: floor(minimalInputHeight * 0.5), isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65)), transition: ComponentTransition(transition)) self.textInputContainerBackgroundView.update(size: textInputContainerBackgroundFrame.size, cornerRadius: floor(minimalInputHeight * 0.5), isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: ComponentTransition(transition))
if let removedAccessoryPanelView { if let removedAccessoryPanelView {
if let removedAccessoryPanelView = removedAccessoryPanelView as? ChatInputAccessoryPanelView { if let removedAccessoryPanelView = removedAccessoryPanelView as? ChatInputAccessoryPanelView {
@ -2625,7 +2625,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.65)), 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)), 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)

View File

@ -41,6 +41,7 @@ swift_library(
"//submodules/TelegramUI/Components/LegacyMessageInputPanelInputView:LegacyMessageInputPanelInputView", "//submodules/TelegramUI/Components/LegacyMessageInputPanelInputView:LegacyMessageInputPanelInputView",
"//submodules/AttachmentTextInputPanelNode", "//submodules/AttachmentTextInputPanelNode",
"//submodules/TelegramUI/Components/BatchVideoRendering", "//submodules/TelegramUI/Components/BatchVideoRendering",
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -31,6 +31,7 @@ import Pasteboard
import EntityKeyboardGifContent import EntityKeyboardGifContent
import LegacyMessageInputPanelInputView import LegacyMessageInputPanelInputView
import AttachmentTextInputPanelNode import AttachmentTextInputPanelNode
import GlassBackgroundComponent
public final class EmptyInputView: UIView, UIInputViewAudioFeedback { public final class EmptyInputView: UIView, UIInputViewAudioFeedback {
public var enableInputClicksWhenVisible: Bool { public var enableInputClicksWhenVisible: Bool {
@ -168,6 +169,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
hasStickers: Bool = true, hasStickers: Bool = true,
hasGifs: Bool = true, hasGifs: Bool = true,
hideBackground: Bool = false, hideBackground: Bool = false,
maskEdge: Bool = false,
forceHasPremium: Bool = false, forceHasPremium: Bool = false,
sendGif: ((FileMediaReference, UIView, CGRect, Bool, Bool) -> Bool)? sendGif: ((FileMediaReference, UIView, CGRect, Bool, Bool) -> Bool)?
) -> Signal<InputData, NoError> { ) -> Signal<InputData, NoError> {
@ -187,7 +189,8 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
chatPeerId: chatPeerId, chatPeerId: chatPeerId,
hasSearch: hasSearch, hasSearch: hasSearch,
forceHasPremium: forceHasPremium, forceHasPremium: forceHasPremium,
hideBackground: hideBackground hideBackground: hideBackground,
maskEdge: maskEdge
) )
let stickerNamespaces: [ItemCollectionId.Namespace] = [Namespaces.ItemCollection.CloudStickerPacks] let stickerNamespaces: [ItemCollectionId.Namespace] = [Namespaces.ItemCollection.CloudStickerPacks]
@ -414,6 +417,14 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
return self.externalTopPanelContainerImpl return self.externalTopPanelContainerImpl
} }
private let clippingView: UIView
private var backgroundView: BlurredBackgroundView?
private var backgroundTintView: UIImageView?
private var backgroundChromeView: UIImageView?
private var backgroundTintMaskView: UIView?
private var backgroundTintMaskContentView: UIView?
private var externalBackground: EmojiPagerContentComponent.ExternalBackground?
public var switchToTextInput: (() -> Void)? public var switchToTextInput: (() -> Void)?
private var currentState: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, interfaceState: ChatPresentationInterfaceState, layoutMetrics: LayoutMetrics, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool)? private var currentState: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, interfaceState: ChatPresentationInterfaceState, layoutMetrics: LayoutMetrics, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool)?
@ -476,6 +487,10 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
self.interaction = interaction self.interaction = interaction
self.clippingView = UIView()
self.clippingView.clipsToBounds = true
self.clippingView.layer.cornerRadius = 20.0
self.entityKeyboardView = ComponentHostView<Empty>() self.entityKeyboardView = ComponentHostView<Empty>()
super.init() super.init()
@ -485,7 +500,41 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
self.topBackgroundExtension = 34.0 self.topBackgroundExtension = 34.0
self.followsDefaultHeight = true self.followsDefaultHeight = true
self.view.addSubview(self.entityKeyboardView) if "".isEmpty {
let backgroundView = BlurredBackgroundView(color: .black, enableBlur: true)
self.backgroundView = backgroundView
self.view.addSubview(backgroundView)
let backgroundTintView = UIImageView()
self.backgroundTintView = backgroundTintView
self.view.addSubview(backgroundTintView)
let backgroundTintMaskView = UIView()
backgroundTintMaskView.backgroundColor = .white
self.backgroundTintMaskView = backgroundTintMaskView
if let filter = CALayer.luminanceToAlpha() {
backgroundTintMaskView.layer.filters = [filter]
}
backgroundTintView.mask = backgroundTintMaskView
let backgroundTintMaskContentView = UIView()
backgroundTintMaskView.addSubview(backgroundTintMaskContentView)
self.backgroundTintMaskContentView = backgroundTintMaskContentView
let backgroundChromeView = UIImageView()
self.backgroundChromeView = backgroundChromeView
self.externalBackground = EmojiPagerContentComponent.ExternalBackground(
effectContainerView: backgroundTintMaskContentView
)
}
self.clippingView.addSubview(self.entityKeyboardView)
self.view.addSubview(self.clippingView)
if let backgroundChromeView = self.backgroundChromeView {
self.view.addSubview(backgroundChromeView)
}
self.externalTopPanelContainerImpl = PagerExternalTopPanelContainer() self.externalTopPanelContainerImpl = PagerExternalTopPanelContainer()
@ -1162,7 +1211,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
chatPeerId: chatPeerId, chatPeerId: chatPeerId,
peekBehavior: stickerPeekBehavior, peekBehavior: stickerPeekBehavior,
customLayout: nil, customLayout: nil,
externalBackground: nil, externalBackground: self.externalBackground,
externalExpansionView: nil, externalExpansionView: nil,
customContentView: nil, customContentView: nil,
useOpaqueTheme: self.useOpaqueTheme, useOpaqueTheme: self.useOpaqueTheme,
@ -1509,7 +1558,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
chatPeerId: chatPeerId, chatPeerId: chatPeerId,
peekBehavior: stickerPeekBehavior, peekBehavior: stickerPeekBehavior,
customLayout: nil, customLayout: nil,
externalBackground: nil, externalBackground: self.externalBackground,
externalExpansionView: nil, externalExpansionView: nil,
customContentView: nil, customContentView: nil,
useOpaqueTheme: self.useOpaqueTheme, useOpaqueTheme: self.useOpaqueTheme,
@ -1739,6 +1788,22 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .immediate, interfaceState: interfaceState, layoutMetrics: layoutMetrics, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded) let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .immediate, interfaceState: interfaceState, layoutMetrics: layoutMetrics, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded)
} }
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let result = super.hitTest(point, with: event) {
return result
}
if let backgroundView = self.backgroundView, backgroundView.frame.contains(point) {
for subview in self.view.subviews.reversed() {
if let result = subview.hitTest(self.view.convert(point, to: subview), with: event) {
return result
}
}
}
return nil
}
public override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, layoutMetrics: LayoutMetrics, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool) -> (CGFloat, CGFloat) { public override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, layoutMetrics: LayoutMetrics, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool) -> (CGFloat, CGFloat) {
self.currentState = (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, layoutMetrics, deviceMetrics, isVisible, isExpanded) self.currentState = (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, layoutMetrics, deviceMetrics, isVisible, isExpanded)
@ -1849,7 +1914,8 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
defaultToEmojiTab: self.defaultToEmojiTab, defaultToEmojiTab: self.defaultToEmojiTab,
externalTopPanelContainer: self.externalTopPanelContainerImpl, externalTopPanelContainer: self.externalTopPanelContainerImpl,
externalBottomPanelContainer: nil, externalBottomPanelContainer: nil,
displayTopPanelBackground: self.opaqueTopPanelBackground ? .opaque : .none, externalTintMaskContainer: self.backgroundTintMaskContentView,
displayTopPanelBackground: self.opaqueTopPanelBackground ? .opaque : .blur,
topPanelExtensionUpdated: { [weak self] topPanelExtension, transition in topPanelExtensionUpdated: { [weak self] topPanelExtension, transition in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -1939,7 +2005,52 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
environment: {}, environment: {},
containerSize: CGSize(width: width, height: expandedHeight) containerSize: CGSize(width: width, height: expandedHeight)
) )
transition.updateFrame(view: self.entityKeyboardView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: entityKeyboardSize))
var clippingFrame = CGRect(origin: CGPoint(), size: entityKeyboardSize)
clippingFrame.size.height += 32.0
var entityKeyboardSizeFrame = CGRect(origin: CGPoint(), size: entityKeyboardSize)
if self.hideInput {
clippingFrame.size.height += self.topBackgroundExtension
clippingFrame.origin.y -= self.topBackgroundExtension
entityKeyboardSizeFrame.origin.y += self.topBackgroundExtension
}
transition.updateFrame(view: self.entityKeyboardView, frame: entityKeyboardSizeFrame)
transition.updateFrame(view: self.clippingView, frame: clippingFrame)
if let backgroundView = self.backgroundView, let backgroundTintView = self.backgroundTintView, let backgroundTintMaskView = self.backgroundTintMaskView, let backgroundTintMaskContentView = self.backgroundTintMaskContentView, let backgroundChromeView = self.backgroundChromeView {
var backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: entityKeyboardSize)
if self.hideInput {
backgroundFrame.size.height += self.topBackgroundExtension
backgroundFrame.origin.y -= self.topBackgroundExtension
}
backgroundFrame.size.height += 32.0
if backgroundChromeView.image == nil {
backgroundChromeView.image = GlassBackgroundView.generateForegroundImage(size: CGSize(width: 20.0 * 2.0, height: 20.0 * 2.0), isDark: interfaceState.theme.overallDarkAppearance, fillColor: .clear)
}
if backgroundTintView.image == nil {
backgroundTintView.image = generateStretchableFilledCircleImage(diameter: 20.0 * 2.0, color: .white)?.withRenderingMode(.alwaysTemplate)
}
backgroundTintView.tintColor = interfaceState.theme.chat.inputMediaPanel.backgroundColor
transition.updateFrame(view: backgroundView, frame: backgroundFrame)
backgroundView.updateColor(color: .clear, forceKeepBlur: true, transition: .immediate)
backgroundView.update(size: backgroundFrame.size, cornerRadius: 20.0, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], transition: transition)
transition.updateFrame(view: backgroundChromeView, frame: backgroundFrame.insetBy(dx: -1.0, dy: 0.0))
var backgroundTintMaskContentFrame = CGRect(origin: CGPoint(), size: backgroundFrame.size)
if self.hideInput {
backgroundTintMaskContentFrame.origin.y += self.topBackgroundExtension
}
transition.updateFrame(view: backgroundTintView, frame: backgroundFrame)
transition.updateFrame(view: backgroundTintMaskView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
transition.updateFrame(view: backgroundTintMaskContentView, frame: backgroundTintMaskContentFrame)
}
let layoutTime = CFAbsoluteTimeGetCurrent() - startTime let layoutTime = CFAbsoluteTimeGetCurrent() - startTime
if layoutTime > 0.1 { if layoutTime > 0.1 {

View File

@ -467,7 +467,7 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM
tintColor = UIColor(white: 0.0, alpha: 0.5) tintColor = UIColor(white: 0.0, alpha: 0.5)
} else { } else {
isDark = self.theme.overallDarkAppearance isDark = self.theme.overallDarkAppearance
tintColor = self.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65) tintColor = self.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)
} }
let view = WrapperBlurrredBackgroundView(size: CGSize(width: 40.0, height: 72.0), isDark: isDark, tintColor: tintColor) let view = WrapperBlurrredBackgroundView(size: CGSize(width: 40.0, height: 72.0), isDark: isDark, tintColor: tintColor)

View File

@ -187,6 +187,7 @@ public final class EmojiStatusSelectionComponent: Component {
defaultToEmojiTab: true, defaultToEmojiTab: true,
externalTopPanelContainer: self.panelHostView, externalTopPanelContainer: self.panelHostView,
externalBottomPanelContainer: nil, externalBottomPanelContainer: nil,
externalTintMaskContainer: nil,
displayTopPanelBackground: .blur, displayTopPanelBackground: .blur,
topPanelExtensionUpdated: { _, _ in }, topPanelExtensionUpdated: { _, _ in },
topPanelScrollingOffset: { _, _ in }, topPanelScrollingOffset: { _, _ in },

View File

@ -623,6 +623,7 @@ public final class EmojiPagerContentComponent: Component {
public let searchState: SearchState public let searchState: SearchState
public let warpContentsOnEdges: Bool public let warpContentsOnEdges: Bool
public let hideBackground: Bool public let hideBackground: Bool
public let maskEdge: Bool
public let displaySearchWithPlaceholder: String? public let displaySearchWithPlaceholder: String?
public let searchCategories: EmojiSearchCategories? public let searchCategories: EmojiSearchCategories?
public let searchInitiallyHidden: Bool public let searchInitiallyHidden: Bool
@ -648,6 +649,7 @@ public final class EmojiPagerContentComponent: Component {
searchState: SearchState, searchState: SearchState,
warpContentsOnEdges: Bool, warpContentsOnEdges: Bool,
hideBackground: Bool, hideBackground: Bool,
maskEdge: Bool,
displaySearchWithPlaceholder: String?, displaySearchWithPlaceholder: String?,
searchCategories: EmojiSearchCategories?, searchCategories: EmojiSearchCategories?,
searchInitiallyHidden: Bool, searchInitiallyHidden: Bool,
@ -672,6 +674,7 @@ public final class EmojiPagerContentComponent: Component {
self.searchState = searchState self.searchState = searchState
self.warpContentsOnEdges = warpContentsOnEdges self.warpContentsOnEdges = warpContentsOnEdges
self.hideBackground = hideBackground self.hideBackground = hideBackground
self.maskEdge = maskEdge
self.displaySearchWithPlaceholder = displaySearchWithPlaceholder self.displaySearchWithPlaceholder = displaySearchWithPlaceholder
self.searchCategories = searchCategories self.searchCategories = searchCategories
self.searchInitiallyHidden = searchInitiallyHidden self.searchInitiallyHidden = searchInitiallyHidden
@ -699,6 +702,7 @@ public final class EmojiPagerContentComponent: Component {
searchState: searchState, searchState: searchState,
warpContentsOnEdges: self.warpContentsOnEdges, warpContentsOnEdges: self.warpContentsOnEdges,
hideBackground: self.hideBackground, hideBackground: self.hideBackground,
maskEdge: self.maskEdge,
displaySearchWithPlaceholder: self.displaySearchWithPlaceholder, displaySearchWithPlaceholder: self.displaySearchWithPlaceholder,
searchCategories: self.searchCategories, searchCategories: self.searchCategories,
searchInitiallyHidden: self.searchInitiallyHidden, searchInitiallyHidden: self.searchInitiallyHidden,
@ -727,6 +731,7 @@ public final class EmojiPagerContentComponent: Component {
searchState: searchState, searchState: searchState,
warpContentsOnEdges: self.warpContentsOnEdges, warpContentsOnEdges: self.warpContentsOnEdges,
hideBackground: self.hideBackground, hideBackground: self.hideBackground,
maskEdge: self.maskEdge,
displaySearchWithPlaceholder: self.displaySearchWithPlaceholder, displaySearchWithPlaceholder: self.displaySearchWithPlaceholder,
searchCategories: self.searchCategories, searchCategories: self.searchCategories,
searchInitiallyHidden: self.searchInitiallyHidden, searchInitiallyHidden: self.searchInitiallyHidden,
@ -755,6 +760,7 @@ public final class EmojiPagerContentComponent: Component {
searchState: searchState, searchState: searchState,
warpContentsOnEdges: self.warpContentsOnEdges, warpContentsOnEdges: self.warpContentsOnEdges,
hideBackground: self.hideBackground, hideBackground: self.hideBackground,
maskEdge: self.maskEdge,
displaySearchWithPlaceholder: self.displaySearchWithPlaceholder, displaySearchWithPlaceholder: self.displaySearchWithPlaceholder,
searchCategories: self.searchCategories, searchCategories: self.searchCategories,
searchInitiallyHidden: self.searchInitiallyHidden, searchInitiallyHidden: self.searchInitiallyHidden,
@ -811,6 +817,9 @@ public final class EmojiPagerContentComponent: Component {
if lhs.hideBackground != rhs.hideBackground { if lhs.hideBackground != rhs.hideBackground {
return false return false
} }
if lhs.maskEdge != rhs.maskEdge {
return false
}
if lhs.displaySearchWithPlaceholder != rhs.displaySearchWithPlaceholder { if lhs.displaySearchWithPlaceholder != rhs.displaySearchWithPlaceholder {
return false return false
} }
@ -4016,12 +4025,12 @@ public final class EmojiPagerContentComponent: Component {
self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(ContentAnimation(type: .groupExpanded(id: groupId)))) self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(ContentAnimation(type: .groupExpanded(id: groupId))))
} }
public func pagerUpdateBackground(backgroundFrame: CGRect, topPanelHeight: CGFloat, transition: ComponentTransition) { public func pagerUpdateBackground(backgroundFrame: CGRect, topPanelHeight: CGFloat, externalTintMaskContainer: UIView?, transition: ComponentTransition) {
guard let component = self.component, let keyboardChildEnvironment = self.keyboardChildEnvironment, let pagerEnvironment = self.pagerEnvironment else { guard let component = self.component, let keyboardChildEnvironment = self.keyboardChildEnvironment, let pagerEnvironment = self.pagerEnvironment else {
return return
} }
if let externalBackground = component.inputInteractionHolder.inputInteraction?.externalBackground, let effectContainerView = externalBackground.effectContainerView { if let effectContainerView = externalTintMaskContainer {
let mirrorContentClippingView: UIView let mirrorContentClippingView: UIView
if let current = self.mirrorContentClippingView { if let current = self.mirrorContentClippingView {
mirrorContentClippingView = current mirrorContentClippingView = current
@ -4066,17 +4075,19 @@ public final class EmojiPagerContentComponent: Component {
if component.hideBackground { if component.hideBackground {
self.backgroundView.isHidden = true self.backgroundView.isHidden = true
let maskLayer: FadingMaskLayer if component.maskEdge {
if let current = self.fadingMaskLayer { let maskLayer: FadingMaskLayer
maskLayer = current if let current = self.fadingMaskLayer {
} else { maskLayer = current
maskLayer = FadingMaskLayer() } else {
self.fadingMaskLayer = maskLayer maskLayer = FadingMaskLayer()
self.fadingMaskLayer = maskLayer
}
if self.layer.mask == nil {
self.layer.mask = maskLayer
}
maskLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((topPanelHeight - 34.0) * 0.75)), size: backgroundFrame.size)
} }
if self.layer.mask == nil {
self.layer.mask = maskLayer
}
maskLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((topPanelHeight - 34.0) * 0.75)), size: backgroundFrame.size)
} else if component.warpContentsOnEdges { } else if component.warpContentsOnEdges {
self.backgroundView.isHidden = true self.backgroundView.isHidden = true
} else { } else {

View File

@ -65,7 +65,8 @@ public extension EmojiPagerContentComponent {
hasRecent: Bool = true, hasRecent: Bool = true,
forceHasPremium: Bool = false, forceHasPremium: Bool = false,
premiumIfSavedMessages: Bool = true, premiumIfSavedMessages: Bool = true,
hideBackground: Bool = false hideBackground: Bool = false,
maskEdge: Bool = false
) -> Signal<EmojiPagerContentComponent, NoError> { ) -> Signal<EmojiPagerContentComponent, NoError> {
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
let isPremiumDisabled = premiumConfiguration.isPremiumDisabled let isPremiumDisabled = premiumConfiguration.isPremiumDisabled
@ -1596,6 +1597,7 @@ public extension EmojiPagerContentComponent {
searchState: .empty(hasResults: false), searchState: .empty(hasResults: false),
warpContentsOnEdges: warpContentsOnEdges, warpContentsOnEdges: warpContentsOnEdges,
hideBackground: hideBackground, hideBackground: hideBackground,
maskEdge: maskEdge,
displaySearchWithPlaceholder: displaySearchWithPlaceholder, displaySearchWithPlaceholder: displaySearchWithPlaceholder,
searchCategories: searchCategories, searchCategories: searchCategories,
searchInitiallyHidden: searchInitiallyHidden, searchInitiallyHidden: searchInitiallyHidden,
@ -1632,7 +1634,8 @@ public extension EmojiPagerContentComponent {
hasAdd: Bool = false, hasAdd: Bool = false,
searchIsPlaceholderOnly: Bool = true, searchIsPlaceholderOnly: Bool = true,
subject: StickersSubject = .chatStickers, subject: StickersSubject = .chatStickers,
hideBackground: Bool = false hideBackground: Bool = false,
maskEdge: Bool = false
) -> Signal<EmojiPagerContentComponent, NoError> { ) -> Signal<EmojiPagerContentComponent, NoError> {
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
let isPremiumDisabled = premiumConfiguration.isPremiumDisabled let isPremiumDisabled = premiumConfiguration.isPremiumDisabled
@ -2178,6 +2181,7 @@ public extension EmojiPagerContentComponent {
searchState: .empty(hasResults: false), searchState: .empty(hasResults: false),
warpContentsOnEdges: warpContentsOnEdges, warpContentsOnEdges: warpContentsOnEdges,
hideBackground: hideBackground, hideBackground: hideBackground,
maskEdge: maskEdge,
displaySearchWithPlaceholder: hasSearch ? strings.StickersSearch_SearchStickersPlaceholder : nil, displaySearchWithPlaceholder: hasSearch ? strings.StickersSearch_SearchStickersPlaceholder : nil,
searchCategories: searchCategories, searchCategories: searchCategories,
searchInitiallyHidden: true, searchInitiallyHidden: true,
@ -2197,7 +2201,8 @@ public extension EmojiPagerContentComponent {
animationCache: AnimationCache, animationCache: AnimationCache,
animationRenderer: MultiAnimationRenderer, animationRenderer: MultiAnimationRenderer,
hasSearch: Bool, hasSearch: Bool,
hideBackground: Bool = false hideBackground: Bool = false,
maskEdge: Bool = false
) -> Signal<EmojiPagerContentComponent, NoError> { ) -> Signal<EmojiPagerContentComponent, NoError> {
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
let isPremiumDisabled = premiumConfiguration.isPremiumDisabled let isPremiumDisabled = premiumConfiguration.isPremiumDisabled
@ -2330,6 +2335,7 @@ public extension EmojiPagerContentComponent {
searchState: .empty(hasResults: false), searchState: .empty(hasResults: false),
warpContentsOnEdges: warpContentsOnEdges, warpContentsOnEdges: warpContentsOnEdges,
hideBackground: hideBackground, hideBackground: hideBackground,
maskEdge: maskEdge,
displaySearchWithPlaceholder: hasSearch ? strings.StickersSearch_SearchStickersPlaceholder : nil, displaySearchWithPlaceholder: hasSearch ? strings.StickersSearch_SearchStickersPlaceholder : nil,
searchCategories: searchCategories, searchCategories: searchCategories,
searchInitiallyHidden: true, searchInitiallyHidden: true,

View File

@ -469,6 +469,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode
searchState: .empty(hasResults: false), searchState: .empty(hasResults: false),
warpContentsOnEdges: false, warpContentsOnEdges: false,
hideBackground: false, hideBackground: false,
maskEdge: false,
displaySearchWithPlaceholder: self.presentationData.strings.EmojiSearch_SearchEmojiPlaceholder, displaySearchWithPlaceholder: self.presentationData.strings.EmojiSearch_SearchEmojiPlaceholder,
searchCategories: nil, searchCategories: nil,
searchInitiallyHidden: false, searchInitiallyHidden: false,
@ -509,6 +510,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode
defaultToEmojiTab: true, defaultToEmojiTab: true,
externalTopPanelContainer: self.panelHostView, externalTopPanelContainer: self.panelHostView,
externalBottomPanelContainer: nil, externalBottomPanelContainer: nil,
externalTintMaskContainer: nil,
displayTopPanelBackground: .blur, displayTopPanelBackground: .blur,
topPanelExtensionUpdated: { _, _ in }, topPanelExtensionUpdated: { _, _ in },
topPanelScrollingOffset: { _, _ in }, topPanelScrollingOffset: { _, _ in },

View File

@ -106,6 +106,7 @@ public final class EntityKeyboardComponent: Component {
public let defaultToEmojiTab: Bool public let defaultToEmojiTab: Bool
public let externalTopPanelContainer: PagerExternalTopPanelContainer? public let externalTopPanelContainer: PagerExternalTopPanelContainer?
public let externalBottomPanelContainer: PagerExternalTopPanelContainer? public let externalBottomPanelContainer: PagerExternalTopPanelContainer?
public let externalTintMaskContainer: UIView?
public let displayTopPanelBackground: DisplayTopPanelBackground public let displayTopPanelBackground: DisplayTopPanelBackground
public let topPanelExtensionUpdated: (CGFloat, ComponentTransition) -> Void public let topPanelExtensionUpdated: (CGFloat, ComponentTransition) -> Void
public let topPanelScrollingOffset: (CGFloat, ComponentTransition) -> Void public let topPanelScrollingOffset: (CGFloat, ComponentTransition) -> Void
@ -141,6 +142,7 @@ public final class EntityKeyboardComponent: Component {
defaultToEmojiTab: Bool, defaultToEmojiTab: Bool,
externalTopPanelContainer: PagerExternalTopPanelContainer?, externalTopPanelContainer: PagerExternalTopPanelContainer?,
externalBottomPanelContainer: PagerExternalTopPanelContainer?, externalBottomPanelContainer: PagerExternalTopPanelContainer?,
externalTintMaskContainer: UIView?,
displayTopPanelBackground: DisplayTopPanelBackground, displayTopPanelBackground: DisplayTopPanelBackground,
topPanelExtensionUpdated: @escaping (CGFloat, ComponentTransition) -> Void, topPanelExtensionUpdated: @escaping (CGFloat, ComponentTransition) -> Void,
topPanelScrollingOffset: @escaping (CGFloat, ComponentTransition) -> Void, topPanelScrollingOffset: @escaping (CGFloat, ComponentTransition) -> Void,
@ -175,6 +177,7 @@ public final class EntityKeyboardComponent: Component {
self.defaultToEmojiTab = defaultToEmojiTab self.defaultToEmojiTab = defaultToEmojiTab
self.externalTopPanelContainer = externalTopPanelContainer self.externalTopPanelContainer = externalTopPanelContainer
self.externalBottomPanelContainer = externalBottomPanelContainer self.externalBottomPanelContainer = externalBottomPanelContainer
self.externalTintMaskContainer = externalTintMaskContainer
self.displayTopPanelBackground = displayTopPanelBackground self.displayTopPanelBackground = displayTopPanelBackground
self.topPanelExtensionUpdated = topPanelExtensionUpdated self.topPanelExtensionUpdated = topPanelExtensionUpdated
self.topPanelScrollingOffset = topPanelScrollingOffset self.topPanelScrollingOffset = topPanelScrollingOffset
@ -716,6 +719,12 @@ public final class EntityKeyboardComponent: Component {
forceUpdate = true forceUpdate = true
} }
var bottomPanelContainerInsets = component.containerInsets
if bottomPanelContainerInsets.left == 0.0 && bottomPanelContainerInsets.bottom != 0.0 {
bottomPanelContainerInsets.left += 16.0
bottomPanelContainerInsets.right += 16.0
}
let isContentInFocus = component.isContentInFocus && self.searchComponent == nil let isContentInFocus = component.isContentInFocus && self.searchComponent == nil
let pagerSize = self.pagerView.update( let pagerSize = self.pagerView.update(
transition: transition, transition: transition,
@ -732,19 +741,20 @@ public final class EntityKeyboardComponent: Component {
topPanel: AnyComponent(EntityKeyboardTopContainerPanelComponent( topPanel: AnyComponent(EntityKeyboardTopContainerPanelComponent(
theme: component.theme, theme: component.theme,
overflowHeight: component.hiddenInputHeight, overflowHeight: component.hiddenInputHeight,
topInset: 12.0, topInset: 6.0,
displayBackground: component.externalTopPanelContainer != nil ? .none : component.displayTopPanelBackground displayBackground: component.externalTopPanelContainer != nil ? .none : component.displayTopPanelBackground
)), )),
externalTopPanelContainer: component.externalTopPanelContainer, externalTopPanelContainer: component.externalTopPanelContainer,
bottomPanel: component.displayBottomPanel ? AnyComponent(EntityKeyboardBottomPanelComponent( bottomPanel: component.displayBottomPanel ? AnyComponent(EntityKeyboardBottomPanelComponent(
theme: component.theme, theme: component.theme,
containerInsets: component.containerInsets, containerInsets: bottomPanelContainerInsets,
deleteBackwards: { [weak self] in deleteBackwards: { [weak self] in
self?.component?.emojiContent?.inputInteractionHolder.inputInteraction?.deleteBackwards?() self?.component?.emojiContent?.inputInteractionHolder.inputInteraction?.deleteBackwards?()
AudioServicesPlaySystemSound(0x451) AudioServicesPlaySystemSound(0x451)
} }
)) : nil, )) : nil,
externalBottomPanelContainer: component.externalBottomPanelContainer, externalBottomPanelContainer: component.externalBottomPanelContainer,
externalTintMaskContainer: component.externalTintMaskContainer,
panelStateUpdated: { [weak self] panelState, transition in panelStateUpdated: { [weak self] panelState, transition in
guard let strongSelf = self else { guard let strongSelf = self else {
return return

View File

@ -163,7 +163,7 @@ final class EntityKeyboardBottomPanelComponent: Component {
private var component: EntityKeyboardBottomPanelComponent? private var component: EntityKeyboardBottomPanelComponent?
override init(frame: CGRect) { override init(frame: CGRect) {
self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true) self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true, customBlurRadius: 5.0)
self.separatorView = UIView() self.separatorView = UIView()
self.separatorView.isUserInteractionEnabled = false self.separatorView.isUserInteractionEnabled = false
@ -186,8 +186,8 @@ final class EntityKeyboardBottomPanelComponent: Component {
func update(component: EntityKeyboardBottomPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize { func update(component: EntityKeyboardBottomPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
if self.component?.theme !== component.theme { if self.component?.theme !== component.theme {
self.separatorView.backgroundColor = component.theme.chat.inputMediaPanel.panelSeparatorColor self.separatorView.backgroundColor = component.theme.chat.inputPanel.panelSeparatorColor
self.backgroundView.updateColor(color: component.theme.chat.inputPanel.panelBackgroundColor.withMultipliedAlpha(1.0), transition: .immediate) self.backgroundView.updateColor(color: component.theme.chat.inputPanel.panelBackgroundColor.withMultipliedAlpha(0.65), transition: .immediate)
self.highlightedIconBackgroundView.backgroundColor = component.theme.chat.inputMediaPanel.panelHighlightedIconBackgroundColor self.highlightedIconBackgroundView.backgroundColor = component.theme.chat.inputMediaPanel.panelHighlightedIconBackgroundColor
} }

View File

@ -206,7 +206,8 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
if let current = self.backgroundView { if let current = self.backgroundView {
backgroundView = current backgroundView = current
} else { } else {
backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true) backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true, customBlurRadius: 5.0)
self.backgroundView = backgroundView
self.insertSubview(backgroundView, at: 0) self.insertSubview(backgroundView, at: 0)
} }
@ -218,12 +219,12 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
self.insertSubview(backgroundSeparatorView, aboveSubview: backgroundView) self.insertSubview(backgroundSeparatorView, aboveSubview: backgroundView)
} }
backgroundView.updateColor(color: component.theme.chat.inputPanel.panelBackgroundColor.withMultipliedAlpha(1.0), transition: .immediate) backgroundView.updateColor(color: component.theme.chat.inputPanel.panelBackgroundColor.withMultipliedAlpha(0.65), transition: .immediate)
backgroundView.update(size: CGSize(width: availableSize.width, height: height), transition: transition.containedViewLayoutTransition) backgroundView.update(size: CGSize(width: availableSize.width, height: height + component.overflowHeight), transition: transition.containedViewLayoutTransition)
transition.setFrame(view: backgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: height))) transition.setFrame(view: backgroundView, frame: CGRect(origin: CGPoint(x: 0.0, y: -component.overflowHeight), size: CGSize(width: availableSize.width, height: height + component.overflowHeight)))
backgroundSeparatorView.backgroundColor = component.theme.chat.inputPanel.panelSeparatorColor backgroundSeparatorView.backgroundColor = component.theme.chat.inputPanel.panelSeparatorColor
transition.setFrame(view: backgroundSeparatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: height), size: CGSize(width: availableSize.width, height: UIScreenPixel))) transition.setFrame(view: backgroundSeparatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: height - UIScreenPixel), size: CGSize(width: availableSize.width, height: UIScreenPixel)))
} else if case .none = component.displayBackground { } else if case .none = component.displayBackground {
self.backgroundColor = nil self.backgroundColor = nil

View File

@ -876,7 +876,7 @@ public final class GifPagerContentComponent: Component {
} }
} }
public func pagerUpdateBackground(backgroundFrame: CGRect, topPanelHeight: CGFloat, transition: ComponentTransition) { public func pagerUpdateBackground(backgroundFrame: CGRect, topPanelHeight: CGFloat, externalTintMaskContainer: UIView?, transition: ComponentTransition) {
guard let theme = self.theme else { guard let theme = self.theme else {
return return
} }
@ -912,6 +912,8 @@ public final class GifPagerContentComponent: Component {
transition.setFrame(view: self.backgroundView, frame: backgroundFrame) transition.setFrame(view: self.backgroundView, frame: backgroundFrame)
self.backgroundView.update(size: backgroundFrame.size, transition: transition.containedViewLayoutTransition) self.backgroundView.update(size: backgroundFrame.size, transition: transition.containedViewLayoutTransition)
self.backgroundView.isHidden = hideBackground
if let vibrancyEffectView = self.vibrancyEffectView { if let vibrancyEffectView = self.vibrancyEffectView {
transition.setFrame(view: vibrancyEffectView, frame: CGRect(origin: CGPoint(x: 0.0, y: -backgroundFrame.minY), size: CGSize(width: backgroundFrame.width, height: backgroundFrame.height + backgroundFrame.minY))) transition.setFrame(view: vibrancyEffectView, frame: CGRect(origin: CGPoint(x: 0.0, y: -backgroundFrame.minY), size: CGSize(width: backgroundFrame.width, height: backgroundFrame.height + backgroundFrame.minY)))
} }

View File

@ -350,6 +350,7 @@ private final class TopicIconSelectionComponent: Component {
defaultToEmojiTab: true, defaultToEmojiTab: true,
externalTopPanelContainer: self.panelHostView, externalTopPanelContainer: self.panelHostView,
externalBottomPanelContainer: nil, externalBottomPanelContainer: nil,
externalTintMaskContainer: nil,
displayTopPanelBackground: .blur, displayTopPanelBackground: .blur,
topPanelExtensionUpdated: { _, _ in }, topPanelExtensionUpdated: { _, _ in },
topPanelScrollingOffset: { _, _ in }, topPanelScrollingOffset: { _, _ in },

View File

@ -4,86 +4,6 @@ import Display
import ComponentFlow import ComponentFlow
import ComponentDisplayAdapters import ComponentDisplayAdapters
private func generateForegroundImage(size: CGSize, isDark: Bool, fillColor: UIColor) -> UIImage {
var size = size
if size == .zero {
size = CGSize(width: 1.0, height: 1.0)
}
return generateImage(size, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
let maxColor = UIColor(white: 1.0, alpha: isDark ? 0.67 : 0.9)
let minColor = UIColor(white: 1.0, alpha: 0.0)
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()
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))
}
private final class ContentContainer: UIView { private final class ContentContainer: UIView {
private let maskContentView: UIView private let maskContentView: UIView
@ -345,7 +265,6 @@ public final class GlassBackgroundView: UIView {
private let foregroundView: UIImageView? private let foregroundView: UIImageView?
private let shadowView: UIImageView? private let shadowView: UIImageView?
private let shadowMaskView: UIImageView?
public let maskContentView: UIView public let maskContentView: UIView
private let contentContainer: ContentContainer private let contentContainer: ContentContainer
@ -371,14 +290,12 @@ public final class GlassBackgroundView: UIView {
nativeView.traitOverrides.userInterfaceStyle = .light nativeView.traitOverrides.userInterfaceStyle = .light
//self.foregroundView = UIImageView() //self.foregroundView = UIImageView()
self.foregroundView = nil self.foregroundView = nil
self.shadowView = nil self.shadowView = UIImageView()
self.shadowMaskView = 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.foregroundView = UIImageView() self.foregroundView = UIImageView()
self.shadowView = UIImageView() self.shadowView = UIImageView()
self.shadowMaskView = UIImageView()
} }
self.maskContentView = UIView() self.maskContentView = UIView()
@ -393,9 +310,6 @@ public final class GlassBackgroundView: UIView {
if let shadowView = self.shadowView { if let shadowView = self.shadowView {
self.addSubview(shadowView) self.addSubview(shadowView)
if let shadowMaskView = self.shadowMaskView {
shadowView.mask = shadowMaskView
}
} }
if let nativeView = self.nativeView { if let nativeView = self.nativeView {
self.addSubview(nativeView) self.addSubview(nativeView)
@ -435,39 +349,36 @@ public final class GlassBackgroundView: UIView {
transition.setFrame(view: backgroundNode.view, frame: CGRect(origin: CGPoint(), size: size)) transition.setFrame(view: backgroundNode.view, frame: CGRect(origin: CGPoint(), size: size))
} }
let shadowInset: CGFloat = 32.0
let params = Params(cornerRadius: cornerRadius, isDark: isDark, tintColor: tintColor) let params = Params(cornerRadius: cornerRadius, isDark: isDark, tintColor: tintColor)
if self.params != params { if self.params != params {
self.params = params self.params = params
if let shadowView = self.shadowView { if let shadowView = self.shadowView {
shadowView.layer.shadowRadius = 10.0 let shadowInnerInset: CGFloat = 0.5
shadowView.layer.shadowColor = UIColor(white: 0.0, alpha: 1.0).cgColor shadowView.image = generateImage(CGSize(width: shadowInset * 2.0 + cornerRadius * 2.0, height: shadowInset * 2.0 + cornerRadius * 2.0), rotatedContext: { size, context in
shadowView.layer.shadowOpacity = 0.08 context.clear(CGRect(origin: CGPoint(), size: size))
shadowView.layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
context.setFillColor(UIColor.black.cgColor)
if let shadowMaskView = self.shadowMaskView { context.setShadow(offset: CGSize(width: 0.0, height: 1.0), blur: 30.0, color: UIColor(white: 0.0, alpha: 0.08).cgColor)
let shadowInset: CGFloat = 32.0 context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowInset + shadowInnerInset, y: shadowInset + shadowInnerInset), size: CGSize(width: size.width - shadowInset * 2.0 - shadowInnerInset * 2.0, height: size.height - shadowInset * 2.0 - shadowInnerInset * 2.0)))
let shadowInnerInset: CGFloat = 0.5
shadowMaskView.image = generateImage(CGSize(width: shadowInset * 2.0 + cornerRadius * 2.0, height: shadowInset * 2.0 + cornerRadius * 2.0), rotatedContext: { size, context in context.setFillColor(UIColor.clear.cgColor)
context.setFillColor(UIColor.white.cgColor) context.setBlendMode(.copy)
context.fill(CGRect(origin: CGPoint(), size: size)) context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowInset + shadowInnerInset, y: shadowInset + shadowInnerInset), size: CGSize(width: size.width - shadowInset * 2.0 - shadowInnerInset * 2.0, height: size.height - shadowInset * 2.0 - shadowInnerInset * 2.0)))
})?.stretchableImage(withLeftCapWidth: Int(shadowInset + cornerRadius), topCapHeight: Int(shadowInset + cornerRadius))
context.setFillColor(UIColor.clear.cgColor)
context.setBlendMode(.copy)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowInset + shadowInnerInset, y: shadowInset + shadowInnerInset), size: CGSize(width: size.width - shadowInset * 2.0 - shadowInnerInset * 2.0, height: size.height - shadowInset * 2.0 - shadowInnerInset * 2.0)))
})?.stretchableImage(withLeftCapWidth: Int(shadowInset + cornerRadius), topCapHeight: Int(shadowInset + cornerRadius))
}
} }
if let foregroundView = self.foregroundView { if let foregroundView = self.foregroundView {
foregroundView.image = generateForegroundImage(size: CGSize(width: cornerRadius * 2.0, height: cornerRadius * 2.0), isDark: isDark, fillColor: tintColor.color) foregroundView.image = GlassBackgroundView.generateForegroundImage(size: CGSize(width: cornerRadius * 2.0, height: cornerRadius * 2.0), isDark: isDark, fillColor: tintColor.color)
} else { } else {
if let nativeView { if let nativeView {
if #available(iOS 26.0, *) { if #available(iOS 26.0, *) {
let glassEffect = UIGlassEffect(style: .regular) let glassEffect = UIGlassEffect(style: .clear)
switch tintColor.kind { switch tintColor.kind {
case .panel: case .panel:
glassEffect.tintColor = nil glassEffect.tintColor = tintColor.color
case .custom: case .custom:
glassEffect.tintColor = tintColor.color glassEffect.tintColor = tintColor.color
} }
@ -484,14 +395,7 @@ public final class GlassBackgroundView: UIView {
transition.setFrame(view: foregroundView, frame: CGRect(origin: CGPoint(), size: size)) transition.setFrame(view: foregroundView, frame: CGRect(origin: CGPoint(), size: size))
} }
if let shadowView = self.shadowView { if let shadowView = self.shadowView {
if shadowView.bounds.size != size { transition.setFrame(view: shadowView, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -shadowInset, dy: -shadowInset))
shadowView.layer.shadowPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: size.height * 0.5).cgPath
}
transition.setFrame(view: shadowView, frame: CGRect(origin: CGPoint(), size: size))
if let shadowMaskView = self.shadowMaskView {
transition.setFrame(view: shadowMaskView, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -32.0, dy: -32.0))
}
} }
transition.setFrame(view: self.contentContainer, frame: CGRect(origin: CGPoint(), size: size)) transition.setFrame(view: self.contentContainer, frame: CGRect(origin: CGPoint(), size: size))
} }
@ -585,3 +489,85 @@ public final class VariableBlurView: UIVisualEffectView {
backdropLayer?.filters = [variableBlur] backdropLayer?.filters = [variableBlur]
} }
} }
public extension GlassBackgroundView {
static func generateForegroundImage(size: CGSize, isDark: Bool, fillColor: UIColor) -> UIImage {
var size = size
if size == .zero {
size = CGSize(width: 1.0, height: 1.0)
}
return generateImage(size, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
let maxColor = UIColor(white: 1.0, alpha: isDark ? 0.25 : 0.9)
let minColor = UIColor(white: 1.0, alpha: 0.0)
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()
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))
}
}

View File

@ -407,6 +407,7 @@ final class MediaEditorScreenComponent: Component {
areCustomEmojiEnabled: true, areCustomEmojiEnabled: true,
hasSearch: true, hasSearch: true,
hideBackground: true, hideBackground: true,
maskEdge: true,
sendGif: nil sendGif: nil
) |> map { inputData -> ChatEntityKeyboardInputNode.InputData in ) |> map { inputData -> ChatEntityKeyboardInputNode.InputData in
return ChatEntityKeyboardInputNode.InputData( return ChatEntityKeyboardInputNode.InputData(

View File

@ -257,6 +257,7 @@ public final class EmojiSelectionComponent: Component {
defaultToEmojiTab: true, defaultToEmojiTab: true,
externalTopPanelContainer: self.panelHostView, externalTopPanelContainer: self.panelHostView,
externalBottomPanelContainer: nil, externalBottomPanelContainer: nil,
externalTintMaskContainer: nil,
displayTopPanelBackground: .blur, displayTopPanelBackground: .blur,
topPanelExtensionUpdated: { _, _ in }, topPanelExtensionUpdated: { _, _ in },
topPanelScrollingOffset: { _, _ in }, topPanelScrollingOffset: { _, _ in },

View File

@ -335,6 +335,7 @@ private final class EmojiSelectionComponent: Component {
defaultToEmojiTab: true, defaultToEmojiTab: true,
externalTopPanelContainer: self.panelHostView, externalTopPanelContainer: self.panelHostView,
externalBottomPanelContainer: nil, externalBottomPanelContainer: nil,
externalTintMaskContainer: nil,
displayTopPanelBackground: .blur, displayTopPanelBackground: .blur,
topPanelExtensionUpdated: { _, _ in }, topPanelExtensionUpdated: { _, _ in },
topPanelScrollingOffset: { _, _ in }, topPanelScrollingOffset: { _, _ in },

View File

@ -283,6 +283,7 @@ private final class StickerSelectionComponent: Component {
defaultToEmojiTab: defaultToEmoji, defaultToEmojiTab: defaultToEmoji,
externalTopPanelContainer: self.panelHostView, externalTopPanelContainer: self.panelHostView,
externalBottomPanelContainer: nil, externalBottomPanelContainer: nil,
externalTintMaskContainer: nil,
displayTopPanelBackground: .blur, displayTopPanelBackground: .blur,
topPanelExtensionUpdated: { _, _ in topPanelExtensionUpdated: { _, _ in
}, },

View File

@ -1768,9 +1768,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
if let inputNode = inputNode as? ChatEntityKeyboardInputNode { if let inputNode = inputNode as? ChatEntityKeyboardInputNode {
inputNode.externalTopPanelContainerImpl = nil inputNode.externalTopPanelContainerImpl = nil
} }
inputNode.clipsToBounds = true
inputNode.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
inputNode.layer.cornerRadius = 30.0
dismissedInputNode = self.inputNode dismissedInputNode = self.inputNode
dismissedInputNodeExternalTopPanelContainer = self.inputNode?.externalTopPanelContainer dismissedInputNodeExternalTopPanelContainer = self.inputNode?.externalTopPanelContainer
@ -2158,11 +2155,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
if self.dismissedAsOverlay { if self.dismissedAsOverlay {
inputPanelFrame!.origin.y = layout.size.height inputPanelFrame!.origin.y = layout.size.height
} }
if let inputNode = self.inputNode, inputNode.hideInput, !inputNode.adjustLayoutForHiddenInput { inputPanelsHeight += inputPanelSize!.height
inputPanelsHeight += inputPanelNodeBaseHeight
} else {
inputPanelsHeight += inputPanelSize!.height
}
} }
if self.secondaryInputPanelNode != nil { if self.secondaryInputPanelNode != nil {
@ -3942,6 +3935,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
chatPeerId: self.chatLocation.peerId, chatPeerId: self.chatLocation.peerId,
areCustomEmojiEnabled: self.chatPresentationInterfaceState.customEmojiAvailable, areCustomEmojiEnabled: self.chatPresentationInterfaceState.customEmojiAvailable,
hasEdit: true, hasEdit: true,
hideBackground: true,
sendGif: { [weak self] fileReference, sourceView, sourceRect, silentPosting, schedule in sendGif: { [weak self] fileReference, sourceView, sourceRect, silentPosting, schedule in
if let self { if let self {
return self.controllerInteraction.sendGif(fileReference, sourceView, sourceRect, silentPosting, schedule) return self.controllerInteraction.sendGif(fileReference, sourceView, sourceRect, silentPosting, schedule)
@ -4235,15 +4229,20 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
} }
var maybeDismissOverlayContent = true var maybeDismissOverlayContent = true
if let inputNode = self.inputNode, inputNode.bounds.contains(self.view.convert(point, to: inputNode.view)) { if let inputNode = self.inputNode {
if let externalTopPanelContainer = inputNode.externalTopPanelContainer { if let result = inputNode.view.hitTest(self.view.convert(point, to: inputNode.view), with: event) {
if externalTopPanelContainer.hitTest(self.view.convert(point, to: externalTopPanelContainer), with: nil) != nil { return result
maybeDismissOverlayContent = true }
if inputNode.bounds.contains(self.view.convert(point, to: inputNode.view)) {
if let externalTopPanelContainer = inputNode.externalTopPanelContainer {
if externalTopPanelContainer.hitTest(self.view.convert(point, to: externalTopPanelContainer), with: nil) != nil {
maybeDismissOverlayContent = true
} else {
maybeDismissOverlayContent = false
}
} else { } else {
maybeDismissOverlayContent = false maybeDismissOverlayContent = false
} }
} else {
maybeDismissOverlayContent = false
} }
} }

View File

@ -94,7 +94,7 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
self.buttonNode.view.addSubview(self.backgroundView) self.buttonNode.view.addSubview(self.backgroundView)
self.backgroundView.frame = CGRect(origin: CGPoint(), size: size) self.backgroundView.frame = CGRect(origin: CGPoint(), size: size)
self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65)), transition: .immediate) self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: .immediate)
self.imageView.tintColor = theme.chat.inputPanel.inputControlColor self.imageView.tintColor = theme.chat.inputPanel.inputControlColor
self.backgroundView.contentView.addSubview(self.imageView) self.backgroundView.contentView.addSubview(self.imageView)
@ -110,7 +110,7 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
if self.theme !== theme { if self.theme !== theme {
self.theme = theme self.theme = theme
self.backgroundView.update(size: self.backgroundView.bounds.size, cornerRadius: self.backgroundView.bounds.size.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65)), transition: .immediate) self.backgroundView.update(size: self.backgroundView.bounds.size, cornerRadius: self.backgroundView.bounds.size.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: .immediate)
self.imageView.tintColor = theme.chat.inputPanel.inputControlColor self.imageView.tintColor = theme.chat.inputPanel.inputControlColor
switch self.type { switch self.type {