diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index cbdc55b4a2..a5b8eefe16 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -1573,6 +1573,56 @@ public extension ContainedViewLayoutTransition { } } + func updateLineWidth(layer: CAShapeLayer, lineWidth: CGFloat, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) { + if layer.lineWidth == lineWidth { + completion?(true) + return + } + + switch self { + case .immediate: + layer.removeAnimation(forKey: "lineWidth") + layer.lineWidth = lineWidth + if let completion = completion { + completion(true) + } + case let .animated(duration, curve): + let fromLineWidth = layer.lineWidth + layer.lineWidth = lineWidth + layer.animate(from: fromLineWidth as NSNumber, to: lineWidth as NSNumber, keyPath: "lineWidth", timingFunction: curve.timingFunction, duration: duration, delay: delay, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: { + result in + if let completion = completion { + completion(result) + } + }) + } + } + + func updateStrokeColor(layer: CAShapeLayer, strokeColor: UIColor, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) { + if layer.strokeColor.flatMap(UIColor.init(cgColor:)) == strokeColor { + completion?(true) + return + } + + switch self { + case .immediate: + layer.removeAnimation(forKey: "strokeColor") + layer.strokeColor = strokeColor.cgColor + if let completion = completion { + completion(true) + } + case let .animated(duration, curve): + let fromStrokeColor = layer.strokeColor ?? UIColor.clear.cgColor + layer.strokeColor = strokeColor.cgColor + layer.animate(from: fromStrokeColor, to: strokeColor.cgColor, keyPath: "strokeColor", timingFunction: curve.timingFunction, duration: duration, delay: delay, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: { + result in + if let completion = completion { + completion(result) + } + }) + } + } + func attachAnimation(view: UIView, id: String, completion: @escaping (Bool) -> Void) { switch self { case .immediate: diff --git a/submodules/Display/Source/GenerateImage.swift b/submodules/Display/Source/GenerateImage.swift index 65deb106b9..36c9486c3e 100644 --- a/submodules/Display/Source/GenerateImage.swift +++ b/submodules/Display/Source/GenerateImage.swift @@ -925,3 +925,68 @@ public func drawSvgPath(_ context: CGContext, path: StaticString, strokeOnMove: } } } + +public func convertSvgPath(_ path: StaticString) throws -> CGPath { + var index: UnsafePointer = path.utf8Start + let end = path.utf8Start.advanced(by: path.utf8CodeUnitCount) + var currentPoint = CGPoint() + + let result = CGMutablePath() + + while index < end { + let c = index.pointee + index = index.successor() + + if c == 77 { // M + let x = try readCGFloat(&index, end: end, separator: 44) + let y = try readCGFloat(&index, end: end, separator: 32) + + //print("Move to \(x), \(y)") + currentPoint = CGPoint(x: x, y: y) + result.move(to: currentPoint) + } else if c == 76 { // L + let x = try readCGFloat(&index, end: end, separator: 44) + let y = try readCGFloat(&index, end: end, separator: 32) + + //print("Line to \(x), \(y)") + currentPoint = CGPoint(x: x, y: y) + result.addLine(to: currentPoint) + } else if c == 72 { // H + let x = try readCGFloat(&index, end: end, separator: 32) + + //print("Move to \(x), \(y)") + currentPoint = CGPoint(x: x, y: currentPoint.y) + result.addLine(to: currentPoint) + } else if c == 86 { // V + let y = try readCGFloat(&index, end: end, separator: 32) + + //print("Move to \(x), \(y)") + currentPoint = CGPoint(x: currentPoint.x, y: y) + result.addLine(to: currentPoint) + } else if c == 67 { // C + let x1 = try readCGFloat(&index, end: end, separator: 44) + let y1 = try readCGFloat(&index, end: end, separator: 32) + let x2 = try readCGFloat(&index, end: end, separator: 44) + let y2 = try readCGFloat(&index, end: end, separator: 32) + let x = try readCGFloat(&index, end: end, separator: 44) + let y = try readCGFloat(&index, end: end, separator: 32) + + currentPoint = CGPoint(x: x, y: y) + result.addCurve(to: currentPoint, control1: CGPoint(x: x1, y: y1), control2: CGPoint(x: x2, y: y2)) + } else if c == 90 { // Z + if index != end && index.pointee != 32 { + throw ParsingError.Generic + } + } else if c == 83 { // S + if index != end && index.pointee != 32 { + throw ParsingError.Generic + } + } else if c == 32 { // space + continue + } else { + throw ParsingError.Generic + } + } + + return result +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift index a3a0913996..eca8b8c05e 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift @@ -97,6 +97,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { let contextSourceNode: ContextReferenceContentNode private let textNode: ImmediateTextNode private let iconNode: ASImageNode + private let backIconLayer: SimpleShapeLayer private var animationNode: MoreIconNode? private let backgroundNode: NavigationBackgroundNode @@ -117,6 +118,15 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { self.iconNode.displaysAsynchronously = false self.iconNode.displayWithoutProcessing = true + self.backIconLayer = SimpleShapeLayer() + self.backIconLayer.lineWidth = 3.0 + self.backIconLayer.lineCap = .round + self.backIconLayer.lineJoin = .round + self.backIconLayer.strokeColor = UIColor.white.cgColor + self.backIconLayer.fillColor = nil + self.backIconLayer.isHidden = true + self.backIconLayer.path = try? convertSvgPath("M10.5,2 L1.5,11 L10.5,20 ") + self.backgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: true) super.init(pointerStyle: .insetRectangle(-8.0, 2.0)) @@ -128,6 +138,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { self.contextSourceNode.addSubnode(self.backgroundNode) self.contextSourceNode.addSubnode(self.textNode) self.contextSourceNode.addSubnode(self.iconNode) + self.contextSourceNode.layer.addSublayer(self.backIconLayer) self.addSubnode(self.containerNode) @@ -146,13 +157,43 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { self.action?(self.contextSourceNode, nil) } - func updateContentsColor(backgroundColor: UIColor, contentsColor: UIColor, transition: ContainedViewLayoutTransition) { + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + var boundingRect = self.bounds + if self.textNode.alpha != 0.0 { + boundingRect = boundingRect.union(self.textNode.frame) + } + boundingRect = boundingRect.insetBy(dx: -8.0, dy: -4.0) + if boundingRect.contains(point) { + return super.hitTest(self.bounds.center, with: event) + } else { + return nil + } + } + + func updateContentsColor(backgroundColor: UIColor, contentsColor: UIColor, canBeExpanded: Bool, transition: ContainedViewLayoutTransition) { self.contentsColor = contentsColor self.backgroundNode.updateColor(color: backgroundColor, transition: transition) transition.updateTintColor(layer: self.textNode.layer, color: self.contentsColor) transition.updateTintColor(layer: self.iconNode.layer, color: self.contentsColor) + transition.updateStrokeColor(layer: self.backIconLayer, strokeColor: self.contentsColor) + + switch self.key { + case .back: + transition.updateAlpha(layer: self.textNode.layer, alpha: canBeExpanded ? 1.0 : 0.0) + transition.updateTransformScale(node: self.textNode, scale: canBeExpanded ? 1.0 : 0.001) + + var iconTransform = CATransform3DIdentity + iconTransform = CATransform3DScale(iconTransform, canBeExpanded ? 1.0 : 0.8, canBeExpanded ? 1.0 : 0.8, 1.0) + iconTransform = CATransform3DTranslate(iconTransform, canBeExpanded ? -7.0 : 0.0, 0.0, 0.0) + transition.updateTransform(node: self.iconNode, transform: CATransform3DGetAffineTransform(iconTransform)) + + transition.updateTransform(layer: self.backIconLayer, transform: CATransform3DGetAffineTransform(iconTransform)) + transition.updateLineWidth(layer: self.backIconLayer, lineWidth: canBeExpanded ? 3.0 : 2.075) + default: + break + } if let animationNode = self.animationNode { transition.updateTintColor(layer: animationNode.imageNode.layer, color: self.contentsColor) @@ -184,9 +225,9 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { var animationState: MoreIconNodeState = .more switch key { case .back: - text = "" + text = presentationData.strings.Common_Back accessibilityText = presentationData.strings.Common_Back - icon = NavigationBar.thinBackArrowImage + icon = NavigationBar.backArrowImage(color: .white) case .edit: text = presentationData.strings.Common_Edit accessibilityText = text @@ -270,11 +311,19 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { } let inset: CGFloat = 0.0 + var textInset: CGFloat = 0.0 + switch key { + case .back: + textInset += 11.0 + default: + break + } let resultSize: CGSize - let textFrame = CGRect(origin: CGPoint(x: inset, y: floor((height - textSize.height) / 2.0)), size: textSize) - self.textNode.frame = textFrame + let textFrame = CGRect(origin: CGPoint(x: inset + textInset, y: floor((height - textSize.height) / 2.0)), size: textSize) + self.textNode.position = textFrame.center + self.textNode.bounds = CGRect(origin: CGPoint(), size: textFrame.size) if let animationNode = self.animationNode { let animationSize = CGSize(width: 30.0, height: 30.0) @@ -286,7 +335,20 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size) resultSize = size } else if let image = self.iconNode.image { - self.iconNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((height - image.size.height) / 2.0)), size: image.size).offsetBy(dx: iconOffset.x, dy: iconOffset.y) + let iconFrame = CGRect(origin: CGPoint(x: inset, y: floor((height - image.size.height) / 2.0)), size: image.size).offsetBy(dx: iconOffset.x, dy: iconOffset.y) + self.iconNode.position = iconFrame.center + self.iconNode.bounds = CGRect(origin: CGPoint(), size: iconFrame.size) + + if case .back = key { + self.backIconLayer.position = iconFrame.center + self.backIconLayer.bounds = CGRect(origin: CGPoint(), size: iconFrame.size) + + self.iconNode.isHidden = true + self.backIconLayer.isHidden = false + } else { + self.iconNode.isHidden = false + self.backIconLayer.isHidden = true + } let size = CGSize(width: image.size.width + inset * 2.0, height: height) self.containerNode.frame = CGRect(origin: CGPoint(), size: size) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift index c12dd1e360..18b3dd3f19 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift @@ -36,18 +36,21 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { private var backgroundContentColor: UIColor = .clear private var contentsColor: UIColor = .white + private var canBeExpanded: Bool = false var performAction: ((PeerInfoHeaderNavigationButtonKey, ContextReferenceContentNode?, ContextGesture?) -> Void)? - func updateContentsColor(backgroundContentColor: UIColor, contentsColor: UIColor, transition: ContainedViewLayoutTransition) { + func updateContentsColor(backgroundContentColor: UIColor, contentsColor: UIColor, canBeExpanded: Bool, transition: ContainedViewLayoutTransition) { self.backgroundContentColor = backgroundContentColor self.contentsColor = contentsColor for (_, button) in self.leftButtonNodes { - button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, transition: transition) + button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: canBeExpanded, transition: transition) + transition.updateSublayerTransformOffset(layer: button.layer, offset: CGPoint(x: canBeExpanded ? -8.0 : 0.0, y: 0.0)) } for (_, button) in self.rightButtonNodes { - button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, transition: transition) + button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: canBeExpanded, transition: transition) + transition.updateSublayerTransformOffset(layer: button.layer, offset: CGPoint(x: canBeExpanded ? 8.0 : 0.0, y: 0.0)) } } @@ -106,7 +109,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { buttonNode.frame = buttonFrame buttonNode.alpha = 0.0 transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) - buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, transition: .immediate) + buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: self.canBeExpanded, transition: .immediate) } else { transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) @@ -202,7 +205,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { } let alphaFactor: CGFloat = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) if wasAdded { - buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, transition: .immediate) + buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: self.canBeExpanded, transition: .immediate) if key == .moreToSearch { buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index c6bb1f3716..18af6ed860 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -553,6 +553,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { let navigationContentsAccentColor: UIColor let navigationContentsPrimaryColor: UIColor let navigationContentsSecondaryColor: UIColor + let navigationContentsCanBeExpanded: Bool let contentButtonBackgroundColor: UIColor let contentButtonForegroundColor: UIColor @@ -640,6 +641,8 @@ final class PeerInfoHeaderNode: ASDisplayNode { navigationContentsAccentColor = collapsedHeaderNavigationContentsAccentColor navigationContentsPrimaryColor = collapsedHeaderNavigationContentsPrimaryColor navigationContentsSecondaryColor = collapsedHeaderNavigationContentsSecondaryColor + navigationContentsCanBeExpanded = true + contentButtonBackgroundColor = collapsedHeaderContentButtonBackgroundColor contentButtonForegroundColor = collapsedHeaderContentButtonForegroundColor @@ -651,6 +654,8 @@ final class PeerInfoHeaderNode: ASDisplayNode { contentButtonBackgroundColor = expandedAvatarContentButtonBackgroundColor contentButtonForegroundColor = expandedAvatarContentButtonForegroundColor + navigationContentsCanBeExpanded = false + headerButtonBackgroundColor = expandedAvatarHeaderButtonBackgroundColor } else { let effectiveTransitionFraction: CGFloat = innerBackgroundTransitionFraction < 0.5 ? 0.0 : 1.0 @@ -659,6 +664,12 @@ final class PeerInfoHeaderNode: ASDisplayNode { navigationContentsPrimaryColor = regularNavigationContentsPrimaryColor.mixedWith(collapsedHeaderNavigationContentsPrimaryColor, alpha: effectiveTransitionFraction) navigationContentsSecondaryColor = regularNavigationContentsSecondaryColor.mixedWith(collapsedHeaderNavigationContentsSecondaryColor, alpha: effectiveTransitionFraction) + if peer?.profileColor != nil { + navigationContentsCanBeExpanded = effectiveTransitionFraction == 1.0 + } else { + navigationContentsCanBeExpanded = true + } + contentButtonBackgroundColor = regularContentButtonBackgroundColor//.mixedWith(collapsedHeaderContentButtonBackgroundColor, alpha: effectiveTransitionFraction) contentButtonForegroundColor = regularContentButtonForegroundColor//.mixedWith(collapsedHeaderContentButtonForegroundColor, alpha: effectiveTransitionFraction) @@ -775,7 +786,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.titleExpandedCredibilityIconSize = expandedIconSize } - self.navigationButtonContainer.updateContentsColor(backgroundContentColor: headerButtonBackgroundColor, contentsColor: navigationContentsAccentColor, transition: navigationTransition) + self.navigationButtonContainer.updateContentsColor(backgroundContentColor: headerButtonBackgroundColor, contentsColor: navigationContentsAccentColor, canBeExpanded: navigationContentsCanBeExpanded, transition: navigationTransition) self.titleNode.updateTintColor(color: navigationContentsPrimaryColor, transition: navigationTransition) self.subtitleNode.updateTintColor(color: navigationContentsSecondaryColor, transition: navigationTransition)