Various improvements

This commit is contained in:
Isaac 2026-01-16 21:23:32 +08:00
parent a14a8ecf00
commit 6558d2e265
26 changed files with 332 additions and 322 deletions

View file

@ -19,8 +19,7 @@ build --features=swift.enable_vfsoverlays
build --strategy=Genrule=standalone
build --spawn_strategy=standalone
build --strategy=SwiftCompile=standalone
build --define RULES_SWIFT_BUILD_DUMMY_WORKER=1
build --strategy=SwiftCompile=worker
build:swift_profile --jobs=1
build:swift_profile --local_cpu_resources=1

View file

@ -82,13 +82,6 @@ class BazelCommandLine:
]
self.common_debug_args = [
# https://github.com/bazelbuild/rules_swift
# If enabled, Swift compilation actions will use batch mode by passing
# `-enable-batch-mode` to `swiftc`. This is a new compilation mode as of
# Swift 4.2 that is intended to speed up non-incremental non-WMO builds by
# invoking a smaller number of frontend processes and passing them batches of
# source files.
'--features=swift.enable_batch_mode',
]
self.common_release_args = [

View file

@ -626,11 +626,11 @@ public class ContactsController: ViewController {
}
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Contacts_Sort_ByLastSeen, icon: { theme in return currentSettings.sortOrder == .presence ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Contacts_Sort_ByLastSeen, icon: { theme in return currentSettings.sortOrder == .presence ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in
f(.default)
updateSortOrder(.presence)
})))
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Contacts_Sort_ByName, icon: { theme in return currentSettings.sortOrder == .natural ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Contacts_Sort_ByName, icon: { theme in return currentSettings.sortOrder == .natural ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in
f(.default)
updateSortOrder(.natural)
})))

View file

@ -129,6 +129,7 @@ public final class ContextMenuActionItem {
public let textColor: ContextMenuActionItemTextColor
public let textFont: ContextMenuActionItemFont
public let textLayout: ContextMenuActionItemTextLayout
public let customTextInsets: UIEdgeInsets?
public let parseMarkdown: Bool
public let badge: ContextMenuActionBadge?
public let icon: (PresentationTheme) -> UIImage?
@ -150,6 +151,7 @@ public final class ContextMenuActionItem {
enableEntityAnimations: Bool = true,
textColor: ContextMenuActionItemTextColor = .primary,
textLayout: ContextMenuActionItemTextLayout = .twoLinesMax,
customTextInsets: UIEdgeInsets? = nil,
textFont: ContextMenuActionItemFont = .regular,
parseMarkdown: Bool = false,
badge: ContextMenuActionBadge? = nil,
@ -172,6 +174,7 @@ public final class ContextMenuActionItem {
enableEntityAnimations: enableEntityAnimations,
textColor: textColor,
textLayout: textLayout,
customTextInsets: customTextInsets,
textFont: textFont,
parseMarkdown: parseMarkdown,
badge: badge,
@ -204,6 +207,7 @@ public final class ContextMenuActionItem {
enableEntityAnimations: Bool = true,
textColor: ContextMenuActionItemTextColor = .primary,
textLayout: ContextMenuActionItemTextLayout = .twoLinesMax,
customTextInsets: UIEdgeInsets? = nil,
textFont: ContextMenuActionItemFont = .regular,
parseMarkdown: Bool = false,
badge: ContextMenuActionBadge? = nil,
@ -226,6 +230,7 @@ public final class ContextMenuActionItem {
self.textColor = textColor
self.textFont = textFont
self.textLayout = textLayout
self.customTextInsets = customTextInsets
self.parseMarkdown = parseMarkdown
self.badge = badge
self.icon = icon
@ -250,12 +255,17 @@ public protocol ContextMenuCustomNode: ASDisplayNode {
func performAction()
var needsSeparator: Bool { get }
var needsPadding: Bool { get }
}
public extension ContextMenuCustomNode {
var needsSeparator: Bool {
return true
}
var needsPadding: Bool {
return true
}
}
public protocol ContextMenuCustomItem {
@ -798,6 +808,7 @@ public protocol ContextControllerActionsStackItemNode: ASDisplayNode {
transition: ContainedViewLayoutTransition
) -> (size: CGSize, apparentHeight: CGFloat)
func highlightGestureShouldBegin(location: CGPoint) -> Bool
func highlightGestureMoved(location: CGPoint)
func highlightGestureFinished(performAction: Bool)

View file

@ -64,9 +64,9 @@ private final class VoiceChatInfoContextItemNode: ASDisplayNode, ContextMenuCust
}
func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
let sideInset: CGFloat = 16.0
let iconSideInset: CGFloat = 12.0
let verticalInset: CGFloat = 12.0
let sideInset: CGFloat = 18.0
let iconSideInset: CGFloat = 20.0
let verticalInset: CGFloat = 11.0
let iconSize = self.iconNode.image.flatMap({ $0.size }) ?? CGSize()
@ -80,11 +80,11 @@ private final class VoiceChatInfoContextItemNode: ASDisplayNode, ContextMenuCust
return (CGSize(width: textSize.width + sideInset + rightTextInset, height: verticalInset * 2.0 + textSize.height), { size, transition in
let verticalOrigin = floor((size.height - textSize.height) / 2.0)
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize)
let textFrame = CGRect(origin: CGPoint(x: iconSideInset + 40.0, y: verticalOrigin), size: textSize)
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
if !iconSize.width.isZero {
transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
}
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))

View file

@ -98,7 +98,6 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen
private let actionSelected: (ContextMenuActionResult) -> Void
private let backgroundNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
private let textNode: ImmediateTextNode
private let statusNode: ImmediateTextNode
private let iconNode: VoiceChatRecordingIconNode
@ -120,10 +119,6 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen
self.backgroundNode = ASDisplayNode()
self.backgroundNode.isAccessibilityElement = false
self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor
self.highlightedBackgroundNode = ASDisplayNode()
self.highlightedBackgroundNode.isAccessibilityElement = false
self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
self.highlightedBackgroundNode.alpha = 0.0
self.textNode = ImmediateTextNode()
self.textNode.isAccessibilityElement = false
@ -149,23 +144,11 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen
super.init()
self.addSubnode(self.backgroundNode)
self.addSubnode(self.highlightedBackgroundNode)
self.addSubnode(self.textNode)
self.addSubnode(self.statusNode)
self.addSubnode(self.iconNode)
self.addSubnode(self.buttonNode)
self.buttonNode.highligthedChanged = { [weak self] highligted in
guard let strongSelf = self else {
return
}
if highligted {
strongSelf.highlightedBackgroundNode.alpha = 1.0
} else {
strongSelf.highlightedBackgroundNode.alpha = 0.0
strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
}
}
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
}
@ -176,14 +159,8 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen
override func didLoad() {
super.didLoad()
self.pointerInteraction = PointerInteraction(node: self.buttonNode, style: .hover, willEnter: { [weak self] in
if let strongSelf = self {
strongSelf.highlightedBackgroundNode.alpha = 0.75
}
}, willExit: { [weak self] in
if let strongSelf = self {
strongSelf.highlightedBackgroundNode.alpha = 0.0
}
self.pointerInteraction = PointerInteraction(node: self.buttonNode, style: .hover, willEnter: {
}, willExit: {
})
let timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
@ -205,15 +182,16 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen
let subtextFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0)
self.statusNode.attributedText = NSAttributedString(string: stringForDuration(Int32(duration)), font: subtextFont, textColor: presentationData.theme.contextMenu.secondaryColor)
let sideInset: CGFloat = 16.0
let sideInset: CGFloat = 18.0
let iconSideInset: CGFloat = 22.0
let statusSize = self.statusNode.updateLayout(CGSize(width: size.width - sideInset - 32.0, height: .greatestFiniteMagnitude))
transition.updateFrameAdditive(node: self.statusNode, frame: CGRect(origin: CGPoint(x: sideInset, y: self.statusNode.frame.minY), size: statusSize))
transition.updateFrameAdditive(node: self.statusNode, frame: CGRect(origin: CGPoint(x: iconSideInset + 38.0, y: self.statusNode.frame.minY), size: statusSize))
}
func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
let sideInset: CGFloat = 16.0
let iconSideInset: CGFloat = 12.0
let verticalInset: CGFloat = 12.0
let sideInset: CGFloat = 18.0
let iconSideInset: CGFloat = 22.0
let verticalInset: CGFloat = 11.0
let iconSide = 16.0 + (1.0 + UIScreenPixel) * 2.0
let iconSize: CGSize = CGSize(width: iconSide, height: iconSide)
@ -237,23 +215,21 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen
self.updateTime(transition: .immediate)
}
let verticalOrigin = floor((size.height - combinedTextHeight) / 2.0)
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize)
let textFrame = CGRect(origin: CGPoint(x: iconSideInset + 38.0, y: verticalOrigin), size: textSize)
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
transition.updateFrameAdditive(node: self.statusNode, frame: CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin + verticalSpacing + textSize.height), size: textSize))
transition.updateFrameAdditive(node: self.statusNode, frame: CGRect(origin: CGPoint(x: iconSideInset + 38.0, y: verticalOrigin + verticalSpacing + textSize.height), size: textSize))
if !iconSize.width.isZero {
transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
}
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
})
}
func updateTheme(presentationData: PresentationData) {
self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor
self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize)
let subtextFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0)
@ -284,10 +260,5 @@ private final class VoiceChatRecordingContextItemNode: ASDisplayNode, ContextMen
}
func setIsHighlighted(_ value: Bool) {
if value {
self.highlightedBackgroundNode.alpha = 1.0
} else {
self.highlightedBackgroundNode.alpha = 0.0
}
}
}

View file

@ -37,7 +37,6 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM
private let actionSelected: (ContextMenuActionResult) -> Void
private let backgroundNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
private let textNode: ImmediateTextNode
private let iconNode: ASImageNode
@ -59,10 +58,6 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM
self.backgroundNode = ASDisplayNode()
self.backgroundNode.isAccessibilityElement = false
self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor
self.highlightedBackgroundNode = ASDisplayNode()
self.highlightedBackgroundNode.isAccessibilityElement = false
self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
self.highlightedBackgroundNode.alpha = 0.0
self.textNode = ImmediateTextNode()
self.textNode.isAccessibilityElement = false
@ -89,7 +84,6 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM
super.init()
self.addSubnode(self.backgroundNode)
self.addSubnode(self.highlightedBackgroundNode)
if let broadcastPickerView = self.broadcastPickerView {
self.view.addSubview(broadcastPickerView)
}
@ -118,9 +112,9 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM
private var validLayout: CGSize?
func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
let sideInset: CGFloat = 16.0
let iconSideInset: CGFloat = 12.0
let verticalInset: CGFloat = 12.0
let sideInset: CGFloat = 18.0
let iconSideInset: CGFloat = 20.0
let verticalInset: CGFloat = 11.0
let iconSize = self.iconNode.image.flatMap({ $0.size }) ?? CGSize()
@ -139,15 +133,14 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM
self.validLayout = size
let verticalOrigin = floor((size.height - combinedTextHeight) / 2.0)
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize)
let textFrame = CGRect(origin: CGPoint(x: iconSideInset + 40.0, y: verticalOrigin), size: textSize)
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
if !iconSize.width.isZero {
transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
}
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
if let broadcastPickerView = self.broadcastPickerView {
broadcastPickerView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))
@ -157,7 +150,6 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM
func updateTheme(presentationData: PresentationData) {
self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor
self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize)
@ -186,10 +178,5 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM
}
func setIsHighlighted(_ value: Bool) {
if value {
self.highlightedBackgroundNode.alpha = 1.0
} else {
self.highlightedBackgroundNode.alpha = 0.0
}
}
}

View file

@ -41,6 +41,8 @@ private final class VoiceChatVolumeContextItemNode: ASDisplayNode, ContextMenuCu
}
}
let needsPadding: Bool = false
private let valueChanged: (CGFloat, Bool) -> Void
private let hapticFeedback = HapticFeedback()
@ -134,7 +136,7 @@ private final class VoiceChatVolumeContextItemNode: ASDisplayNode, ContextMenuCu
textSize.width = valueWidth
return (CGSize(width: height * 3.0, height: height), { size, transition in
let leftInset: CGFloat = 17.0
let leftInset: CGFloat = 25.0
let textFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - textSize.height) / 2.0)), size: textSize)
transition.updateFrameAdditive(node: self.backgroundTextNode, frame: textFrame)

View file

@ -29,6 +29,8 @@ private final class ChatSendAsPeerTitleContextItemNode: ASDisplayNode, ContextMe
private let backgroundNode: ASDisplayNode
private let textNode: ImmediateTextNode
let needsPadding: Bool = false
init(presentationData: PresentationData, item: ChatSendAsPeerTitleContextItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) {
self.item = item
self.presentationData = presentationData
@ -55,10 +57,10 @@ private final class ChatSendAsPeerTitleContextItemNode: ASDisplayNode, ContextMe
}
func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
let sideInset: CGFloat = 16.0
let sideInset: CGFloat = 18.0
let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - sideInset, height: .greatestFiniteMagnitude))
let height: CGFloat = 28.0
let height: CGFloat = 28.0 + 10.0
return (CGSize(width: textSize.width + sideInset + sideInset, height: height), { size, transition in
let verticalOrigin = floor((size.height - textSize.height) / 2.0)

View file

@ -11,6 +11,9 @@ import TextFormat
import TextNodeWithEntities
import SwiftSignalKit
import ContextUI
import GlassBackgroundComponent
import ComponentFlow
import ComponentDisplayAdapters
private final class ContextActionsSelectionGestureRecognizer: UIPanGestureRecognizer {
var updateLocation: ((CGPoint, Bool) -> Void)?
@ -325,10 +328,7 @@ private final class InnerActionsContainerNode: ASDisplayNode {
final class InnerTextSelectionTipContainerNode: ASDisplayNode {
private let presentationData: PresentationData
let shadowNode: ASImageNode
private let backgroundNode: ASDisplayNode
private var effectView: UIVisualEffectView?
private let highlightBackgroundNode: ASDisplayNode
private var background: (container: GlassBackgroundContainerView, background: GlassBackgroundView)?
private let buttonNode: HighlightTrackingButtonNode
private let textNode: TextNodeWithEntities
private var textSelectionNode: TextSelectionNode?
@ -347,22 +347,15 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
private var action: (() -> Void)?
var requestDismiss: (@escaping () -> Void) -> Void = { _ in }
init(presentationData: PresentationData, tip: ContextController.Tip) {
init(presentationData: PresentationData, tip: ContextController.Tip, isInline: Bool) {
self.tip = tip
self.presentationData = presentationData
self.shadowNode = ASImageNode()
self.shadowNode.displaysAsynchronously = false
self.shadowNode.displayWithoutProcessing = true
self.shadowNode.image = UIImage(bundleImageName: "Components/Context Menu/Shadow")?.stretchableImage(withLeftCapWidth: 60, topCapHeight: 60)
self.shadowNode.contentMode = .scaleToFill
self.shadowNode.isHidden = true
self.backgroundNode = ASDisplayNode()
self.highlightBackgroundNode = ASDisplayNode()
self.highlightBackgroundNode.isAccessibilityElement = false
self.highlightBackgroundNode.alpha = 0.0
if !isInline {
self.background = (GlassBackgroundContainerView(), GlassBackgroundView())
} else {
self.background = nil
}
self.buttonNode = HighlightTrackingButtonNode()
@ -448,13 +441,6 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
super.init()
self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.backgroundColor
self.backgroundNode.clipsToBounds = true
self.backgroundNode.cornerRadius = 14.0
self.highlightBackgroundNode.clipsToBounds = true
self.highlightBackgroundNode.cornerRadius = 14.0
let textSelectionNode = TextSelectionNode(theme: TextSelectionTheme(selection: presentationData.theme.contextMenu.primaryColor.withAlphaComponent(0.15), knob: presentationData.theme.contextMenu.primaryColor, knobDiameter: 8.0, isDark: presentationData.theme.overallDarkAppearance), strings: presentationData.strings, textNode: self.textNode.textNode, updateIsActive: { _ in
}, present: { _, _ in
}, rootNode: { [weak self] in
@ -463,17 +449,24 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
})
self.textSelectionNode = textSelectionNode
self.addSubnode(self.backgroundNode)
self.addSubnode(self.highlightBackgroundNode)
self.addSubnode(self.textNode.textNode)
self.addSubnode(self.iconNode)
self.addSubnode(self.placeholderNode)
let parentView: UIView
if let background = self.background {
self.view.addSubview(background.container)
background.container.contentView.addSubview(background.background)
parentView = background.background.contentView
} else {
parentView = self.view
}
self.textSelectionNode.flatMap(self.addSubnode)
parentView.addSubview(self.textNode.textNode.view)
parentView.addSubview(self.iconNode.view)
parentView.addSubview(self.placeholderNode.view)
self.addSubnode(textSelectionNode.highlightAreaNode)
self.textSelectionNode.flatMap { parentView.addSubview($0.view) }
self.addSubnode(self.buttonNode)
parentView.addSubview(textSelectionNode.highlightAreaNode.view)
parentView.addSubview(self.buttonNode.view)
self.buttonNode.highligthedChanged = { [weak self] highlighted in
guard let strongSelf = self else {
@ -512,7 +505,11 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
]
for node in nodes {
other.addSubnode(node)
if let background = other.background {
background.background.contentView.addSubview(node.view)
} else {
other.view.addSubview(node.view)
}
node.layer.animateAlpha(from: node.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak node] _ in
node?.removeFromSupernode()
})
@ -531,44 +528,13 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
}
func updateLayout(widthClass: ContainerViewLayoutSizeClass, presentation: ContextControllerActionsStackNode.Presentation, width: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
var needsBlur = false
if case .regular = widthClass {
needsBlur = true
} else if case .inline = presentation {
needsBlur = true
}
if !needsBlur {
if let effectView = self.effectView {
self.effectView = nil
effectView.removeFromSuperview()
}
} else {
if self.effectView == nil {
let effectView: UIVisualEffectView
if #available(iOS 13.0, *) {
if self.presentationData.theme.overallDarkAppearance {
effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialDark))
} else {
effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialLight))
}
} else {
effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
}
effectView.clipsToBounds = true
effectView.layer.cornerRadius = self.backgroundNode.cornerRadius
self.effectView = effectView
self.view.insertSubview(effectView, at: 0)
}
}
let verticalInset: CGFloat = 10.0
let horizontalInset: CGFloat = 16.0
let verticalInset: CGFloat = 16.0
let horizontalInset: CGFloat = 18.0
let standardIconWidth: CGFloat = 32.0
let iconSideInset: CGFloat = 12.0
let iconSideInset: CGFloat = 20.0
let textFont = Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0))
let boldTextFont = Font.bold(floor(presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0))
let textFont = Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0))
let boldTextFont = Font.bold(floor(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0))
let textColor = self.presentationData.theme.contextMenu.primaryColor
let linkColor = self.presentationData.theme.overallDarkAppearance ? UIColor(rgb: 0x64d2ff) : self.presentationData.theme.contextMenu.badgeFillColor
@ -626,7 +592,7 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
transition.updateFrame(node: self.placeholderNode, frame: CGRect(origin: CGPoint(x: horizontalInset, y: floorToScreenPixels((size.height - lineHeight) / 2.0)), size: CGSize(width: width - horizontalInset * 2.0, height: lineHeight)))
transition.updateAlpha(node: self.placeholderNode, alpha: textFrame.height.isZero ? 1.0 : 0.0)
let iconFrame = CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
let iconFrame = CGRect(origin: CGPoint(x: iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
transition.updateFrame(node: self.iconNode, frame: iconFrame)
if let textSelectionNode = self.textSelectionNode {
@ -634,30 +600,24 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
textSelectionNode.highlightAreaNode.frame = textFrame
}
switch presentation {
case .modal:
self.shadowNode.isHidden = true
case .inline, .additional:
self.shadowNode.isHidden = false
}
if let effectView = self.effectView {
transition.updateFrame(view: effectView, frame: CGRect(origin: CGPoint(), size: size))
}
self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
return size
}
func setActualSize(size: CGSize, transition: ContainedViewLayoutTransition) {
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
self.highlightBackgroundNode.frame = CGRect(origin: CGPoint(), size: size)
let transition = ComponentTransition(transition)
if let background = self.background {
background.container.update(size: size, isDark: self.presentationData.theme.overallDarkAppearance, transition: transition)
transition.setFrame(view: background.container, frame: CGRect(origin: CGPoint(), size: size))
background.background.update(size: size, cornerRadius: min(30.0, size.height * 0.5), isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: transition)
transition.setFrame(view: background.background, frame: CGRect(origin: CGPoint(), size: size))
}
self.buttonNode.frame = CGRect(origin: CGPoint(), size: size)
}
func updateTheme(presentationData: PresentationData) {
self.backgroundColor = presentationData.theme.contextMenu.backgroundColor
}
func animateIn() {
@ -673,17 +633,6 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
}
func updateHighlight(animated: Bool) {
if self.isButtonHighlighted || self.isHighlighted {
self.highlightBackgroundNode.alpha = 1.0
} else {
if animated {
let previousAlpha = self.highlightBackgroundNode.alpha
self.highlightBackgroundNode.alpha = 0.0
self.highlightBackgroundNode.layer.animateAlpha(from: previousAlpha, to: 0.0, duration: 0.2)
} else {
self.highlightBackgroundNode.alpha = 0.0
}
}
}
private var isButtonHighlighted = false
@ -796,8 +745,6 @@ final class ContextActionsContainerNode: ASDisplayNode {
super.init()
//self.addSubnode(self.shadowNode)
//self.additionalShadowNode.flatMap(self.addSubnode)
self.additionalActionsNode.flatMap(self.scrollNode.addSubnode)
self.scrollNode.addSubnode(self.actionsNode)
self.addSubnode(self.scrollNode)
@ -857,7 +804,7 @@ final class ContextActionsContainerNode: ASDisplayNode {
textSelectionTipNode.removeFromSupernode()
}
let textSelectionTipNode = InnerTextSelectionTipContainerNode(presentationData: self.presentationData, tip: tip)
let textSelectionTipNode = InnerTextSelectionTipContainerNode(presentationData: self.presentationData, tip: tip, isInline: false)
let getController = self.getController
textSelectionTipNode.requestDismiss = { completion in
getController()?.dismiss(completion: completion)

View file

@ -26,6 +26,8 @@ public protocol ContextControllerActionsListItemNode: ASDisplayNode {
func canBeHighlighted() -> Bool
func updateIsHighlighted(isHighlighted: Bool)
func performAction()
var needsPadding: Bool { get }
}
public final class ContextControllerActionsListActionItemNode: HighlightTrackingButtonNode, ContextControllerActionsListItemNode {
@ -49,6 +51,8 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking
private var iconDisposable: Disposable?
public let needsPadding: Bool = true
public init(
context: AccountContext?,
getController: @escaping () -> ContextControllerProtocol?,
@ -205,10 +209,10 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking
let sideInset: CGFloat = 18.0
let verticalInset: CGFloat = 11.0
let titleSubtitleSpacing: CGFloat = 1.0
let iconSideInset: CGFloat = 18.0 + 6.0
let iconSideInset: CGFloat = 20.0
let standardIconWidth: CGFloat = 32.0
let iconSpacing: CGFloat = 8.0
var forcedHeight: CGFloat?
var titleVerticalOffset: CGFloat?
let titleFont: UIFont
@ -490,6 +494,10 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking
maxTextWidth -= sideInset
}
if let additionalIconSize {
maxTextWidth -= additionalIconSize.width
}
if let badgeSize = badgeSize {
maxTextWidth -= badgeSize.width
maxTextWidth -= 8.0
@ -510,8 +518,8 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking
} else {
minSize.width += sideInset
}
if self.item.additionalLeftIcon != nil {
minSize.width += 24.0
if let additionalIconSize {
minSize.width += additionalIconSize.width
minSize.width += iconSideInset
minSize.width += iconSpacing
}
@ -528,13 +536,15 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking
return (minSize: minSize, apply: { size, transition in
var titleFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalInset), size: titleSize)
if let customTextInsets = self.item.customTextInsets {
titleFrame.origin.x = customTextInsets.left
}
if let titleVerticalOffset {
titleFrame = titleFrame.offsetBy(dx: 0.0, dy: titleVerticalOffset)
}
var subtitleFrame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.maxY + titleSubtitleSpacing), size: subtitleSize)
if self.item.additionalLeftIcon != nil {
} else if iconSize != nil {
titleFrame.origin.x = iconSideInset + 36.0
if iconSize != nil {
titleFrame.origin.x = iconSideInset + 40.0
subtitleFrame.origin.x = titleFrame.minX
}
@ -548,7 +558,7 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking
if let iconSize {
let iconFrame = CGRect(
origin: CGPoint(
x: iconSideInset,
x: iconSideInset + floor((standardIconWidth - iconSize.width) * 0.5),
y: floor((size.height - iconSize.height) / 2.0)
),
size: iconSize
@ -577,7 +587,7 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking
if let additionalIconSize {
let iconFrame = CGRect(
origin: CGPoint(
x: 10.0,
x: size.width - iconSideInset - additionalIconSize.width,
y: floor((size.height - additionalIconSize.height) / 2.0)
),
size: additionalIconSize
@ -591,6 +601,8 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking
private final class ContextControllerActionsListSeparatorItemNode: ASDisplayNode, ContextControllerActionsListItemNode {
private let separatorView: UIImageView
let needsPadding: Bool = false
func canBeHighlighted() -> Bool {
return false
}
@ -644,6 +656,14 @@ private final class ContextControllerActionsListCustomItemNode: ASDisplayNode, C
}
}
var needsPadding: Bool {
if let itemNode = self.itemNode {
return itemNode.needsPadding
} else {
return true
}
}
private let getController: () -> ContextControllerProtocol?
private let item: ContextMenuCustomItem
private let requestDismiss: (ContextMenuActionResult) -> Void
@ -733,6 +753,10 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio
private var items: [ContextMenuItem]
private var itemNodes: [Item]
private var tip: ContextController.Tip?
private var tipDisposable: Disposable?
private var tipNode: InnerTextSelectionTipContainerNode?
private var hapticFeedback: HapticFeedback?
private let highlightedItemBackgroundView: UIView
@ -750,13 +774,16 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio
getController: @escaping () -> ContextControllerProtocol?,
requestDismiss: @escaping (ContextMenuActionResult) -> Void,
requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void,
items: [ContextMenuItem]
items: [ContextMenuItem],
tip: ContextController.Tip?,
tipSignal: Signal<ContextController.Tip?, NoError>?
) {
self.context = context
self.requestUpdate = requestUpdate
self.getController = getController
self.requestDismiss = requestDismiss
self.items = items
self.tip = tip
var requestUpdateAction: ((AnyHashable, ContextMenuActionItem) -> Void)?
self.itemNodes = items.map { item -> Item in
@ -807,6 +834,10 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio
}
}
deinit {
self.tipDisposable?.dispose()
}
func updateItems(items: [ContextMenuItem]) {
self.items = items
for i in 0 ..< items.count {
@ -890,21 +921,35 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio
let verticalInset: CGFloat = 10.0
var itemNodeLayouts: [(minSize: CGSize, apply: (_ size: CGSize, _ transition: ContainedViewLayoutTransition) -> Void)] = []
var combinedSize = CGSize(width: 0.0, height: verticalInset)
for item in self.itemNodes {
var combinedSize = CGSize(width: 0.0, height: 0.0)
for i in 0 ..< self.itemNodes.count {
let item = self.itemNodes[i]
let itemNodeLayout = item.node.update(
presentationData: presentationData,
constrainedSize: CGSize(width: standardMaxWidth, height: constrainedSize.height)
)
if item.node.needsPadding {
if i == 0 {
combinedSize.height += verticalInset
}
}
itemNodeLayouts.append(itemNodeLayout)
combinedSize.width = max(combinedSize.width, itemNodeLayout.minSize.width)
combinedSize.height += itemNodeLayout.minSize.height
if item.node.needsPadding {
if i == self.itemNodes.count - 1 {
combinedSize.height += verticalInset
}
}
}
combinedSize.height += verticalInset
self.invalidatedItemNodes = false
combinedSize.width = max(combinedSize.width, standardMinWidth)
var nextItemOrigin = CGPoint(x: 0.0, y: verticalInset)
var nextItemOrigin = CGPoint(x: 0.0, y: 0.0)
for i in 0 ..< self.itemNodes.count {
let item = self.itemNodes[i]
let itemNodeLayout = itemNodeLayouts[i]
@ -914,6 +959,12 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio
itemTransition = .immediate
}
if item.node.needsPadding {
if i == 0 {
nextItemOrigin.y += verticalInset
}
}
let itemSize = CGSize(width: combinedSize.width, height: itemNodeLayout.minSize.height)
let itemFrame = CGRect(origin: nextItemOrigin, size: itemSize)
itemTransition.updateFrame(node: item.node, frame: itemFrame, beginWithCurrentState: true)
@ -926,6 +977,27 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio
}
}
if let tip = self.tip, !"".isEmpty {
let tipNode: InnerTextSelectionTipContainerNode
var tipTransition = transition
if let current = self.tipNode {
tipNode = current
} else {
tipTransition = .immediate
tipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip, isInline: true)
self.addSubnode(tipNode)
self.tipNode = tipNode
}
let tipSize = tipNode.updateLayout(widthClass: .compact, presentation: .inline, width: combinedSize.width, transition: tipTransition)
let tipFrame = CGRect(origin: nextItemOrigin, size: tipSize)
tipTransition.updateFrame(node: tipNode, frame: tipFrame)
nextItemOrigin.y += tipSize.height
combinedSize.height += tipSize.height
} else if let tipNode = self.tipNode {
tipNode.removeFromSupernode()
self.tipNode = nil
}
if let highlightedItemFrame {
self.highlightedItemBackgroundView.backgroundColor = presentationData.theme.overallDarkAppearance ? UIColor.white : UIColor.black
self.highlightedItemBackgroundView.setMonochromaticEffect(tintColor: self.highlightedItemBackgroundView.backgroundColor)
@ -955,6 +1027,18 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio
return (combinedSize, combinedSize.height)
}
func highlightGestureShouldBegin(location: CGPoint) -> Bool {
for itemNode in self.itemNodes {
if itemNode.node.frame.contains(location) {
if !itemNode.node.canBeHighlighted() {
return false
}
break
}
}
return true
}
func highlightGestureMoved(location: CGPoint) {
var highlightedItemNode: Item?
for itemNode in self.itemNodes {
@ -1058,7 +1142,9 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio
getController: getController,
requestDismiss: requestDismiss,
requestUpdate: requestUpdate,
items: self.items
items: self.items,
tip: self.tip,
tipSignal: self.tipSignal
)
}
}
@ -1110,6 +1196,10 @@ final class ContextControllerActionsCustomStackItem: ContextControllerActionsSta
return (contentLayout.cleanSize, contentLayout.apparentHeight)
}
func highlightGestureShouldBegin(location: CGPoint) -> Bool {
return true
}
func highlightGestureMoved(location: CGPoint) {
}
@ -1195,7 +1285,7 @@ func makeContextControllerActionsStackItem(items: ContextController.Items) -> [C
}
private final class ItemSelectionRecognizer: UIGestureRecognizer {
var shouldBegin: (() -> Bool)?
var shouldBegin: ((CGPoint) -> Bool)?
private var initialLocation: CGPoint?
private var currentLocation: CGPoint?
@ -1216,7 +1306,7 @@ private final class ItemSelectionRecognizer: UIGestureRecognizer {
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
if let shouldBegin = self.shouldBegin, !shouldBegin() {
if let location = touches.first?.location(in: self.view), let shouldBegin = self.shouldBegin, !shouldBegin(location) {
self.state = .failed
return
}
@ -1283,7 +1373,6 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context
self.contentContainer = UIView()
self.contentContainer.clipsToBounds = true
self.contentContainer.layer.cornerRadius = 30.0
self.backgroundView.contentView.addSubview(self.contentContainer)
super.init()
@ -1416,12 +1505,14 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context
})
}
transition.setCornerRadius(layer: self.contentContainer.layer, cornerRadius: min(30.0, size.height * 0.5))
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size))
self.backgroundView.update(size: size, cornerRadius: 30.0, isDark: presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition)
self.backgroundView.update(size: size, cornerRadius: min(30.0, size.height * 0.5), isDark: presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition)
if let sourceExtractableContainer = self.sourceExtractableContainer {
transition.setFrame(view: sourceExtractableContainer.extractableContentView, frame: CGRect(origin: CGPoint(), size: size))
sourceExtractableContainer.updateState(state: .extracted(size: size, cornerRadius: 30.0, state: .animatedIn), transition: transition.containedViewLayoutTransition)
sourceExtractableContainer.updateState(state: .extracted(size: size, cornerRadius: min(30.0, size.height * 0.5), state: .animatedIn), transition: transition.containedViewLayoutTransition)
}
}
}
@ -1540,7 +1631,7 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context
} else {
let previousTipNode = self.tipNode
updatedTransition = .immediate
let tipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip)
let tipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip, isInline: false)
tipNode.requestDismiss = { [weak self] completion in
self?.getController()?.dismiss(completion: completion)
}
@ -1572,6 +1663,10 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context
transition.updateAlpha(node: self.dimNode, alpha: 1.0 - transitionFraction, beginWithCurrentState: true)
}
func highlightGestureShouldBegin(at location: CGPoint) -> Bool {
return self.node.highlightGestureShouldBegin(location: self.view.convert(location, to: self.node.view))
}
func highlightGestureMoved(location: CGPoint) {
if let tipNode = self.tipNode {
let tipLocation = self.view.convert(location, to: tipNode.view)
@ -1655,7 +1750,7 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context
}
let selectionPanGesture = ItemSelectionRecognizer(target: self, action: #selector(self.panGesture(_:)))
selectionPanGesture.shouldBegin = { [weak self] in
selectionPanGesture.shouldBegin = { [weak self] point in
guard let self, let topItemContainer = self.itemContainers.last else {
return false
}
@ -1663,6 +1758,9 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context
if topItemContainer.item is ContextControllerActionsCustomStackItem {
return false
}
if !topItemContainer.highlightGestureShouldBegin(at: self.view.convert(point, to: topItemContainer.view)) {
return false
}
return true
}
@ -1959,32 +2057,25 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context
let tipTransition = transition
var animateTipIn = false
if tip.tipNode.supernode == nil {
self.insertSubnode(tip.tipNode.shadowNode, at: 0)
self.addSubnode(tip.tipNode)
animateTipIn = transition.isAnimated
let tipFrame = CGRect(origin: CGPoint(x: previousNavigationContainerFrame.minX, y: previousNavigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tip.tipHeight))
tip.tipNode.frame = tipFrame
tip.tipNode.setActualSize(size: tipFrame.size, transition: .immediate)
transition.updateFrame(node: tip.tipNode.shadowNode, frame: tipFrame.insetBy(dx: -30.0, dy: -30.0))
}
let tipAlpha: CGFloat = itemLayouts[i].alphaTransitionFraction
let tipFrame = CGRect(origin: CGPoint(x: navigationContainerFrame.minX, y: navigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tip.tipHeight))
tipTransition.updateFrame(node: tip.tipNode, frame: tipFrame, beginWithCurrentState: true)
transition.updateFrame(node: tip.tipNode.shadowNode, frame: tipFrame.insetBy(dx: -30.0, dy: -30.0))
tip.tipNode.setActualSize(size: tip.tipNode.bounds.size, transition: tipTransition)
if animateTipIn {
tip.tipNode.alpha = tipAlpha
tip.tipNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
tip.tipNode.shadowNode.alpha = tipAlpha
tip.tipNode.shadowNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
tip.tipNode.alpha = 0.0
ComponentTransition.easeInOut(duration: 0.2).setAlpha(view: tip.tipNode.view, alpha: 1.0)
} else {
tipTransition.updateAlpha(node: tip.tipNode, alpha: tipAlpha, beginWithCurrentState: true)
tipTransition.updateAlpha(node: tip.tipNode.shadowNode, alpha: tipAlpha, beginWithCurrentState: true)
ComponentTransition(tipTransition).setAlpha(view: tip.tipNode.view, alpha: tipAlpha)
}
if i == self.itemContainers.count - 1 {
@ -2006,15 +2097,10 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context
if let tipNode = itemContainer.tipNode {
let tipFrame = CGRect(origin: CGPoint(x: navigationContainerFrame.minX, y: navigationContainerFrame.maxY + tipSpacing), size: tipNode.frame.size)
transition.updateFrame(node: tipNode, frame: tipFrame, beginWithCurrentState: true)
transition.updateFrame(node: tipNode.shadowNode, frame: tipFrame.insetBy(dx: -30.0, dy: -30.0))
transition.updateAlpha(node: tipNode, alpha: 0.0, completion: { [weak tipNode] _ in
ComponentTransition(transition).setAlpha(view: tipNode.view, alpha: 0.0, completion: { [weak tipNode] _ in
tipNode?.removeFromSupernode()
})
let shadowNode = tipNode.shadowNode
transition.updateAlpha(node: shadowNode, alpha: 0.0, completion: { [weak shadowNode] _ in
shadowNode?.removeFromSupernode()
})
}
}
self.dismissingItemContainers.removeAll()

View file

@ -51,6 +51,9 @@ public class EdgeEffectView: UIView {
}
if blur {
let blurHeight: CGFloat = max(edgeSize, bounds.height - 14.0)
let blurFrame = CGRect(origin: CGPoint(x: 0.0, y: edge == .bottom ? (bounds.height - blurHeight) : 0.0), size: CGSize(width: bounds.width, height: blurHeight))
let blurView: VariableBlurView
if let current = self.blurView {
blurView = current
@ -59,14 +62,15 @@ public class EdgeEffectView: UIView {
self.insertSubview(blurView, at: 0)
self.blurView = blurView
}
blurView.update(
size: bounds.size,
constantHeight: edgeSize,
size: blurFrame.size,
constantHeight: max(1.0, edgeSize - 4.0),
isInverted: edge == .bottom,
gradient: EdgeEffectView.generateEdgeGradientData(baseHeight: edgeSize),
gradient: EdgeEffectView.generateEdgeGradientData(baseHeight: max(1.0, edgeSize - 4.0)),
transition: transition.containedViewLayoutTransition
)
transition.setFrame(view: blurView, frame: bounds)
transition.setFrame(view: blurView, frame: blurFrame)
blurView.transform = self.contentMaskView.transform
} else if let blurView = self.blurView {
self.blurView = nil

View file

@ -159,12 +159,12 @@ public final class GiftLoadingShimmerView: UIView {
let filterWidth = (size.width - sideInset * 2.0 - filterSpacing * 3.0) / 4.0
for i in 0 ..< 4 {
let rect = CGRect(origin: CGPoint(x: sideInset + (filterWidth + filterSpacing) * CGFloat(i), y: 0.0),
size: CGSize(width: filterWidth, height: 28.0))
context.addPath(CGPath(roundedRect: rect, cornerWidth: 14.0, cornerHeight: 14.0, transform: nil))
size: CGSize(width: filterWidth, height: 36.0))
context.addPath(CGPath(roundedRect: rect, cornerWidth: 36.0 * 0.5, cornerHeight: 36.0 * 0.5, transform: nil))
}
}
var currentY: CGFloat = 52.0 + 7.0
var currentY: CGFloat = 52.0 + 16.0
var rowIndex: Int = 0
let optionSpacing: CGFloat = 10.0
@ -178,7 +178,7 @@ public final class GiftLoadingShimmerView: UIView {
for i in 0 ..< 3 {
let itemOrigin = CGPoint(
x: sideInset + CGFloat(i) * (itemSize.width + optionSpacing),
y: 39.0 + 9.0 + CGFloat(rowIndex) * (itemSize.height + optionSpacing)
y: 39.0 + 13.0 + CGFloat(rowIndex) * (itemSize.height + optionSpacing)
)
context.addPath(CGPath(roundedRect: CGRect(origin: itemOrigin, size: itemSize),
cornerWidth: 10.0, cornerHeight: 10.0, transform: nil))

View file

@ -83,7 +83,6 @@ private func actionForAttribute(attribute: StarGift.UniqueGift.Attribute, presen
)
title += count
}
let words = title.components(separatedBy: .whitespacesAndNewlines).filter { !$0.isEmpty }
var wordStartIndices: [String.Index] = []
@ -118,7 +117,9 @@ private func actionForAttribute(attribute: StarGift.UniqueGift.Attribute, presen
}
}
return ContextMenuActionItem(text: title, entities: entities, entityFiles: entityFiles, enableEntityAnimations: false, parseMarkdown: true, icon: { theme in
return ContextMenuActionItem(text: title, entities: entities, entityFiles: entityFiles, enableEntityAnimations: false, customTextInsets: UIEdgeInsets(top: 0.0, left: 18.0 + 5.0, bottom: 0.0, right: 0.0), parseMarkdown: true, icon: { _ in
return nil
}, additionalLeftIcon: { theme in
return isSelected ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, f in
getController()?.dismiss(result: .dismissWithoutContent, completion: nil)
@ -134,7 +135,7 @@ private func actionForAttribute(attribute: StarGift.UniqueGift.Attribute, presen
let isSelected = selectedAttributes.isEmpty || selectedAttributes.contains(attributeId)
var entities: [MessageTextEntity] = []
var title = " \(name)"
var title = "\(name)"
var count = ""
if let counter = item.attributeCount[attributeId] {
count = " \(presentationStringsFormattedNumber(counter, presentationData.dateTimeFormat.groupingSeparator))"
@ -177,10 +178,10 @@ private func actionForAttribute(attribute: StarGift.UniqueGift.Attribute, presen
}
}
return ContextMenuActionItem(text: title, entities: entities, icon: { theme in
return isSelected ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, additionalLeftIcon: { _ in
return ContextMenuActionItem(text: title, entities: entities, icon: { _ in
return generateGradientFilledCircleImage(diameter: 24.0, colors: [UIColor(rgb: UInt32(bitPattern: innerColor)).cgColor, UIColor(rgb: UInt32(bitPattern: outerColor)).cgColor])
}, additionalLeftIcon: { theme in
return isSelected ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, f in
getController()?.dismiss(result: .dismissWithoutContent, completion: nil)
@ -212,6 +213,8 @@ private final class GiftAttributeListContextItemNode: ASDisplayNode, ContextMenu
private var totalContentHeight: CGFloat = 0
private var itemFrames: [AnyHashable: CGRect] = [:]
let needsPadding: Bool = false
init(presentationData: PresentationData, item: GiftAttributeListContextItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) {
self.item = item
self.presentationData = presentationData.withUpdate(listsFontSize: .regular)
@ -286,19 +289,12 @@ private final class GiftAttributeListContextItemNode: ASDisplayNode, ContextMenu
yOffset += UIScreenPixel
}
for (index, attribute) in effectiveAttributes.enumerated() {
for (_, attribute) in effectiveAttributes.enumerated() {
let attributeId = self.getAttributeId(from: attribute)
let height = self.itemHeights[attributeId] ?? defaultHeight
let frame = CGRect(x: 0, y: yOffset, width: constrainedWidth, height: height)
items.append((attributeId, .attribute(attribute), frame))
yOffset += height
if index < effectiveAttributes.count - 1 {
let separatorId = AnyHashable("separator_\(attributeId)")
let separatorFrame = CGRect(x: 0, y: yOffset, width: constrainedWidth, height: UIScreenPixel)
items.append((separatorId, .separator, separatorFrame))
yOffset += UIScreenPixel
}
}
if !self.searchQuery.isEmpty && effectiveAttributes.isEmpty {
@ -467,7 +463,7 @@ private final class GiftAttributeListContextItemNode: ASDisplayNode, ContextMenu
}
func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
let minActionsWidth: CGFloat = 250.0
let minActionsWidth: CGFloat = 270.0
let maxActionsWidth: CGFloat = 300.0
let constrainedWidth = min(constrainedWidth, maxActionsWidth)
let maxWidth = max(constrainedWidth, minActionsWidth)
@ -503,7 +499,7 @@ private final class GiftAttributeListContextItemNode: ASDisplayNode, ContextMenu
}
func canBeHighlighted() -> Bool {
return self.isActionEnabled
return false
}
func updateIsHighlighted(isHighlighted: Bool) {

View file

@ -133,9 +133,9 @@ final class GiftStoreScreenComponent: Component {
self.scrollView.delegate = self
self.addSubview(self.scrollView)
self.addSubview(self.loadingView)
self.addSubview(self.edgeEffectView)
self.addSubview(self.loadingView)
self.scrollView.layer.addSublayer(self.topOverscrollLayer)
}
@ -415,7 +415,7 @@ final class GiftStoreScreenComponent: Component {
if view.superview == nil {
view.alpha = 0.0
fadeTransition.setAlpha(view: view, alpha: 1.0)
self.insertSubview(view, belowSubview: self.loadingView)
self.insertSubview(view, aboveSubview: self.scrollView)
view.playOnce()
}
view.bounds = CGRect(origin: .zero, size: emptyResultsAnimationFrame.size)
@ -425,7 +425,7 @@ final class GiftStoreScreenComponent: Component {
if view.superview == nil {
view.alpha = 0.0
fadeTransition.setAlpha(view: view, alpha: 1.0)
self.insertSubview(view, belowSubview: self.loadingView)
self.insertSubview(view, aboveSubview: self.scrollView)
}
view.bounds = CGRect(origin: .zero, size: emptyResultsTitleFrame.size)
ComponentTransition.immediate.setPosition(view: view, position: emptyResultsTitleFrame.center)
@ -1203,7 +1203,7 @@ final class GiftStoreScreenComponent: Component {
} else {
loadingTransition.setAlpha(view: self.loadingView, alpha: 0.0)
}
transition.setFrame(view: self.loadingView, frame: CGRect(origin: CGPoint(x: 0.0, y: environment.navigationHeight), size: availableSize))
transition.setFrame(view: self.loadingView, frame: CGRect(origin: CGPoint(x: 0.0, y: environment.navigationHeight + 10.0), size: availableSize))
return availableSize
}

View file

@ -80,7 +80,7 @@ private final class SearchContextItemNode: ASDisplayNode, ContextMenuCustomNode,
environment: {},
containerSize: size
)
let iconFrame = CGRect(origin: CGPoint(x: 17.0, y: floorToScreenPixels((size.height - iconSize.height) / 2.0)), size: iconSize)
let iconFrame = CGRect(origin: CGPoint(x: 23.0, y: floorToScreenPixels((size.height - iconSize.height) / 2.0)), size: iconSize)
if let iconView = self.icon.view {
if iconView.superview == nil {
self.view.addSubview(iconView)
@ -88,7 +88,7 @@ private final class SearchContextItemNode: ASDisplayNode, ContextMenuCustomNode,
transition.setFrame(view: iconView, frame: iconFrame)
}
let inputInset: CGFloat = 42.0
let inputInset: CGFloat = 46.0
self.inputField.parentState = self.state
let inputFieldSize = self.inputField.update(

View file

@ -305,6 +305,8 @@ public final class HorizontalTabsComponent: Component {
private var tabSwitchFraction: CGFloat = 0.0
private var isDraggingTabs: Bool = false
private var temporaryLiftTimer: Foundation.Timer?
private var didTapOnAnItem: Bool = false
private var didTapOnAnItemTimer: Foundation.Timer?
private var tapRecognizer: UITapGestureRecognizer?
@ -531,6 +533,14 @@ public final class HorizontalTabsComponent: Component {
for (id, itemView) in self.itemViews {
if self.scrollView.convert(itemView.selectionFrame, to: self).contains(point) {
if let tab = component.tabs.first(where: { $0.id == id }) {
self.didTapOnAnItem = true
self.didTapOnAnItemTimer?.invalidate()
self.didTapOnAnItemTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { [weak self] _ in
guard let self else {
return
}
self.didTapOnAnItem = false
})
tab.action()
}
}
@ -609,7 +619,7 @@ public final class HorizontalTabsComponent: Component {
self.temporaryLiftTimer?.invalidate()
self.temporaryLiftTimer = nil
if !transition.animation.isImmediate {
if !transition.animation.isImmediate && self.didTapOnAnItem {
self.temporaryLiftTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false, block: { [weak self] _ in
guard let self else {
return
@ -627,6 +637,12 @@ public final class HorizontalTabsComponent: Component {
self.component = component
self.state = state
self.didTapOnAnItem = false
if let didTapOnAnItemTimer = self.didTapOnAnItemTimer {
self.didTapOnAnItemTimer = nil
didTapOnAnItemTimer.invalidate()
}
self.reorderingGesture?.isEnabled = component.isEditing
let sizeHeight: CGFloat = availableSize.height

View file

@ -222,7 +222,7 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
if let backgroundContainer = self.backgroundContainer {
backgroundContainer.contentView.addSubview(titleView)
} else {
self.buttonsContainerNode.view.addSubview(titleView)
self.buttonsContainerNode.view.insertSubview(titleView, at: 0)
}
}
@ -814,10 +814,13 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
edgeEffectView.isHidden = true
} else {
edgeEffectView.isHidden = false
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight + 20.0 + 20.0))
let edgeEffectHeight: CGFloat = size.height + additionalBackgroundHeight + 24.0
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: size.width, height: 20.0 + edgeEffectHeight))
transition.updatePosition(layer: edgeEffectView.layer, position: edgeEffectFrame.center)
transition.updateBounds(layer: edgeEffectView.layer, bounds: CGRect(origin: CGPoint(), size: edgeEffectFrame.size))
edgeEffectView.update(content: self.presentationData.theme.edgeEffectColor ?? .white, blur: true, rect: CGRect(origin: CGPoint(), size: edgeEffectFrame.size), edge: .top, edgeSize: 50.0, transition: ComponentTransition(transition))
edgeEffectView.update(content: self.presentationData.theme.edgeEffectColor ?? .white, blur: true, rect: CGRect(origin: CGPoint(), size: edgeEffectFrame.size), edge: .top, edgeSize: min(64.0, edgeEffectHeight), transition: ComponentTransition(transition))
}
}

View file

@ -228,6 +228,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
let edgeEffectView: EdgeEffectView
let headerNode: PeerInfoHeaderNode
var underHeaderContentsAlpha: CGFloat = 1.0
var regularSections: [AnyHashable: PeerInfoScreenItemSectionContainerNode] = [:]
var editingSections: [AnyHashable: PeerInfoScreenItemSectionContainerNode] = [:]
let paneContainerNode: PeerInfoPaneContainerNode
@ -2059,6 +2060,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
guard let self else {
return
}
self.underHeaderContentsAlpha = alpha
if !self.state.isEditing {
for (_, section) in self.regularSections {
transition.updateAlpha(node: section, alpha: alpha)
@ -5293,7 +5295,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
if wasAdded && transition.isAnimated && (self.isSettings || self.isMyProfile) && !self.state.isEditing {
sectionNode.alpha = 0.0
transition.updateAlpha(node: sectionNode, alpha: 1.0, delay: 0.1)
transition.updateAlpha(node: sectionNode, alpha: self.underHeaderContentsAlpha, delay: 0.1)
}
let sectionWidth = layout.size.width - insets.left - insets.right
@ -5312,7 +5314,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
if wasAdded && transition.isAnimated && (self.isSettings || self.isMyProfile) && !self.state.isEditing {
} else {
transition.updateAlpha(node: sectionNode, alpha: self.state.isEditing ? 0.0 : 1.0)
transition.updateAlpha(node: sectionNode, alpha: self.state.isEditing ? 0.0 : self.underHeaderContentsAlpha)
}
if !sectionHeight.isZero && !self.state.isEditing {
contentHeight += sectionHeight

View file

@ -360,7 +360,7 @@ extension PeerInfoScreenNode {
items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowPhotos, icon: { theme in
if !showPhotos {
return nil
return UIImage()
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, action: { [weak pane] _, a in
@ -384,7 +384,7 @@ extension PeerInfoScreenNode {
})))
items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowVideos, icon: { theme in
if !showVideos {
return nil
return UIImage()
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, action: { [weak pane] _, a in

View file

@ -113,7 +113,6 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom
private let getController: () -> ContextControllerProtocol?
private let actionSelected: (ContextMenuActionResult) -> Void
private let highlightedBackgroundNode: ASDisplayNode
private let buttonNode: HighlightTrackingButtonNode
private let textNode: ImmediateTextNode
private let avatarNode: AvatarNode
@ -127,11 +126,6 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 17.0 / 17.0)
self.highlightedBackgroundNode = ASDisplayNode()
self.highlightedBackgroundNode.isAccessibilityElement = false
self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
self.highlightedBackgroundNode.alpha = 0.0
self.textNode = ImmediateTextNode()
self.textNode.isAccessibilityElement = false
self.textNode.isUserInteractionEnabled = false
@ -150,29 +144,17 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom
super.init()
self.addSubnode(self.highlightedBackgroundNode)
self.addSubnode(self.textNode)
self.addSubnode(self.avatarNode)
self.addSubnode(self.buttonNode)
self.buttonNode.highligthedChanged = { [weak self] highligted in
guard let strongSelf = self else {
return
}
if highligted {
strongSelf.highlightedBackgroundNode.alpha = 1.0
} else {
strongSelf.highlightedBackgroundNode.alpha = 0.0
strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
}
}
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
}
func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
let sideInset: CGFloat = 16.0
let iconSideInset: CGFloat = 12.0
let verticalInset: CGFloat = 12.0
let sideInset: CGFloat = 18.0
let iconSideInset: CGFloat = 20.0
let verticalInset: CGFloat = 11.0
let iconSize = CGSize(width: 28.0, height: 28.0)
@ -192,7 +174,7 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom
return (CGSize(width: textSize.width + sideInset + rightTextInset, height: verticalInset * 2.0 + textSize.height), { size, transition in
let verticalOrigin = floor((size.height - textSize.height) / 2.0)
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize)
let textFrame = CGRect(origin: CGPoint(x: iconSideInset + 40.0, y: verticalOrigin), size: textSize)
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
var iconContent: EmojiStatusComponent.Content?
@ -229,16 +211,13 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom
}
}
transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
})
}
func updateTheme(presentationData: PresentationData) {
self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
if let attributedText = self.textNode.attributedText {
let updatedAttributedText = NSMutableAttributedString(attributedString: attributedText)
updatedAttributedText.addAttribute(.foregroundColor, value: presentationData.theme.contextMenu.primaryColor.cgColor, range: NSRange(location: 0, length: updatedAttributedText.length))
@ -255,11 +234,6 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom
}
func setIsHighlighted(_ value: Bool) {
if value {
self.highlightedBackgroundNode.alpha = 1.0
} else {
self.highlightedBackgroundNode.alpha = 0.0
}
}
func updateIsHighlighted(isHighlighted: Bool) {

View file

@ -30,6 +30,10 @@ private final class SectionTitleContextItemNode: ASDisplayNode, ContextMenuCusto
return false
}
var needsPadding: Bool {
return false
}
init(presentationData: PresentationData, item: SectionTitleContextItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) {
self.item = item
self.presentationData = presentationData
@ -56,17 +60,17 @@ private final class SectionTitleContextItemNode: ASDisplayNode, ContextMenuCusto
}
func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
let sideInset: CGFloat = 16.0
let sideInset: CGFloat = 18.0 + 4.0
let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - sideInset, height: .greatestFiniteMagnitude))
let height: CGFloat = 28.0
let height: CGFloat = 10.0 + 28.0
return (CGSize(width: textSize.width + sideInset + sideInset, height: height), { size, transition in
let verticalOrigin = floor((size.height - textSize.height) / 2.0)
let verticalOrigin = floor((size.height - 10.0 - textSize.height) / 2.0)
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize)
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - 10.0)))
})
}

View file

@ -47,12 +47,17 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode,
let title: String?
let minValue: CGFloat
let maxValue: CGFloat
var suppressAnimation = false
var value: CGFloat = 1.0 {
didSet {
self.updateValue(transition: .animated(duration: 0.2, curve: .spring))
self.updateValue(transition: self.suppressAnimation ? .immediate :.animated(duration: 0.2, curve: .spring))
}
}
public var needsPadding: Bool {
return true
}
private let valueChanged: (CGFloat, Bool) -> Void
private let hapticFeedback = HapticFeedback()
@ -167,11 +172,21 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode,
}
private func updateValue(transition: ContainedViewLayoutTransition = .immediate) {
let width = self.frame.width
let sideInset: CGFloat = 10.0
let width = self.frame.width - sideInset * 2.0
let range = self.maxValue - self.minValue
let value = (self.value - self.minValue) / range
transition.updateFrameAdditive(node: self.foregroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: value * width, height: self.frame.height)))
var foregroundFrame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: value * width, height: self.frame.height))
var foregroundOffset: CGFloat = 0.0
if foregroundFrame.width < 40.0 {
foregroundOffset = (40.0 - foregroundFrame.width) * 0.5
foregroundFrame = foregroundFrame.insetBy(dx: 0.0, dy: foregroundOffset)
}
transition.updateFrameAdditive(node: self.foregroundNode, frame: foregroundFrame)
transition.updateSublayerTransformOffset(layer: self.foregroundNode.layer, offset: CGPoint(x: 0.0, y: -foregroundOffset))
transition.updateCornerRadius(node: self.foregroundNode, cornerRadius: min(20.0, foregroundFrame.width * 0.5))
let stringValue = String(format: "%.1fx", self.value)
@ -221,9 +236,10 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode,
let _ = self.foregroundTitleNode.updateLayout(CGSize(width: 120.0, height: 100.0))
return (CGSize(width: height * 3.0, height: height), { size, transition in
let leftInset: CGFloat = 17.0
let leftInset: CGFloat = 10.0
let sideInset: CGFloat = 10.0
self.vibrancyEffectView?.frame = CGRect(origin: .zero, size: size)
self.vibrancyEffectView?.frame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: size.width - sideInset * 2.0, height: size.height))
let backgroundTextWidth = self.backgroundTextNode.updateLayout(size: CGSize(width: 70.0, height: .greatestFiniteMagnitude), animated: true).width
@ -235,18 +251,18 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode,
titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - backgroundTitleSize.height) / 2.0)), size: backgroundTitleSize)
if self.title != nil {
textFrame = CGRect(origin: CGPoint(x: size.width - leftInset - backgroundTextWidth, y: floor((height - backgroundTextSize.height) / 2.0)), size: backgroundTextSize)
textFrame = CGRect(origin: CGPoint(x: size.width - sideInset - leftInset - backgroundTextWidth, y: floor((height - backgroundTextSize.height) / 2.0)), size: backgroundTextSize)
} else {
textFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - backgroundTextSize.height) / 2.0)), size: backgroundTextSize)
textFrame = CGRect(origin: CGPoint(x: leftInset + sideInset, y: floor((height - backgroundTextSize.height) / 2.0)), size: backgroundTextSize)
}
transition.updateFrameAdditive(node: self.dimBackgroundTitleNode, frame: titleFrame)
transition.updateFrameAdditive(node: self.backgroundTitleNode, frame: titleFrame)
transition.updateFrameAdditive(node: self.backgroundTitleNode, frame: titleFrame.offsetBy(dx: sideInset, dy: 0.0))
transition.updateFrameAdditive(node: self.foregroundTitleNode, frame: titleFrame)
transition.updateFrameAdditive(node: self.dimBackgroundTextNode, frame: textFrame)
transition.updateFrameAdditive(node: self.backgroundTextNode, frame: textFrame)
transition.updateFrameAdditive(node: self.foregroundTextNode, frame: textFrame)
transition.updateFrameAdditive(node: self.foregroundTextNode, frame: textFrame.offsetBy(dx: -sideInset, dy: 0.0))
})
}
@ -260,7 +276,9 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode,
let translation: CGFloat = gestureRecognizer.translation(in: gestureRecognizer.view).x
let delta = translation / self.bounds.width * range
self.suppressAnimation = true
self.value = max(self.minValue, min(self.maxValue, self.value + delta))
self.suppressAnimation = false
gestureRecognizer.setTranslation(CGPoint(), in: gestureRecognizer.view)
if self.value == 2.0 && previousValue != 2.0 {

View file

@ -2242,14 +2242,17 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
edgeEffectAlpha = self.chatPresentationInterfaceState.chatWallpaper.singleColor != nil ? 0.85 : 0.75
}
var bottomBackgroundEdgeEffectNode: WallpaperEdgeEffectNode?
if let current = self.bottomBackgroundEdgeEffectNode {
bottomBackgroundEdgeEffectNode = current
} else {
if let value = self.backgroundNode.makeEdgeEffectNode() {
bottomBackgroundEdgeEffectNode = value
self.bottomBackgroundEdgeEffectNode = value
self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view)
if self.historyNode.rotated {
if let current = self.bottomBackgroundEdgeEffectNode {
bottomBackgroundEdgeEffectNode = current
} else {
if let value = self.backgroundNode.makeEdgeEffectNode() {
bottomBackgroundEdgeEffectNode = value
self.bottomBackgroundEdgeEffectNode = value
self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view)
}
}
}
if let bottomBackgroundEdgeEffectNode {
@ -2451,13 +2454,15 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
})
var topBackgroundEdgeEffectNode: WallpaperEdgeEffectNode?
if let current = self.topBackgroundEdgeEffectNode {
topBackgroundEdgeEffectNode = current
} else {
if let value = self.backgroundNode.makeEdgeEffectNode() {
topBackgroundEdgeEffectNode = value
self.topBackgroundEdgeEffectNode = value
self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view)
if self.historyNode.rotated {
if let current = self.topBackgroundEdgeEffectNode {
topBackgroundEdgeEffectNode = current
} else {
if let value = self.backgroundNode.makeEdgeEffectNode() {
topBackgroundEdgeEffectNode = value
self.topBackgroundEdgeEffectNode = value
self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view)
}
}
}
if let topBackgroundEdgeEffectNode {

View file

@ -70,6 +70,7 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
}
self.badgeBackgroundView = GlassBackgroundView()
self.badgeBackgroundView.isUserInteractionEnabled = false
self.badgeBackgroundView.alpha = 0.0
self.badgeTextNode = ImmediateAnimatedCountLabelNode()

View file

@ -178,10 +178,8 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
self.title = self.titleProducer(self.presentationData.strings)
if glass {
} else {
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
}
if self.multipleSelection == .always {
@ -413,15 +411,6 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let presentationArguments = self.presentationArguments as? ViewControllerPresentationArguments {
switch presentationArguments.presentationAnimation {
case .modalSheet:
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(cancelPressed))
case .none:
break
}
}
self.contactsNode.contactListNode.enableUpdates = true
}