From 5728de8df8466932acec3903e20b8f359fdd87f3 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sun, 28 Sep 2025 15:01:52 +0800 Subject: [PATCH] Glass --- .../Sources/ChatTextInputPanelNode.swift | 65 +++++--- .../Sources/GlassBackgroundComponent.swift | 157 ++++++++++++------ .../Sources/ChatControllerNode.swift | 8 +- 3 files changed, 157 insertions(+), 73 deletions(-) diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index b6533f1f02..2c1c3e2052 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -66,7 +66,10 @@ public let chatTextInputMinFontSize: CGFloat = 5.0 private let minInputFontSize = chatTextInputMinFontSize private func calclulateTextFieldMinHeight(_ presentationInterfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { - let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + var baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + if "".isEmpty { + baseFontSize = 17.0 + } var result: CGFloat if baseFontSize.isEqual(to: 26.0) { result = 42.0 @@ -82,15 +85,14 @@ private func calclulateTextFieldMinHeight(_ presentationInterfaceState: ChatPres result = 31.0 } - if case .regular = metrics.widthClass { - result = max(33.0, result) - } - return result } private func calculateTextFieldRealInsets(presentationInterfaceState: ChatPresentationInterfaceState, accessoryButtonsWidth: CGFloat) -> UIEdgeInsets { - let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + var baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + if "".isEmpty { + baseFontSize = 17.0 + } let top: CGFloat let bottom: CGFloat if baseFontSize.isEqual(to: 14.0) { @@ -452,6 +454,9 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) } + if "".isEmpty { + baseFontSize = 17.0 + } textInputNode.attributedText = NSAttributedString(string: value, font: Font.regular(baseFontSize), textColor: textColor) self.chatInputTextNodeDidUpdateText() } @@ -1038,7 +1043,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg var accessoryButtonsWidth: CGFloat = 0.0 var firstButton = true - for (_, button) in self.accessoryItemButtons { + for (item, button) in self.accessoryItemButtons { if firstButton { firstButton = false accessoryButtonsWidth += self.accessoryButtonInset @@ -1046,6 +1051,12 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg accessoryButtonsWidth += self.accessoryButtonSpacing } accessoryButtonsWidth += button.buttonWidth + switch item { + case .input: + accessoryButtonsWidth -= 2.0 + default: + break + } } var textFieldMinHeight: CGFloat = 35.0 @@ -1081,11 +1092,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg } private func textFieldInsets(metrics: LayoutMetrics) -> UIEdgeInsets { - var insets = UIEdgeInsets(top: 0.0, left: 54.0, bottom: 0.0, right: 54.0) - if case .regular = metrics.widthClass, case .regular = metrics.heightClass { - insets.top += 1.0 - insets.bottom += 1.0 - } + let insets = UIEdgeInsets(top: 0.0, left: 54.0, bottom: 0.0, right: 54.0) return insets } @@ -1097,10 +1104,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg override public func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { let textFieldMinHeight = calclulateTextFieldMinHeight(interfaceState, metrics: metrics) - var minimalHeight: CGFloat = 14.0 + textFieldMinHeight - if case .regular = metrics.widthClass, case .regular = metrics.heightClass { - minimalHeight += 2.0 - } + let minimalHeight: CGFloat = 14.0 + textFieldMinHeight return minimalHeight } @@ -1563,7 +1567,10 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg if self.theme == nil || !self.theme!.chat.inputPanel.inputTextColor.isEqual(interfaceState.theme.chat.inputPanel.inputTextColor) { let textColor = interfaceState.theme.chat.inputPanel.inputTextColor - let baseFontSize = max(minInputFontSize, interfaceState.fontSize.baseDisplaySize) + var baseFontSize = max(minInputFontSize, interfaceState.fontSize.baseDisplaySize) + if "".isEmpty { + baseFontSize = 17.0 + } if let textInputNode = self.textInputNode { if let text = textInputNode.attributedText { @@ -2505,6 +2512,12 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg transition.updateAlpha(layer: button.layer, alpha: audioRecordingItemsAlpha) nextButtonTopRight.x -= buttonSize.width nextButtonTopRight.x -= accessoryButtonSpacing + switch item.key { + case .input: + nextButtonTopRight.x += 2.0 + default: + break + } } let textInputBackgroundFrame = CGRect(x: hideOffset.x + leftInset + textFieldInsets.left, y: hideOffset.y + textFieldInsets.top + textFieldTopContentOffset, width: baseWidth - textFieldInsets.left - textFieldInsets.right, height: panelHeight - textFieldInsets.top - textFieldInsets.bottom + self.textInputViewInternalInsets.top + self.textInputViewInternalInsets.bottom) @@ -2518,7 +2531,10 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg if (updatedPlaceholder != nil && self.currentPlaceholder != updatedPlaceholder) || themeUpdated { let currentPlaceholder = updatedPlaceholder ?? self.currentPlaceholder ?? "" self.currentPlaceholder = currentPlaceholder - let baseFontSize = max(minInputFontSize, interfaceState.fontSize.baseDisplaySize) + var baseFontSize = max(minInputFontSize, interfaceState.fontSize.baseDisplaySize) + if "".isEmpty { + baseFontSize = 17.0 + } let attributedPlaceholder = NSMutableAttributedString(string: currentPlaceholder, font: Font.regular(baseFontSize), textColor: placeholderColor.withAlphaComponent(1.0)) if placeholderHasStar, let range = attributedPlaceholder.string.range(of: "#") { @@ -2923,7 +2939,10 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg var rects: [CGRect] = [] var customEmojiRects: [(CGRect, ChatTextInputTextCustomEmojiAttribute, CGFloat)] = [] - let fontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + var fontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + if "".isEmpty { + fontSize = 17.0 + } if let attributedText = textInputNode.attributedText { let beginning = textInputNode.textView.beginningOfDocument @@ -3949,7 +3968,10 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg self.inputMenu.hide() } - let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + var baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + if "".isEmpty { + baseFontSize = 17.0 + } refreshChatTextInputTypingAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize) self.updateSpoilersRevealed() @@ -4338,6 +4360,9 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + if "".isEmpty { + baseFontSize = 17.0 + } } let cleanReplacementString = textAttributedStringForStateText(context: context, stateText: NSAttributedString(string: cleanText), fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) diff --git a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift index 1dc26e7f38..709950713a 100644 --- a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift +++ b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift @@ -577,7 +577,9 @@ public extension GlassBackgroundView { size.width += inset * 2.0 size.height += inset * 2.0 - return generateImage(size, rotatedContext: { size, context in + return UIGraphicsImageRenderer(size: size).image { ctx in + let context = ctx.cgContext + context.clear(CGRect(origin: CGPoint(), size: size)) func pathApplyingSpread(_ path: CGPath, spread: CGFloat) -> CGPath { @@ -606,8 +608,14 @@ public extension GlassBackgroundView { } return result } + + let maskImage = UIGraphicsImageRenderer(size: size).image { ctx in + let context = ctx.cgContext + context.setFillColor(UIColor.black.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize)) + } - let addShadow: (Bool, CGPoint, CGFloat, CGFloat, UIColor, Bool) -> Void = { isOuter, position, blur, spread, shadowColor, isMultiply in + let addShadow: (Bool, CGPoint, CGFloat, CGFloat, UIColor, CGBlendMode) -> Void = { isOuter, position, blur, spread, shadowColor, blendMode in var blur = blur blur += abs(spread) @@ -641,72 +649,119 @@ public extension GlassBackgroundView { context.fillPath() context.setBlendMode(.normal) } else { - if let image = generateImage(size, rotatedContext: { size, context in + let image = UIGraphicsImageRenderer(size: size).image(actions: { ctx in + let context = ctx.cgContext + context.clear(CGRect(origin: CGPoint(), size: size)) - let spreadRect = CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize).insetBy(dx: -0.25, dy: -0.25) + let spreadRect = CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize).insetBy(dx: -spread - 1.0, dy: -spread - 1.0) 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) + context.setFillColor(UIColor.red.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() - } + maskImage.draw(at: CGPoint(), blendMode: .destinationIn, alpha: 1.0) + }) + + UIGraphicsPushContext(context) + image.draw(in: CGRect(origin: .zero, size: size), blendMode: blendMode, alpha: 1.0) + UIGraphicsPopContext() } } - if isDark { - 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), false) - - context.setFillColor(fillColor.cgColor) - 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.25), false) - addShadow(false, CGPoint(x: 2.0, y: -2.0), 1.0, 0.0, UIColor(white: 1.0, alpha: 0.125), false) - addShadow(false, CGPoint(x: -2.0, y: 2.0), 1.0, 0.0, UIColor(white: 1.0, alpha: 0.125), false) - } else { - addShadow(true, CGPoint(), 16.0, 0.0, UIColor(white: 0.0, alpha: 0.08), false) - - context.setFillColor(fillColor.cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset)) - - let highlightColor: UIColor - if fillColor.hsb.s > 0.5 { - var (h, s, v) = fillColor.hsb - s = max(0.0, min(1.0, s * 0.25)) - v = max(v, 0.95) - h = max(0.0, min(1.0, h - 0.1)) - - highlightColor = UIColor(hue: h, saturation: s, brightness: v, alpha: fillColor.alpha) - } else { - highlightColor = UIColor(white: 1.0, alpha: min(1.0, fillColor.alpha * 1.2)) + addShadow(true, CGPoint(), 16.0, 0.0, UIColor(white: 0.0, alpha: 0.08), .normal) + + context.setFillColor(fillColor.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset).insetBy(dx: 0.1, dy: 0.1)) + + /* + case normal = 0 + + case multiply = 1 + + case screen = 2 + + case overlay = 3 + + case darken = 4 + + case lighten = 5 + + case colorDodge = 6 + + case colorBurn = 7 + + case softLight = 8 + + case hardLight = 9 + + case difference = 10 + + case exclusion = 11 + + case hue = 12 + + case saturation = 13 + + case color = 14 + + case luminosity = 15 + + case clear = 16 + + case copy = 17 + + case sourceIn = 18 + + case sourceOut = 19 + + case sourceAtop = 20 + + case destinationOver = 21 + + case destinationIn = 22 + + case destinationOut = 23 + + case destinationAtop = 24 + + case xor = 25 + + case plusDarker = 26 + + case plusLighter = 27 + */ + + var a: CGFloat = 0.0 + var b: CGFloat = 0.0 + fillColor.getHue(nil, saturation: nil, brightness: &b, alpha: &a) + + addShadow(true, CGPoint(x: 0.0, y: 0.0), 20.0, 0.0, UIColor(white: 0.0, alpha: 0.04), .normal) + addShadow(true, CGPoint(x: 0.0, y: 0.0), 5.0, 0.0, UIColor(white: 0.0, alpha: 0.04), .normal) + + let edgeWidth: CGFloat = 0.5 + let edgeAlpha: CGFloat = max(0.2, min(0.8, a * a * a)) + + if b >= 0.2 { + for _ in 0 ..< 3 { + addShadow(false, CGPoint(x: 0.0, y: 0.0), 1.0, 0.0, UIColor(white: 1.0, alpha: edgeAlpha), .overlay) + addShadow(false, CGPoint(x: edgeWidth, y: edgeWidth), 1.4, 0.0, UIColor(white: 1.0, alpha: edgeAlpha * 0.9), .overlay) + addShadow(false, CGPoint(x: -edgeWidth, y: -edgeWidth), 1.4, 0.0, UIColor(white: 1.0, alpha: edgeAlpha * 0.9), .overlay) + } + } else { + for _ in 0 ..< 3 { + addShadow(false, CGPoint(x: 0.0, y: 0.0), 1.0, 0.0, UIColor(white: 1.0, alpha: edgeAlpha), .normal) + addShadow(false, CGPoint(x: edgeWidth, y: edgeWidth), 1.4, 0.0, UIColor(white: 1.0, alpha: edgeAlpha * 0.9), .normal) + addShadow(false, CGPoint(x: -edgeWidth, y: -edgeWidth), 1.4, 0.0, UIColor(white: 1.0, alpha: edgeAlpha * 0.9), .normal) } - - addShadow(false, CGPoint(x: 2.0, y: -2.0), 1.0, 0.0, highlightColor, false) - addShadow(false, CGPoint(x: -2.0, y: 2.0), 1.0, 0.0, highlightColor, false) } - })!.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)) } static func generateForegroundImage(size: CGSize, isDark: Bool, fillColor: UIColor) -> UIImage { diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index e4ee18890e..fa1c971e6d 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -3784,11 +3784,15 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } self.searchNavigationNode?.deactivate() - self.view.window?.endEditing(true) + if let firstResponder = self.view.window?.findFirstResponder() { + firstResponder.resignFirstResponder() + } } func dismissTextInput() { - self.view.window?.endEditing(true) + if let firstResponder = self.view.window?.findFirstResponder() { + firstResponder.resignFirstResponder() + } } func collapseInput() {