[WIP] MultiScaleTextNode

This commit is contained in:
Ali
2022-12-13 22:20:27 +04:00
parent fc24724e2e
commit 1589f86235
2 changed files with 77 additions and 55 deletions

View File

@@ -19,11 +19,16 @@ private final class MultiScaleTextStateNode: ASDisplayNode {
} }
final class MultiScaleTextState { final class MultiScaleTextState {
let attributedText: NSAttributedString struct Attributes {
var font: UIFont
var color: UIColor
}
let attributes: Attributes
let constrainedSize: CGSize let constrainedSize: CGSize
init(attributedText: NSAttributedString, constrainedSize: CGSize) { init(attributes: Attributes, constrainedSize: CGSize) {
self.attributedText = attributedText self.attributes = attributes
self.constrainedSize = constrainedSize self.constrainedSize = constrainedSize
} }
} }
@@ -49,7 +54,7 @@ final class MultiScaleTextNode: ASDisplayNode {
return self.stateNodes[key]?.textNode return self.stateNodes[key]?.textNode
} }
func updateLayout(states: [AnyHashable: MultiScaleTextState], mainState: AnyHashable) -> [AnyHashable: MultiScaleTextLayout] { func updateLayout(text: String, states: [AnyHashable: MultiScaleTextState], mainState: AnyHashable) -> [AnyHashable: MultiScaleTextLayout] {
assert(Set(states.keys) == Set(self.stateNodes.keys)) assert(Set(states.keys) == Set(self.stateNodes.keys))
assert(states[mainState] != nil) assert(states[mainState] != nil)
@@ -57,7 +62,7 @@ final class MultiScaleTextNode: ASDisplayNode {
var mainLayout: MultiScaleTextLayout? var mainLayout: MultiScaleTextLayout?
for (key, state) in states { for (key, state) in states {
if let node = self.stateNodes[key] { if let node = self.stateNodes[key] {
node.textNode.attributedText = state.attributedText node.textNode.attributedText = NSAttributedString(string: text, font: state.attributes.font, textColor: state.attributes.color)
let nodeSize = node.textNode.updateLayout(state.constrainedSize) let nodeSize = node.textNode.updateLayout(state.constrainedSize)
let nodeLayout = MultiScaleTextLayout(size: nodeSize) let nodeLayout = MultiScaleTextLayout(size: nodeSize)
if key == mainState { if key == mainState {

View File

@@ -2645,14 +2645,16 @@ final class PeerInfoHeaderNode: ASDisplayNode {
var isPremium = false var isPremium = false
var isVerified = false var isVerified = false
var isFake = false var isFake = false
let smallTitleString: NSAttributedString let titleStringText: String
let titleString: NSAttributedString let smallTitleAttributes: MultiScaleTextState.Attributes
let smallSubtitleString: NSAttributedString let titleAttributes: MultiScaleTextState.Attributes
let subtitleString: NSAttributedString let subtitleStringText: String
let smallSubtitleAttributes: MultiScaleTextState.Attributes
let subtitleAttributes: MultiScaleTextState.Attributes
var subtitleIsButton: Bool = false var subtitleIsButton: Bool = false
var panelSubtitleString: NSAttributedString? var panelSubtitleString: (text: String, attributes: MultiScaleTextState.Attributes)?
var nextPanelSubtitleString: NSAttributedString? var nextPanelSubtitleString: (text: String, attributes: MultiScaleTextState.Attributes)?
let usernameString: NSAttributedString let usernameString: (text: String, attributes: MultiScaleTextState.Attributes)
if let peer = peer { if let peer = peer {
isPremium = peer.isPremium isPremium = peer.isPremium
isVerified = peer.isVerified isVerified = peer.isVerified
@@ -2681,17 +2683,21 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} }
} }
titleString = NSAttributedString(string: title, font: Font.regular(30.0), textColor: presentationData.theme.list.itemPrimaryTextColor) titleStringText = title
smallTitleString = NSAttributedString(string: title, font: Font.regular(30.0), textColor: .white) titleAttributes = MultiScaleTextState.Attributes(font: Font.regular(30.0), color: presentationData.theme.list.itemPrimaryTextColor)
smallTitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(30.0), color: .white)
if self.isSettings, let user = peer as? TelegramUser { if self.isSettings, let user = peer as? TelegramUser {
var subtitle = formatPhoneNumber(context: self.context, number: user.phone ?? "") var subtitle = formatPhoneNumber(context: self.context, number: user.phone ?? "")
if let mainUsername = user.addressName, !mainUsername.isEmpty { if let mainUsername = user.addressName, !mainUsername.isEmpty {
subtitle = "\(subtitle) • @\(mainUsername)" subtitle = "\(subtitle) • @\(mainUsername)"
} }
smallSubtitleString = NSAttributedString(string: subtitle, font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7)) subtitleStringText = subtitle
subtitleString = NSAttributedString(string: subtitle, font: Font.regular(17.0), textColor: presentationData.theme.list.itemSecondaryTextColor) subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(17.0), color: presentationData.theme.list.itemSecondaryTextColor)
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor)
usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor))
} else if let _ = threadData { } else if let _ = threadData {
let subtitleColor: UIColor let subtitleColor: UIColor
subtitleColor = presentationData.theme.list.itemAccentColor subtitleColor = presentationData.theme.list.itemAccentColor
@@ -2699,9 +2705,11 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let statusText: String let statusText: String
statusText = peer.debugDisplayTitle statusText = peer.debugDisplayTitle
smallSubtitleString = NSAttributedString(string: statusText, font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7)) subtitleStringText = statusText
subtitleString = NSAttributedString(string: statusText, font: Font.semibold(15.0), textColor: subtitleColor) subtitleAttributes = MultiScaleTextState.Attributes(font: Font.semibold(15.0), color: subtitleColor)
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: UIColor(white: 1.0, alpha: 0.7))
usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor))
subtitleIsButton = true subtitleIsButton = true
@@ -2713,10 +2721,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} else { } else {
subtitleColor = presentationData.theme.list.itemSecondaryTextColor subtitleColor = presentationData.theme.list.itemSecondaryTextColor
} }
panelSubtitleString = NSAttributedString(string: panelStatusData.text, font: Font.regular(17.0), textColor: subtitleColor) panelSubtitleString = (panelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: subtitleColor))
} }
if let nextPanelStatusData = maybeNextPanelStatusData { if let nextPanelStatusData = maybeNextPanelStatusData {
nextPanelSubtitleString = NSAttributedString(string: nextPanelStatusData.text, font: Font.regular(17.0), textColor: presentationData.theme.list.itemSecondaryTextColor) nextPanelSubtitleString = (nextPanelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: presentationData.theme.list.itemSecondaryTextColor))
} }
} else if let statusData = statusData { } else if let statusData = statusData {
let subtitleColor: UIColor let subtitleColor: UIColor
@@ -2725,9 +2733,12 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} else { } else {
subtitleColor = presentationData.theme.list.itemSecondaryTextColor subtitleColor = presentationData.theme.list.itemSecondaryTextColor
} }
smallSubtitleString = NSAttributedString(string: statusData.text, font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7))
subtitleString = NSAttributedString(string: statusData.text, font: Font.regular(17.0), textColor: subtitleColor) subtitleStringText = statusData.text
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(17.0), color: subtitleColor)
smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: UIColor(white: 1.0, alpha: 0.7))
usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor))
let (maybePanelStatusData, maybeNextPanelStatusData, _) = panelStatusData let (maybePanelStatusData, maybeNextPanelStatusData, _) = panelStatusData
if let panelStatusData = maybePanelStatusData { if let panelStatusData = maybePanelStatusData {
@@ -2737,22 +2748,28 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} else { } else {
subtitleColor = presentationData.theme.list.itemSecondaryTextColor subtitleColor = presentationData.theme.list.itemSecondaryTextColor
} }
panelSubtitleString = NSAttributedString(string: panelStatusData.text, font: Font.regular(17.0), textColor: subtitleColor) panelSubtitleString = (panelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: subtitleColor))
} }
if let nextPanelStatusData = maybeNextPanelStatusData { if let nextPanelStatusData = maybeNextPanelStatusData {
nextPanelSubtitleString = NSAttributedString(string: nextPanelStatusData.text, font: Font.regular(17.0), textColor: presentationData.theme.list.itemSecondaryTextColor) nextPanelSubtitleString = (nextPanelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: presentationData.theme.list.itemSecondaryTextColor))
} }
} else { } else {
subtitleString = NSAttributedString(string: " ", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) subtitleStringText = " "
smallSubtitleString = subtitleString subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor)
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor)
usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor))
} }
} else { } else {
titleString = NSAttributedString(string: " ", font: Font.semibold(24.0), textColor: presentationData.theme.list.itemPrimaryTextColor) titleStringText = " "
smallTitleString = titleString titleAttributes = MultiScaleTextState.Attributes(font: Font.regular(24.0), color: presentationData.theme.list.itemPrimaryTextColor)
subtitleString = NSAttributedString(string: " ", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) smallTitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(24.0), color: .white)
smallSubtitleString = subtitleString
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) subtitleStringText = " "
subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor)
smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor)
usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(15.0), color: presentationData.theme.list.itemSecondaryTextColor))
} }
let textSideInset: CGFloat = 36.0 let textSideInset: CGFloat = 36.0
@@ -2760,17 +2777,17 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let titleConstrainedSize = CGSize(width: width - textSideInset * 2.0 - (isPremium || isVerified || isFake ? 20.0 : 0.0), height: .greatestFiniteMagnitude) let titleConstrainedSize = CGSize(width: width - textSideInset * 2.0 - (isPremium || isVerified || isFake ? 20.0 : 0.0), height: .greatestFiniteMagnitude)
let titleNodeLayout = self.titleNode.updateLayout(states: [ let titleNodeLayout = self.titleNode.updateLayout(text: titleStringText, states: [
TitleNodeStateRegular: MultiScaleTextState(attributedText: titleString, constrainedSize: titleConstrainedSize), TitleNodeStateRegular: MultiScaleTextState(attributes: titleAttributes, constrainedSize: titleConstrainedSize),
TitleNodeStateExpanded: MultiScaleTextState(attributedText: smallTitleString, constrainedSize: titleConstrainedSize) TitleNodeStateExpanded: MultiScaleTextState(attributes: smallTitleAttributes, constrainedSize: titleConstrainedSize)
], mainState: TitleNodeStateRegular) ], mainState: TitleNodeStateRegular)
self.titleNode.accessibilityLabel = titleString.string self.titleNode.accessibilityLabel = titleStringText
let subtitleNodeLayout = self.subtitleNode.updateLayout(states: [ let subtitleNodeLayout = self.subtitleNode.updateLayout(text: subtitleStringText, states: [
TitleNodeStateRegular: MultiScaleTextState(attributedText: subtitleString, constrainedSize: titleConstrainedSize), TitleNodeStateRegular: MultiScaleTextState(attributes: subtitleAttributes, constrainedSize: titleConstrainedSize),
TitleNodeStateExpanded: MultiScaleTextState(attributedText: smallSubtitleString, constrainedSize: titleConstrainedSize) TitleNodeStateExpanded: MultiScaleTextState(attributes: smallSubtitleAttributes, constrainedSize: titleConstrainedSize)
], mainState: TitleNodeStateRegular) ], mainState: TitleNodeStateRegular)
self.subtitleNode.accessibilityLabel = subtitleString.string self.subtitleNode.accessibilityLabel = subtitleStringText
if subtitleIsButton { if subtitleIsButton {
let subtitleBackgroundNode: ASDisplayNode let subtitleBackgroundNode: ASDisplayNode
@@ -2863,25 +2880,25 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} }
} }
let panelSubtitleNodeLayout = self.panelSubtitleNode.updateLayout(states: [ let panelSubtitleNodeLayout = self.panelSubtitleNode.updateLayout(text: panelSubtitleString?.text ?? subtitleStringText, states: [
TitleNodeStateRegular: MultiScaleTextState(attributedText: panelSubtitleString ?? subtitleString, constrainedSize: titleConstrainedSize), TitleNodeStateRegular: MultiScaleTextState(attributes: panelSubtitleString?.attributes ?? subtitleAttributes, constrainedSize: titleConstrainedSize),
TitleNodeStateExpanded: MultiScaleTextState(attributedText: panelSubtitleString ?? subtitleString, constrainedSize: titleConstrainedSize) TitleNodeStateExpanded: MultiScaleTextState(attributes: panelSubtitleString?.attributes ?? subtitleAttributes, constrainedSize: titleConstrainedSize)
], mainState: TitleNodeStateRegular) ], mainState: TitleNodeStateRegular)
self.panelSubtitleNode.accessibilityLabel = (panelSubtitleString ?? subtitleString).string self.panelSubtitleNode.accessibilityLabel = panelSubtitleString?.text ?? subtitleStringText
let nextPanelSubtitleNodeLayout = self.nextPanelSubtitleNode.updateLayout(states: [ let nextPanelSubtitleNodeLayout = self.nextPanelSubtitleNode.updateLayout(text: nextPanelSubtitleString?.text ?? subtitleStringText, states: [
TitleNodeStateRegular: MultiScaleTextState(attributedText: nextPanelSubtitleString ?? subtitleString, constrainedSize: titleConstrainedSize), TitleNodeStateRegular: MultiScaleTextState(attributes: nextPanelSubtitleString?.attributes ?? subtitleAttributes, constrainedSize: titleConstrainedSize),
TitleNodeStateExpanded: MultiScaleTextState(attributedText: nextPanelSubtitleString ?? subtitleString, constrainedSize: titleConstrainedSize) TitleNodeStateExpanded: MultiScaleTextState(attributes: nextPanelSubtitleString?.attributes ?? subtitleAttributes, constrainedSize: titleConstrainedSize)
], mainState: TitleNodeStateRegular) ], mainState: TitleNodeStateRegular)
if let _ = nextPanelSubtitleString { if let _ = nextPanelSubtitleString {
self.nextPanelSubtitleNode.isHidden = false self.nextPanelSubtitleNode.isHidden = false
} }
let usernameNodeLayout = self.usernameNode.updateLayout(states: [ let usernameNodeLayout = self.usernameNode.updateLayout(text: usernameString.text, states: [
TitleNodeStateRegular: MultiScaleTextState(attributedText: usernameString, constrainedSize: CGSize(width: titleConstrainedSize.width, height: titleConstrainedSize.height)), TitleNodeStateRegular: MultiScaleTextState(attributes: usernameString.attributes, constrainedSize: CGSize(width: titleConstrainedSize.width, height: titleConstrainedSize.height)),
TitleNodeStateExpanded: MultiScaleTextState(attributedText: usernameString, constrainedSize: CGSize(width: width - titleNodeLayout[TitleNodeStateExpanded]!.size.width - 8.0, height: titleConstrainedSize.height)) TitleNodeStateExpanded: MultiScaleTextState(attributes: usernameString.attributes, constrainedSize: CGSize(width: width - titleNodeLayout[TitleNodeStateExpanded]!.size.width - 8.0, height: titleConstrainedSize.height))
], mainState: TitleNodeStateRegular) ], mainState: TitleNodeStateRegular)
self.usernameNode.accessibilityLabel = usernameString.string self.usernameNode.accessibilityLabel = usernameString.text
let avatarCenter: CGPoint let avatarCenter: CGPoint
if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { if let transitionSourceAvatarFrame = transitionSourceAvatarFrame {
@@ -2987,7 +3004,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
subtitleAlpha = 1.0 - titleCollapseFraction subtitleAlpha = 1.0 - titleCollapseFraction
panelSubtitleAlpha = 0.0 panelSubtitleAlpha = 0.0
} else { } else {
if (panelSubtitleString ?? subtitleString).string != subtitleString.string { if (panelSubtitleString?.text ?? subtitleStringText) != subtitleStringText {
subtitleAlpha = 1.0 - effectiveAreaExpansionFraction subtitleAlpha = 1.0 - effectiveAreaExpansionFraction
panelSubtitleAlpha = effectiveAreaExpansionFraction panelSubtitleAlpha = effectiveAreaExpansionFraction
subtitleOffset = -effectiveAreaExpansionFraction * 5.0 subtitleOffset = -effectiveAreaExpansionFraction * 5.0