Theme updates

This commit is contained in:
Ali 2021-05-31 01:23:08 +04:00
parent 648f2e50c2
commit 6428ae2754
18 changed files with 348 additions and 210 deletions

View File

@ -156,7 +156,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
private var phase: Int = 0 private var phase: Int = 0
private let contentView: UIImageView public let contentView: UIImageView
private var validPhase: Int? private var validPhase: Int?
private var invalidated: Bool = false private var invalidated: Bool = false

View File

@ -20,8 +20,6 @@ public func guessMimeTypeByFileExtension(_ ext: String) -> String {
} }
public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, context: AccountContext, peer: Peer, chatLocation: ChatLocation, captionsEnabled: Bool = true, storeCreatedAssets: Bool = true, showFileTooltip: Bool = false, initialCaption: String, hasSchedule: Bool, presentWebSearch: (() -> Void)?, presentSelectionLimitExceeded: @escaping () -> Void, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?) { public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, context: AccountContext, peer: Peer, chatLocation: ChatLocation, captionsEnabled: Bool = true, storeCreatedAssets: Bool = true, showFileTooltip: Bool = false, initialCaption: String, hasSchedule: Bool, presentWebSearch: (() -> Void)?, presentSelectionLimitExceeded: @escaping () -> Void, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?) {
let isSecretChat = peer.id.namespace == Namespaces.Peer.SecretChat
let paintStickersContext = LegacyPaintStickersContext(context: context) let paintStickersContext = LegacyPaintStickersContext(context: context)
paintStickersContext.presentStickersController = { completion in paintStickersContext.presentStickersController = { completion in
return presentStickers({ file, animated, view, rect in return presentStickers({ file, animated, view, rect in

View File

@ -75,10 +75,12 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
self.imageNode.frame = CGRect(origin: CGPoint(), size: size) self.imageNode.frame = CGRect(origin: CGPoint(), size: size)
var colors: [UInt32] = [] var colors: [UInt32] = []
var intensity: CGFloat = 0.5
if case let .gradient(value, _) = wallpaper { if case let .gradient(value, _) = wallpaper {
colors = value colors = value
} else if case let .file(file) = wallpaper { } else if case let .file(file) = wallpaper {
colors = file.settings.colors colors = file.settings.colors
intensity = CGFloat(file.settings.intensity ?? 50) / 100.0
} else if case let .color(color) = wallpaper { } else if case let .color(color) = wallpaper {
colors = [color] colors = [color]
} }
@ -93,7 +95,11 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
self.insertSubnode(gradientNode, belowSubnode: self.imageNode) self.insertSubnode(gradientNode, belowSubnode: self.imageNode)
} }
if intensity < 0.0 {
self.imageNode.layer.compositingFilter = nil
} else {
self.imageNode.layer.compositingFilter = "softLightBlendMode" self.imageNode.layer.compositingFilter = "softLightBlendMode"
}
self.backgroundNode.image = nil self.backgroundNode.image = nil
} else { } else {
if let gradientNode = self.gradientNode { if let gradientNode = self.gradientNode {
@ -101,13 +107,17 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
gradientNode.removeFromSupernode() gradientNode.removeFromSupernode()
} }
if colors.count >= 2 { if intensity < 0.0 {
self.imageNode.layer.compositingFilter = nil
} else {
self.imageNode.layer.compositingFilter = "softLightBlendMode" self.imageNode.layer.compositingFilter = "softLightBlendMode"
}
if colors.count >= 2 {
self.backgroundNode.image = generateGradientImage(size: CGSize(width: 80.0, height: 80.0), colors: colors.map(UIColor.init(rgb:)), locations: [0.0, 1.0], direction: .vertical) self.backgroundNode.image = generateGradientImage(size: CGSize(width: 80.0, height: 80.0), colors: colors.map(UIColor.init(rgb:)), locations: [0.0, 1.0], direction: .vertical)
self.backgroundNode.backgroundColor = nil self.backgroundNode.backgroundColor = nil
} else if colors.count >= 1 { } else if colors.count >= 1 {
self.backgroundNode.image = nil self.backgroundNode.image = nil
self.imageNode.layer.compositingFilter = "softLightBlendMode"
self.backgroundNode.backgroundColor = UIColor(rgb: colors[0]) self.backgroundNode.backgroundColor = UIColor(rgb: colors[0])
} }
} }
@ -147,24 +157,20 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
let imageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError> let imageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>
if wallpaper.isPattern { if wallpaper.isPattern {
var patternColors: [UIColor] = []
var patternColor = UIColor(rgb: 0xd6e2ee, alpha: 0.5)
var patternIntensity: CGFloat = 0.5 var patternIntensity: CGFloat = 0.5
if !file.settings.colors.isEmpty { if !file.settings.colors.isEmpty {
if let intensity = file.settings.intensity { if let intensity = file.settings.intensity {
patternIntensity = CGFloat(intensity) / 100.0 patternIntensity = CGFloat(intensity) / 100.0
} }
patternColor = UIColor(rgb: file.settings.colors[0], alpha: patternIntensity)
patternColors.append(patternColor)
if file.settings.colors.count >= 2 {
patternColors.append(UIColor(rgb: file.settings.colors[1], alpha: patternIntensity))
}
} }
if patternIntensity < 0.0 {
self.imageNode.alpha = 1.0
self.arguments = PatternWallpaperArguments(colors: [.black], rotation: nil, customPatternColor: UIColor(white: 0.0, alpha: 1.0 + patternIntensity))
} else {
self.imageNode.alpha = CGFloat(file.settings.intensity ?? 50) / 100.0 self.imageNode.alpha = CGFloat(file.settings.intensity ?? 50) / 100.0
self.arguments = PatternWallpaperArguments(colors: [.clear], rotation: nil, customPatternColor: UIColor(white: 0.0, alpha: 1.0)) self.arguments = PatternWallpaperArguments(colors: [.clear], rotation: nil, customPatternColor: UIColor(white: 0.0, alpha: 1.0))
}
imageSignal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: .thumbnail, autoFetchFullSize: true) imageSignal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: .thumbnail, autoFetchFullSize: true)
} else { } else {
self.imageNode.alpha = 1.0 self.imageNode.alpha = 1.0

View File

@ -246,7 +246,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
self.messagesContainerNode = ASDisplayNode() self.messagesContainerNode = ASDisplayNode()
self.messagesContainerNode.clipsToBounds = true self.messagesContainerNode.clipsToBounds = true
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) self.messagesContainerNode.transform = CATransform3DMakeScale(-1.0, -1.0, 1.0)
self.colorPanelNode = WallpaperColorPanelNode(theme: self.theme, strings: self.presentationData.strings) self.colorPanelNode = WallpaperColorPanelNode(theme: self.theme, strings: self.presentationData.strings)
self.patternPanelNode = WallpaperPatternPanelNode(context: self.context, theme: self.theme, strings: self.presentationData.strings) self.patternPanelNode = WallpaperPatternPanelNode(context: self.context, theme: self.theme, strings: self.presentationData.strings)
@ -400,12 +400,15 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
} }
} }
self.backgroundNode.update(wallpaper: self.wallpaper)
self.backgroundNode.updateBubbleTheme(bubbleTheme: self.theme, bubbleCorners: self.presentationData.chatBubbleCorners)
self.stateDisposable = (self.statePromise.get() self.stateDisposable = (self.statePromise.get()
|> deliverOn(self.queue) |> deliverOn(self.queue)
|> mapToThrottled { next -> Signal<ThemeColorState, NoError> in |> mapToThrottled { next -> Signal<ThemeColorState, NoError> in
return .single(next) |> then(.complete() |> delay(0.0166667, queue: self.queue)) return .single(next) |> then(.complete() |> delay(0.0166667, queue: self.queue))
} }
|> map { [weak self] state -> (PresentationTheme?, TelegramWallpaper, UIColor, [UInt32], PatternWallpaperArguments, Bool) in |> map { state -> (PresentationTheme?, TelegramWallpaper, UIColor, [UInt32], PatternWallpaperArguments, Bool) in
let accentColor = state.accentColor let accentColor = state.accentColor
var backgroundColors = state.backgroundColors var backgroundColors = state.backgroundColors
let messagesColors = state.messagesColors let messagesColors = state.messagesColors
@ -494,6 +497,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
strongSelf.serviceBackgroundColorPromise.set(.single(serviceBackgroundColor)) strongSelf.serviceBackgroundColorPromise.set(.single(serviceBackgroundColor))
strongSelf.backgroundNode.update(wallpaper: wallpaper) strongSelf.backgroundNode.update(wallpaper: wallpaper)
strongSelf.backgroundNode.updateBubbleTheme(bubbleTheme: strongSelf.theme, bubbleCorners: strongSelf.presentationData.chatBubbleCorners)
strongSelf.ready.set(.single(true)) strongSelf.ready.set(.single(true))
@ -871,7 +875,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
itemNode = node itemNode = node
apply().1(ListViewItemApply(isOnScreen: true)) apply().1(ListViewItemApply(isOnScreen: true))
}) })
itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0) //itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
messageNodes.append(itemNode!) messageNodes.append(itemNode!)
self.messagesContainerNode.addSubnode(itemNode!) self.messagesContainerNode.addSubnode(itemNode!)
} }
@ -881,9 +885,13 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
var bottomOffset: CGFloat = 9.0 + bottomInset var bottomOffset: CGFloat = 9.0 + bottomInset
if let messageNodes = self.messageNodes { if let messageNodes = self.messageNodes {
for itemNode in messageNodes { for itemNode in messageNodes {
let previousFrame = itemNode.frame
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: itemNode.frame.size)) transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: itemNode.frame.size))
bottomOffset += itemNode.frame.height bottomOffset += itemNode.frame.height
itemNode.updateFrame(itemNode.frame, within: layout.size) itemNode.updateFrame(itemNode.frame, within: layout.size)
if case let .animated(duration, curve) = transition {
itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -itemNode.frame.minY + previousFrame.minY), animationCurve: curve, duration: duration)
}
} }
} }
@ -893,7 +901,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem) headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
} else { } else {
dateHeaderNode = headerItem.node() dateHeaderNode = headerItem.node()
dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0) //dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
self.messagesContainerNode.addSubnode(dateHeaderNode) self.messagesContainerNode.addSubnode(dateHeaderNode)
self.dateHeaderNode = dateHeaderNode self.dateHeaderNode = dateHeaderNode
} }
@ -954,7 +962,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
self.chatListBackgroundNode.frame = CGRect(x: bounds.width, y: 0.0, width: bounds.width, height: bounds.height) self.chatListBackgroundNode.frame = CGRect(x: bounds.width, y: 0.0, width: bounds.width, height: bounds.height)
transition.updateFrame(node: self.messagesContainerNode, frame: CGRect(x: 0.0, y: navigationBarHeight, width: bounds.width, height: bounds.height - bottomInset - navigationBarHeight)) transition.updateBounds(node: self.messagesContainerNode, bounds: CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height))
transition.updatePosition(node: self.messagesContainerNode, position: CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height).center)
let backgroundSize = CGSize(width: bounds.width, height: bounds.height - (colorPanelHeight - colorPanelOffset)) let backgroundSize = CGSize(width: bounds.width, height: bounds.height - (colorPanelHeight - colorPanelOffset))
transition.updateFrame(node: self.backgroundContainerNode, frame: CGRect(origin: CGPoint(), size: backgroundSize)) transition.updateFrame(node: self.backgroundContainerNode, frame: CGRect(origin: CGPoint(), size: backgroundSize))
@ -967,12 +976,12 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
transition.updateBounds(node: self.backgroundWrapperNode, bounds: CGRect(origin: CGPoint(), size: layout.size)) transition.updateBounds(node: self.backgroundWrapperNode, bounds: CGRect(origin: CGPoint(), size: layout.size))
let displayOptionButtons = self.state.section == .background let displayOptionButtons = self.state.section == .background
var messagesBottomInset: CGFloat = 0.0 var messagesBottomInset: CGFloat = bottomInset
if displayOptionButtons { if displayOptionButtons {
messagesBottomInset = 46.0 messagesBottomInset += 46.0
} else if chatListPreviewAvailable { } else if chatListPreviewAvailable {
messagesBottomInset = 37.0 messagesBottomInset += 37.0
} }
self.updateChatsLayout(layout: layout, topInset: navigationBarHeight, transition: transition) self.updateChatsLayout(layout: layout, topInset: navigationBarHeight, transition: transition)
self.updateMessagesLayout(layout: layout, bottomInset: messagesBottomInset, transition: messagesTransition) self.updateMessagesLayout(layout: layout, bottomInset: messagesBottomInset, transition: messagesTransition)

View File

@ -375,7 +375,9 @@ final class ThemeGridController: ViewController {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed)) self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed))
self.searchContentNode?.setIsEnabled(false, animated: true) self.searchContentNode?.setIsEnabled(false, animated: true)
self.controllerNode.updateState { state in self.controllerNode.updateState { state in
return state.withUpdatedEditing(true) var state = state
state.editing = true
return state
} }
} }
@ -384,7 +386,9 @@ final class ThemeGridController: ViewController {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed))
self.searchContentNode?.setIsEnabled(true, animated: true) self.searchContentNode?.setIsEnabled(true, animated: true)
self.controllerNode.updateState { state in self.controllerNode.updateState { state in
return state.withUpdatedEditing(false) var state = state
state.editing = false
return state
} }
} }
} }

View File

@ -12,6 +12,7 @@ import GridMessageSelectionNode
final class ThemeGridControllerItem: GridItem { final class ThemeGridControllerItem: GridItem {
let context: AccountContext let context: AccountContext
let wallpaper: TelegramWallpaper let wallpaper: TelegramWallpaper
let wallpaperId: ThemeGridControllerEntry.StableId
let index: Int let index: Int
let editable: Bool let editable: Bool
let selected: Bool let selected: Bool
@ -19,9 +20,10 @@ final class ThemeGridControllerItem: GridItem {
let section: GridSection? = nil let section: GridSection? = nil
init(context: AccountContext, wallpaper: TelegramWallpaper, index: Int, editable: Bool, selected: Bool, interaction: ThemeGridControllerInteraction) { init(context: AccountContext, wallpaper: TelegramWallpaper, wallpaperId: ThemeGridControllerEntry.StableId, index: Int, editable: Bool, selected: Bool, interaction: ThemeGridControllerInteraction) {
self.context = context self.context = context
self.wallpaper = wallpaper self.wallpaper = wallpaper
self.wallpaperId = wallpaperId
self.index = index self.index = index
self.editable = editable self.editable = editable
self.selected = selected self.selected = selected
@ -30,7 +32,7 @@ final class ThemeGridControllerItem: GridItem {
func node(layout: GridNodeLayout, synchronousLoad: Bool) -> GridItemNode { func node(layout: GridNodeLayout, synchronousLoad: Bool) -> GridItemNode {
let node = ThemeGridControllerItemNode() let node = ThemeGridControllerItemNode()
node.setup(context: self.context, wallpaper: self.wallpaper, editable: self.editable, selected: self.selected, interaction: self.interaction, synchronousLoad: synchronousLoad) node.setup(item: self, synchronousLoad: synchronousLoad)
return node return node
} }
@ -39,7 +41,7 @@ final class ThemeGridControllerItem: GridItem {
assertionFailure() assertionFailure()
return return
} }
node.setup(context: self.context, wallpaper: self.wallpaper, editable: self.editable, selected: self.selected, interaction: self.interaction, synchronousLoad: false) node.setup(item: self, synchronousLoad: false)
} }
} }
@ -47,11 +49,11 @@ final class ThemeGridControllerItemNode: GridItemNode {
private let wallpaperNode: SettingsThemeWallpaperNode private let wallpaperNode: SettingsThemeWallpaperNode
private var selectionNode: GridMessageSelectionNode? private var selectionNode: GridMessageSelectionNode?
private var currentState: (AccountContext, TelegramWallpaper, Bool, Bool, Bool)? private var item: ThemeGridControllerItem?
private var interaction: ThemeGridControllerInteraction?
override init() { override init() {
self.wallpaperNode = SettingsThemeWallpaperNode() self.wallpaperNode = SettingsThemeWallpaperNode()
super.init() super.init()
self.addSubnode(self.wallpaperNode) self.addSubnode(self.wallpaperNode)
@ -64,50 +66,35 @@ final class ThemeGridControllerItemNode: GridItemNode {
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
} }
func setup(context: AccountContext, wallpaper: TelegramWallpaper, editable: Bool, selected: Bool, interaction: ThemeGridControllerInteraction, synchronousLoad: Bool) { func setup(item: ThemeGridControllerItem, synchronousLoad: Bool) {
self.interaction = interaction self.item = item
if self.currentState == nil || self.currentState!.0 !== context || wallpaper != self.currentState!.1 || selected != self.currentState!.2 || synchronousLoad != self.currentState!.3 || editable != self.currentState!.4 {
self.currentState = (context, wallpaper, selected, synchronousLoad, editable)
self.updateSelectionState(animated: false) self.updateSelectionState(animated: false)
self.setNeedsLayout() self.setNeedsLayout()
} }
}
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) { @objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state { if case .ended = recognizer.state {
if let (_, wallpaper, _, _, _) = self.currentState { if let item = self.item {
self.interaction?.openWallpaper(wallpaper) item.interaction.openWallpaper(item.wallpaper)
} }
} }
} }
func updateSelectionState(animated: Bool) { func updateSelectionState(animated: Bool) {
if let (context, wallpaper, _, _, editable) = self.currentState { if let item = self.item {
var editing = false let (editing, selectedIds) = item.interaction.selectionState
var id: Int64?
if case let .file(file) = wallpaper { if editing && item.editable {
id = file.id let selected = selectedIds.contains(item.wallpaperId)
} else if case .image = wallpaper {
id = 0
}
var selectedIndices = Set<Int64>()
if let interaction = self.interaction {
let (active, indices) = interaction.selectionState
editing = active
selectedIndices = indices
}
if let id = id, editing && editable {
let selected = selectedIndices.contains(id)
if let selectionNode = self.selectionNode { if let selectionNode = self.selectionNode {
selectionNode.updateSelected(selected, animated: animated) selectionNode.updateSelected(selected, animated: animated)
selectionNode.frame = CGRect(origin: CGPoint(), size: self.bounds.size) selectionNode.frame = CGRect(origin: CGPoint(), size: self.bounds.size)
} else { } else {
let theme = context.sharedContext.currentPresentationData.with { $0 }.theme let theme = item.context.sharedContext.currentPresentationData.with { $0 }.theme
let selectionNode = GridMessageSelectionNode(theme: theme, toggle: { [weak self] value in let selectionNode = GridMessageSelectionNode(theme: theme, toggle: { [weak self] value in
if let strongSelf = self { if let strongSelf = self {
strongSelf.interaction?.toggleWallpaperSelection(id, value) strongSelf.item?.interaction.toggleWallpaperSelection(item.wallpaperId, value)
} }
}) })
@ -139,8 +126,8 @@ final class ThemeGridControllerItemNode: GridItemNode {
super.layout() super.layout()
let bounds = self.bounds let bounds = self.bounds
if let (context, wallpaper, selected, synchronousLoad, _) = self.currentState { if let item = self.item {
self.wallpaperNode.setWallpaper(context: context, wallpaper: wallpaper, selected: selected, size: bounds.size, synchronousLoad: synchronousLoad) self.wallpaperNode.setWallpaper(context: item.context, wallpaper: item.wallpaper, selected: item.selected, size: bounds.size, synchronousLoad: false)
self.selectionNode?.frame = CGRect(origin: CGPoint(), size: bounds.size) self.selectionNode?.frame = CGRect(origin: CGPoint(), size: bounds.size)
} }
} }

View File

@ -18,36 +18,18 @@ import SearchUI
import WallpaperResources import WallpaperResources
struct ThemeGridControllerNodeState: Equatable { struct ThemeGridControllerNodeState: Equatable {
let editing: Bool var editing: Bool
var selectedIndices: Set<Int64> var selectedIds: Set<ThemeGridControllerEntry.StableId>
func withUpdatedEditing(_ editing: Bool) -> ThemeGridControllerNodeState {
return ThemeGridControllerNodeState(editing: editing, selectedIndices: editing ? self.selectedIndices : Set())
}
func withUpdatedSelectedIndices(_ selectedIndices: Set<Int64>) -> ThemeGridControllerNodeState {
return ThemeGridControllerNodeState(editing: self.editing, selectedIndices: selectedIndices)
}
static func ==(lhs: ThemeGridControllerNodeState, rhs: ThemeGridControllerNodeState) -> Bool {
if lhs.editing != rhs.editing {
return false
}
if lhs.selectedIndices != rhs.selectedIndices {
return false
}
return true
}
} }
final class ThemeGridControllerInteraction { final class ThemeGridControllerInteraction {
let openWallpaper: (TelegramWallpaper) -> Void let openWallpaper: (TelegramWallpaper) -> Void
let toggleWallpaperSelection: (Int64, Bool) -> Void let toggleWallpaperSelection: (ThemeGridControllerEntry.StableId, Bool) -> Void
let deleteSelectedWallpapers: () -> Void let deleteSelectedWallpapers: () -> Void
let shareSelectedWallpapers: () -> Void let shareSelectedWallpapers: () -> Void
var selectionState: (Bool, Set<Int64>) = (false, Set()) var selectionState: (Bool, Set<ThemeGridControllerEntry.StableId>) = (false, Set())
init(openWallpaper: @escaping (TelegramWallpaper) -> Void, toggleWallpaperSelection: @escaping (Int64, Bool) -> Void, deleteSelectedWallpapers: @escaping () -> Void, shareSelectedWallpapers: @escaping () -> Void) { init(openWallpaper: @escaping (TelegramWallpaper) -> Void, toggleWallpaperSelection: @escaping (ThemeGridControllerEntry.StableId, Bool) -> Void, deleteSelectedWallpapers: @escaping () -> Void, shareSelectedWallpapers: @escaping () -> Void) {
self.openWallpaper = openWallpaper self.openWallpaper = openWallpaper
self.toggleWallpaperSelection = toggleWallpaperSelection self.toggleWallpaperSelection = toggleWallpaperSelection
self.deleteSelectedWallpapers = deleteSelectedWallpapers self.deleteSelectedWallpapers = deleteSelectedWallpapers
@ -55,7 +37,7 @@ final class ThemeGridControllerInteraction {
} }
} }
private struct ThemeGridControllerEntry: Comparable, Identifiable { struct ThemeGridControllerEntry: Comparable, Identifiable {
enum StableId: Hashable { enum StableId: Hashable {
case builtin case builtin
case color(UInt32) case color(UInt32)
@ -64,14 +46,10 @@ private struct ThemeGridControllerEntry: Comparable, Identifiable {
case image(String) case image(String)
} }
let index: Int var index: Int
let wallpaper: TelegramWallpaper var wallpaper: TelegramWallpaper
let isEditable: Bool var isEditable: Bool
let isSelected: Bool var isSelected: Bool
static func ==(lhs: ThemeGridControllerEntry, rhs: ThemeGridControllerEntry) -> Bool {
return lhs.index == rhs.index && lhs.wallpaper == rhs.wallpaper && lhs.isEditable == rhs.isEditable && lhs.isSelected == rhs.isSelected
}
static func <(lhs: ThemeGridControllerEntry, rhs: ThemeGridControllerEntry) -> Bool { static func <(lhs: ThemeGridControllerEntry, rhs: ThemeGridControllerEntry) -> Bool {
return lhs.index < rhs.index return lhs.index < rhs.index
@ -97,7 +75,7 @@ private struct ThemeGridControllerEntry: Comparable, Identifiable {
} }
func item(context: AccountContext, interaction: ThemeGridControllerInteraction) -> ThemeGridControllerItem { func item(context: AccountContext, interaction: ThemeGridControllerInteraction) -> ThemeGridControllerItem {
return ThemeGridControllerItem(context: context, wallpaper: self.wallpaper, index: self.index, editable: self.isEditable, selected: self.isSelected, interaction: interaction) return ThemeGridControllerItem(context: context, wallpaper: self.wallpaper, wallpaperId: self.stableId, index: self.index, editable: self.isEditable, selected: self.isSelected, interaction: interaction)
} }
} }
@ -149,20 +127,19 @@ private func selectedWallpapers(entries: [ThemeGridControllerEntry]?, state: The
} }
var wallpapers: [TelegramWallpaper] = [] var wallpapers: [TelegramWallpaper] = []
for entry in entries { for entry in entries {
if case let .file(file) = entry.wallpaper { if state.selectedIds.contains(entry.stableId) {
if state.selectedIndices.contains(file.id) {
wallpapers.append(entry.wallpaper) wallpapers.append(entry.wallpaper)
} }
} else if case .image = entry.wallpaper {
if state.selectedIndices.contains(0) {
wallpapers.append(entry.wallpaper)
}
}
} }
return wallpapers return wallpapers
} }
final class ThemeGridControllerNode: ASDisplayNode { final class ThemeGridControllerNode: ASDisplayNode {
private struct Wallpaper: Equatable {
var wallpaper: TelegramWallpaper
var isLocal: Bool
}
private let context: AccountContext private let context: AccountContext
private var presentationData: PresentationData private var presentationData: PresentationData
private var controllerInteraction: ThemeGridControllerInteraction? private var controllerInteraction: ThemeGridControllerInteraction?
@ -176,7 +153,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
var requestDeactivateSearch: (() -> Void)? var requestDeactivateSearch: (() -> Void)?
let ready = ValuePromise<Bool>() let ready = ValuePromise<Bool>()
let wallpapersPromise: Promise<[TelegramWallpaper]> private let wallpapersPromise: Promise<[Wallpaper]>
private var backgroundNode: ASDisplayNode private var backgroundNode: ASDisplayNode
private var separatorNode: ASDisplayNode private var separatorNode: ASDisplayNode
@ -196,7 +173,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
private var selectionPanel: ThemeGridSelectionPanelNode? private var selectionPanel: ThemeGridSelectionPanelNode?
private var selectionPanelSeparatorNode: ASDisplayNode? private var selectionPanelSeparatorNode: ASDisplayNode?
private var selectionPanelBackgroundNode: ASDisplayNode? private var selectionPanelBackgroundNode: NavigationBackgroundNode?
let gridNode: GridNode let gridNode: GridNode
private let leftOverlayNode: ASDisplayNode private let leftOverlayNode: ASDisplayNode
@ -261,22 +238,12 @@ final class ThemeGridControllerNode: ASDisplayNode {
self.resetDescriptionItemNode = ItemListTextItemNode() self.resetDescriptionItemNode = ItemListTextItemNode()
self.resetDescriptionItem = ItemListTextItem(presentationData: ItemListPresentationData(presentationData), text: .plain(presentationData.strings.Wallpaper_ResetWallpapersInfo), sectionId: 0) self.resetDescriptionItem = ItemListTextItem(presentationData: ItemListPresentationData(presentationData), text: .plain(presentationData.strings.Wallpaper_ResetWallpapersInfo), sectionId: 0)
self.currentState = ThemeGridControllerNodeState(editing: false, selectedIndices: Set()) self.currentState = ThemeGridControllerNodeState(editing: false, selectedIds: Set())
self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true) self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true)
let defaultWallpaper = presentationData.theme.chat.defaultWallpaper let defaultWallpaper = presentationData.theme.chat.defaultWallpaper
let wallpapersPromise = Promise<[TelegramWallpaper]>() let wallpapersPromise = Promise<[Wallpaper]>()
wallpapersPromise.set(telegramWallpapers(postbox: context.account.postbox, network: context.account.network)
|> map { wallpapers in
var wallpapers = wallpapers
if !wallpapers.contains(where: {
$0.isBasicallyEqual(to: defaultWallpaper)
}) {
wallpapers.insert(defaultWallpaper, at: 0)
}
return wallpapers
})
self.wallpapersPromise = wallpapersPromise self.wallpapersPromise = wallpapersPromise
let deletedWallpaperSlugsValue = Atomic<Set<String>>(value: Set()) let deletedWallpaperSlugsValue = Atomic<Set<String>>(value: Set())
@ -322,31 +289,47 @@ final class ThemeGridControllerNode: ASDisplayNode {
} }
}, toggleWallpaperSelection: { [weak self] id, value in }, toggleWallpaperSelection: { [weak self] id, value in
if let strongSelf = self { if let strongSelf = self {
strongSelf.updateState { current in strongSelf.updateState { state in
var updated = current.selectedIndices var state = state
if value { if value {
updated.insert(id) state.selectedIds.insert(id)
} else { } else {
updated.remove(id) state.selectedIds.remove(id)
} }
return current.withUpdatedSelectedIndices(updated) return state
} }
} }
}, deleteSelectedWallpapers: { [weak self] in }, deleteSelectedWallpapers: { [weak self] in
let entries = previousEntries.with { $0 } let entries = previousEntries.with { $0 }
if let strongSelf = self, let entries = entries { if let strongSelf = self, let entries = entries {
deleteWallpapers(selectedWallpapers(entries: entries, state: strongSelf.currentState), { [weak self] in let wallpapers = selectedWallpapers(entries: entries, state: strongSelf.currentState)
deleteWallpapers(wallpapers, { [weak self] in
if let strongSelf = self { if let strongSelf = self {
var updatedDeletedSlugs = deletedWallpaperSlugsValue.with { $0 } var updatedDeletedSlugs = deletedWallpaperSlugsValue.with { $0 }
for entry in entries { for entry in entries {
if case let .file(file) = entry.wallpaper, strongSelf.currentState.selectedIndices.contains(file.id) { if case let .file(file) = entry.wallpaper, strongSelf.currentState.selectedIds.contains(entry.stableId) {
updatedDeletedSlugs.insert(file.slug) updatedDeletedSlugs.insert(file.slug)
} }
} }
let _ = deletedWallpaperSlugsValue.swap(updatedDeletedSlugs) let _ = deletedWallpaperSlugsValue.swap(updatedDeletedSlugs)
deletedWallpaperSlugsPromise.set(updatedDeletedSlugs) deletedWallpaperSlugsPromise.set(updatedDeletedSlugs)
let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction in
WallpapersState.update(transaction: transaction, { state in
var state = state
for wallpaper in wallpapers {
if let index = state.wallpapers.firstIndex(where: {
$0.isBasicallyEqual(to: wallpaper)
}) {
state.wallpapers.remove(at: index)
}
}
return state
})
}).start()
} }
}) })
} }
@ -358,7 +341,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
}) })
self.controllerInteraction = interaction self.controllerInteraction = interaction
let transition = combineLatest(wallpapersPromise.get(), deletedWallpaperSlugsPromise.get(), context.sharedContext.presentationData) let transition = combineLatest(self.wallpapersPromise.get(), deletedWallpaperSlugsPromise.get(), context.sharedContext.presentationData)
|> map { wallpapers, deletedWallpaperSlugs, presentationData -> (ThemeGridEntryTransition, Bool) in |> map { wallpapers, deletedWallpaperSlugs, presentationData -> (ThemeGridEntryTransition, Bool) in
var entries: [ThemeGridControllerEntry] = [] var entries: [ThemeGridControllerEntry] = []
var index = 1 var index = 1
@ -369,7 +352,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
} else if presentationData.chatWallpaper.isBasicallyEqual(to: presentationData.theme.chat.defaultWallpaper) { } else if presentationData.chatWallpaper.isBasicallyEqual(to: presentationData.theme.chat.defaultWallpaper) {
isSelectedEditable = false isSelectedEditable = false
} }
entries.insert(ThemeGridControllerEntry(index: 0, wallpaper: presentationData.chatWallpaper, isEditable: isSelectedEditable, isSelected: true), at: 0) entries.insert(ThemeGridControllerEntry(index: 0, wallpaper: presentationData.chatWallpaper, isEditable: false, isSelected: true), at: 0)
var defaultWallpaper: TelegramWallpaper? var defaultWallpaper: TelegramWallpaper?
if !presentationData.chatWallpaper.isBasicallyEqual(to: presentationData.theme.chat.defaultWallpaper) { if !presentationData.chatWallpaper.isBasicallyEqual(to: presentationData.theme.chat.defaultWallpaper) {
@ -397,17 +380,22 @@ final class ThemeGridControllerNode: ASDisplayNode {
var sortedWallpapers: [TelegramWallpaper] = [] var sortedWallpapers: [TelegramWallpaper] = []
if presentationData.theme.overallDarkAppearance { if presentationData.theme.overallDarkAppearance {
var localWallpapers: [TelegramWallpaper] = []
var darkWallpapers: [TelegramWallpaper] = [] var darkWallpapers: [TelegramWallpaper] = []
for wallpaper in wallpapers { for wallpaper in wallpapers {
if case let .file(file) = wallpaper, file.isDark { if wallpaper.isLocal {
darkWallpapers.append(wallpaper) localWallpapers.append(wallpaper.wallpaper)
} else { } else {
sortedWallpapers.append(wallpaper) if case let .file(file) = wallpaper.wallpaper, file.isDark {
darkWallpapers.append(wallpaper.wallpaper)
} else {
sortedWallpapers.append(wallpaper.wallpaper)
} }
} }
sortedWallpapers = darkWallpapers + sortedWallpapers }
sortedWallpapers = localWallpapers + darkWallpapers + sortedWallpapers
} else { } else {
sortedWallpapers = wallpapers sortedWallpapers = wallpapers.map(\.wallpaper)
} }
for wallpaper in sortedWallpapers { for wallpaper in sortedWallpapers {
@ -423,6 +411,9 @@ final class ThemeGridControllerNode: ASDisplayNode {
if case .builtin = wallpaper { if case .builtin = wallpaper {
isEditable = false isEditable = false
} }
if isDefault || presentationData.chatWallpaper.isBasicallyEqual(to: wallpaper) {
isEditable = false
}
if !selected && !isDefault { if !selected && !isDefault {
let entry = ThemeGridControllerEntry(index: index, wallpaper: wallpaper, isEditable: isEditable, isSelected: false) let entry = ThemeGridControllerEntry(index: index, wallpaper: wallpaper, isEditable: isEditable, isSelected: false)
if !entries.contains(where: { $0.stableId == entry.stableId }) { if !entries.contains(where: { $0.stableId == entry.stableId }) {
@ -440,6 +431,8 @@ final class ThemeGridControllerNode: ASDisplayNode {
strongSelf.enqueueTransition(transition) strongSelf.enqueueTransition(transition)
} }
}) })
self.updateWallpapers()
} }
deinit { deinit {
@ -522,7 +515,31 @@ final class ThemeGridControllerNode: ASDisplayNode {
} }
func updateWallpapers() { func updateWallpapers() {
self.wallpapersPromise.set(telegramWallpapers(postbox: self.context.account.postbox, network: self.context.account.network)) self.wallpapersPromise.set(combineLatest(queue: .mainQueue(),
telegramWallpapers(postbox: self.context.account.postbox, network: self.context.account.network),
self.context.sharedContext.accountManager.sharedData(keys: [SharedDataKeys.wallapersState])
)
|> map { remoteWallpapers, sharedData -> [Wallpaper] in
let localState = (sharedData.entries[SharedDataKeys.wallapersState] as? WallpapersState) ?? WallpapersState.default
var wallpapers: [Wallpaper] = []
for wallpaper in localState.wallpapers {
if !wallpapers.contains(where: {
$0.wallpaper.isBasicallyEqual(to: wallpaper)
}) {
wallpapers.append(Wallpaper(wallpaper: wallpaper, isLocal: true))
}
}
for wallpaper in remoteWallpapers {
if !wallpapers.contains(where: {
$0.wallpaper.isBasicallyEqual(to: wallpaper)
}) {
wallpapers.append(Wallpaper(wallpaper: wallpaper, isLocal: false))
}
}
return wallpapers
})
} }
func updatePresentationData(_ presentationData: PresentationData) { func updatePresentationData(_ presentationData: PresentationData) {
@ -562,7 +579,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
self.statePromise.set(state) self.statePromise.set(state)
} }
let selectionState = (self.currentState.editing, self.currentState.selectedIndices) let selectionState = (self.currentState.editing, self.currentState.selectedIds)
if let interaction = self.controllerInteraction, interaction.selectionState != selectionState { if let interaction = self.controllerInteraction, interaction.selectionState != selectionState {
let requestLayout = interaction.selectionState.0 != self.currentState.editing let requestLayout = interaction.selectionState.0 != self.currentState.editing
self.controllerInteraction?.selectionState = selectionState self.controllerInteraction?.selectionState = selectionState
@ -576,7 +593,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
if requestLayout, let (containerLayout, navigationBarHeight) = self.validLayout { if requestLayout, let (containerLayout, navigationBarHeight) = self.validLayout {
self.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.4, curve: .spring)) self.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.4, curve: .spring))
} }
self.selectionPanel?.selectedIndices = selectionState.1 self.selectionPanel?.selectedIds = selectionState.1
} }
} }
@ -678,7 +695,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
if self.currentState.editing { if self.currentState.editing {
let panelHeight: CGFloat let panelHeight: CGFloat
if let selectionPanel = self.selectionPanel { if let selectionPanel = self.selectionPanel {
selectionPanel.selectedIndices = self.currentState.selectedIndices selectionPanel.selectedIds = self.currentState.selectedIds
panelHeight = selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: 0.0, transition: transition, metrics: layout.metrics) panelHeight = selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: 0.0, transition: transition, metrics: layout.metrics)
transition.updateFrame(node: selectionPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: panelHeight))) transition.updateFrame(node: selectionPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: panelHeight)))
if let selectionPanelSeparatorNode = self.selectionPanelSeparatorNode { if let selectionPanelSeparatorNode = self.selectionPanelSeparatorNode {
@ -686,24 +703,21 @@ final class ThemeGridControllerNode: ASDisplayNode {
} }
if let selectionPanelBackgroundNode = self.selectionPanelBackgroundNode { if let selectionPanelBackgroundNode = self.selectionPanelBackgroundNode {
transition.updateFrame(node: selectionPanelBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: insets.bottom + panelHeight))) transition.updateFrame(node: selectionPanelBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: insets.bottom + panelHeight)))
selectionPanelBackgroundNode.update(size: selectionPanelBackgroundNode.bounds.size, transition: transition)
} }
} else { } else {
let selectionPanelBackgroundNode = ASDisplayNode() let selectionPanelBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.rootController.navigationBar.blurredBackgroundColor)
selectionPanelBackgroundNode.isLayerBacked = true
selectionPanelBackgroundNode.backgroundColor = self.presentationData.theme.chat.inputPanel.panelBackgroundColor
self.addSubnode(selectionPanelBackgroundNode) self.addSubnode(selectionPanelBackgroundNode)
self.selectionPanelBackgroundNode = selectionPanelBackgroundNode self.selectionPanelBackgroundNode = selectionPanelBackgroundNode
let selectionPanel = ThemeGridSelectionPanelNode(theme: self.presentationData.theme) let selectionPanel = ThemeGridSelectionPanelNode(theme: self.presentationData.theme)
selectionPanel.backgroundColor = self.presentationData.theme.chat.inputPanel.panelBackgroundColor
selectionPanel.controllerInteraction = self.controllerInteraction selectionPanel.controllerInteraction = self.controllerInteraction
selectionPanel.selectedIndices = self.currentState.selectedIndices selectionPanel.selectedIds = self.currentState.selectedIds
panelHeight = selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: 0.0, transition: .immediate, metrics: layout.metrics) panelHeight = selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: 0.0, transition: .immediate, metrics: layout.metrics)
self.selectionPanel = selectionPanel self.selectionPanel = selectionPanel
self.addSubnode(selectionPanel) self.addSubnode(selectionPanel)
let selectionPanelSeparatorNode = ASDisplayNode() let selectionPanelSeparatorNode = ASDisplayNode()
selectionPanelSeparatorNode.isLayerBacked = true
selectionPanelSeparatorNode.backgroundColor = self.presentationData.theme.chat.inputPanel.panelSeparatorColor selectionPanelSeparatorNode.backgroundColor = self.presentationData.theme.chat.inputPanel.panelSeparatorColor
self.addSubnode(selectionPanelSeparatorNode) self.addSubnode(selectionPanelSeparatorNode)
self.selectionPanelSeparatorNode = selectionPanelSeparatorNode self.selectionPanelSeparatorNode = selectionPanelSeparatorNode
@ -713,6 +727,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
selectionPanelSeparatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: CGSize(width: layout.size.width, height: UIScreenPixel)) selectionPanelSeparatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: CGSize(width: layout.size.width, height: UIScreenPixel))
transition.updateFrame(node: selectionPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: panelHeight))) transition.updateFrame(node: selectionPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: panelHeight)))
transition.updateFrame(node: selectionPanelBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: insets.bottom + panelHeight))) transition.updateFrame(node: selectionPanelBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: insets.bottom + panelHeight)))
selectionPanelBackgroundNode.update(size: selectionPanelBackgroundNode.bounds.size, transition: .immediate)
transition.updateFrame(node: selectionPanelSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel))) transition.updateFrame(node: selectionPanelSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
} }
@ -732,6 +747,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
transition.updateFrame(node: selectionPanelBackgroundNode, frame: selectionPanelBackgroundNode.frame.offsetBy(dx: 0.0, dy: selectionPanel.bounds.size.height + insets.bottom), completion: { [weak selectionPanelSeparatorNode] _ in transition.updateFrame(node: selectionPanelBackgroundNode, frame: selectionPanelBackgroundNode.frame.offsetBy(dx: 0.0, dy: selectionPanel.bounds.size.height + insets.bottom), completion: { [weak selectionPanelSeparatorNode] _ in
selectionPanelSeparatorNode?.removeFromSupernode() selectionPanelSeparatorNode?.removeFromSupernode()
}) })
selectionPanelBackgroundNode.update(size: selectionPanelBackgroundNode.bounds.size, transition: transition)
} }
} }

View File

@ -17,11 +17,11 @@ final class ThemeGridSelectionPanelNode: ASDisplayNode {
private var theme: PresentationTheme private var theme: PresentationTheme
var selectedIndices = Set<Int64>() { var selectedIds = Set<ThemeGridControllerEntry.StableId>() {
didSet { didSet {
if oldValue != self.selectedIndices { if oldValue != self.selectedIds {
self.deleteButton.isEnabled = !self.selectedIndices.isEmpty self.deleteButton.isEnabled = !self.selectedIds.isEmpty
self.shareButton.isEnabled = !self.selectedIndices.isEmpty self.shareButton.isEnabled = !self.selectedIds.isEmpty
} }
} }
} }

View File

@ -475,6 +475,18 @@ public class WallpaperGalleryController: ViewController {
break break
} }
let _ = installWallpaper(account: strongSelf.context.account, wallpaper: wallpaper).start() let _ = installWallpaper(account: strongSelf.context.account, wallpaper: wallpaper).start()
let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction in
WallpapersState.update(transaction: transaction, { state in
var state = state
if let index = state.wallpapers.firstIndex(where: {
$0.isBasicallyEqual(to: wallpaper)
}) {
state.wallpapers.remove(at: index)
}
state.wallpapers.insert(wallpaper, at: 0)
return state
})
}).start()
} }
let applyWallpaper: (TelegramWallpaper) -> Void = { wallpaper in let applyWallpaper: (TelegramWallpaper) -> Void = { wallpaper in
@ -808,9 +820,24 @@ public class WallpaperGalleryController: ViewController {
let patternPanelNode = WallpaperPatternPanelNode(context: self.context, theme: presentationData.theme, strings: presentationData.strings) let patternPanelNode = WallpaperPatternPanelNode(context: self.context, theme: presentationData.theme, strings: presentationData.strings)
patternPanelNode.patternChanged = { [weak self] pattern, intensity, preview in patternPanelNode.patternChanged = { [weak self] pattern, intensity, preview in
if let strongSelf = self, strongSelf.validLayout != nil, let patternInitialWallpaper = strongSelf.patternInitialWallpaper { if let strongSelf = self, strongSelf.validLayout != nil, let patternInitialWallpaper = strongSelf.patternInitialWallpaper {
var colors: [UInt32] = []
switch patternInitialWallpaper {
case let .color(color):
colors = [color]
case let .file(file):
colors = file.settings.colors
case let .gradient(colorsValue, _):
colors = colorsValue
default:
break
}
switch patternInitialWallpaper { switch patternInitialWallpaper {
case .color, .file, .gradient: case .color, .file, .gradient:
strongSelf.updateEntries(pattern: pattern, intensity: intensity, preview: preview) if let pattern = pattern, case let .file(file) = pattern {
let newSettings = WallpaperSettings(blur: file.settings.blur, motion: file.settings.motion, colors: colors, intensity: intensity)
let newWallpaper = TelegramWallpaper.file(id: file.id, accessHash: file.accessHash, isCreator: file.isCreator, isDefault: file.isDefault, isPattern: pattern.isPattern, isDark: file.isDark, slug: file.slug, file: file.file, settings: newSettings)
strongSelf.updateEntries(wallpaper: newWallpaper, preview: preview)
}
default: default:
break break
} }
@ -841,8 +868,8 @@ public class WallpaperGalleryController: ViewController {
var wallpaper: TelegramWallpaper = .gradient(colors, WallpaperSettings(blur: false, motion: false, colors: [], intensity: nil, rotation: nil)) var wallpaper: TelegramWallpaper = .gradient(colors, WallpaperSettings(blur: false, motion: false, colors: [], intensity: nil, rotation: nil))
if case .file = currentWallpaper { if case let .file(file) = currentWallpaper {
wallpaper = currentWallpaper.withUpdatedSettings(WallpaperSettings(blur: false, motion: false, colors: colors, intensity: nil, rotation: nil)) wallpaper = currentWallpaper.withUpdatedSettings(WallpaperSettings(blur: false, motion: false, colors: colors, intensity: file.settings.intensity, rotation: file.settings.rotation))
} }
strongSelf.updateEntries(wallpaper: wallpaper) strongSelf.updateEntries(wallpaper: wallpaper)

View File

@ -336,7 +336,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
case let .color(color): case let .color(color):
displaySize = CGSize(width: 1.0, height: 1.0) displaySize = CGSize(width: 1.0, height: 1.0)
contentSize = displaySize contentSize = displaySize
signal = solidColorImage(UIColor(rgb: color)) signal = .single({ _ in nil })
fetchSignal = .complete() fetchSignal = .complete()
statusSignal = .single(.Local) statusSignal = .single(.Local)
subtitleSignal = .single(nil) subtitleSignal = .single(nil)
@ -346,11 +346,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
case let .gradient(colors, settings): case let .gradient(colors, settings):
displaySize = CGSize(width: 1.0, height: 1.0) displaySize = CGSize(width: 1.0, height: 1.0)
contentSize = displaySize contentSize = displaySize
if colors.count >= 2 { signal = .single({ _ in nil })
signal = gradientImage([UIColor(rgb: colors[0]), UIColor(rgb: colors[1])], rotation: settings.rotation)
} else {
signal = solidColorImage(UIColor(rgb: colors[0]))
}
fetchSignal = .complete() fetchSignal = .complete()
statusSignal = .single(.Local) statusSignal = .single(.Local)
subtitleSignal = .single(nil) subtitleSignal = .single(nil)
@ -389,7 +385,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.backgroundColor = patternColor.withAlphaComponent(1.0) self.backgroundColor = patternColor.withAlphaComponent(1.0)
if let previousEntry = previousEntry, case let .wallpaper(wallpaper, _) = previousEntry, case let .file(previousFile) = wallpaper, file.id == previousFile.id && (file.settings.colors != previousFile.settings.colors || file.settings.intensity != previousFile.settings.intensity) && self.colorPreview == self.arguments.colorPreview { /*if let previousEntry = previousEntry, case let .wallpaper(wallpaper, _) = previousEntry, case let .file(previousFile) = wallpaper, file.id == previousFile.id && (file.settings.colors != previousFile.settings.colors || file.settings.intensity != previousFile.settings.intensity) && self.colorPreview == self.arguments.colorPreview {
let makeImageLayout = self.imageNode.asyncLayout() let makeImageLayout = self.imageNode.asyncLayout()
Queue.concurrentDefaultQueue().async { Queue.concurrentDefaultQueue().async {
@ -405,15 +401,16 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
return return
} else { } else {
patternArguments = PatternWallpaperArguments(colors: patternColors, rotation: file.settings.rotation) patternArguments = PatternWallpaperArguments(colors: patternColors, rotation: file.settings.rotation)
} }*/
self.colorPreview = self.arguments.colorPreview self.colorPreview = self.arguments.colorPreview
if file.settings.colors.count >= 3 { signal = .single({ _ in nil })
/*if file.settings.colors.count >= 3 {
signal = .single({ _ in nil }) signal = .single({ _ in nil })
} else { } else {
signal = patternWallpaperImage(account: self.context.account, accountManager: self.context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true) signal = patternWallpaperImage(account: self.context.account, accountManager: self.context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true)
} }*/
colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: self.context.account.postbox.mediaBox) colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: self.context.account.postbox.mediaBox)
isBlurrable = false isBlurrable = false

View File

@ -263,8 +263,8 @@ final class WallpaperPatternPanelNode: ASDisplayNode {
sliderView.lineSize = 2.0 sliderView.lineSize = 2.0
sliderView.minimumValue = 0.0 sliderView.minimumValue = 0.0
sliderView.startValue = 0.0 sliderView.startValue = 0.0
sliderView.maximumValue = 100.0 sliderView.maximumValue = 200.0
sliderView.value = 40.0 sliderView.value = 150.0
sliderView.disablesInteractiveTransitionGestureRecognizer = true sliderView.disablesInteractiveTransitionGestureRecognizer = true
sliderView.backgroundColor = .clear sliderView.backgroundColor = .clear
sliderView.backColor = self.theme.list.disclosureArrowColor sliderView.backColor = self.theme.list.disclosureArrowColor
@ -312,7 +312,7 @@ final class WallpaperPatternPanelNode: ASDisplayNode {
if let strongSelf = self { if let strongSelf = self {
strongSelf.currentWallpaper = updatedWallpaper strongSelf.currentWallpaper = updatedWallpaper
if let sliderView = strongSelf.sliderView { if let sliderView = strongSelf.sliderView {
strongSelf.patternChanged?(updatedWallpaper, Int32(sliderView.value), false) strongSelf.patternChanged?(updatedWallpaper, Int32(sliderView.value - 100.0), false)
} }
if let subnodes = strongSelf.scrollNode.subnodes { if let subnodes = strongSelf.scrollNode.subnodes {
for case let subnode as SettingsThemeWallpaperNode in subnodes { for case let subnode as SettingsThemeWallpaperNode in subnodes {
@ -354,7 +354,7 @@ final class WallpaperPatternPanelNode: ASDisplayNode {
} }
if let wallpaper = self.currentWallpaper { if let wallpaper = self.currentWallpaper {
self.patternChanged?(wallpaper, Int32(sliderView.value), sliderView.isTracking) self.patternChanged?(wallpaper, Int32(sliderView.value - 100.0), sliderView.isTracking)
} }
} }
@ -368,7 +368,7 @@ final class WallpaperPatternPanelNode: ASDisplayNode {
} }
self.currentWallpaper = wallpaper self.currentWallpaper = wallpaper
self.sliderView?.value = CGFloat(intensity ?? 50) self.sliderView?.value = CGFloat(intensity.flatMap { $0 + 100 } ?? 150)
self.scrollNode.view.contentOffset = CGPoint() self.scrollNode.view.contentOffset = CGPoint()
@ -385,7 +385,7 @@ final class WallpaperPatternPanelNode: ASDisplayNode {
} }
if initialWallpaper == nil, let wallpaper = self.currentWallpaper, let sliderView = self.sliderView { if initialWallpaper == nil, let wallpaper = self.currentWallpaper, let sliderView = self.sliderView {
self.patternChanged?(wallpaper, Int32(sliderView.value), false) self.patternChanged?(wallpaper, Int32(sliderView.value - 100.0), false)
} }
if let selectedNode = selectedNode { if let selectedNode = selectedNode {

View File

@ -349,6 +349,7 @@ private enum SharedDataKeyValues: Int32 {
case autodownloadSettings = 5 case autodownloadSettings = 5
case themeSettings = 6 case themeSettings = 6
case countriesList = 7 case countriesList = 7
case wallapersState = 8
} }
public struct SharedDataKeys { public struct SharedDataKeys {
@ -393,6 +394,12 @@ public struct SharedDataKeys {
key.setInt32(0, value: SharedDataKeyValues.countriesList.rawValue) key.setInt32(0, value: SharedDataKeyValues.countriesList.rawValue)
return key return key
}() }()
public static let wallapersState: ValueBoxKey = {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: SharedDataKeyValues.wallapersState.rawValue)
return key
}()
} }
public func applicationSpecificItemCacheCollectionId(_ value: Int8) -> Int8 { public func applicationSpecificItemCacheCollectionId(_ value: Int8) -> Int8 {

View File

@ -0,0 +1,35 @@
import Postbox
import SwiftSignalKit
public struct WallpapersState: PreferencesEntry, Equatable {
public var wallpapers: [TelegramWallpaper]
public static var `default`: WallpapersState {
return WallpapersState(wallpapers: [])
}
public init(wallpapers: [TelegramWallpaper]) {
self.wallpapers = wallpapers
}
public init(decoder: PostboxDecoder) {
self.wallpapers = decoder.decodeObjectArrayWithDecoderForKey("wallpapers")
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeObjectArray(self.wallpapers, forKey: "wallpapers")
}
public func isEqual(to: PreferencesEntry) -> Bool {
return self == (to as? WallpapersState)
}
}
public extension WallpapersState {
static func update(transaction: AccountManagerModifier, _ f: (WallpapersState) -> WallpapersState) {
transaction.updateSharedData(SharedDataKeys.wallapersState, { current in
let item = (transaction.getSharedData(SharedDataKeys.wallapersState) as? WallpapersState) ?? WallpapersState(wallpapers: [])
return f(item)
})
}
}

View File

@ -174,6 +174,7 @@ private var declaredEncodables: Void = {
declareEncodable(CachedPeerExportedInvitations.self, f: { CachedPeerExportedInvitations(decoder: $0) }) declareEncodable(CachedPeerExportedInvitations.self, f: { CachedPeerExportedInvitations(decoder: $0) })
declareEncodable(ExportedInvitation.self, f: { ExportedInvitation(decoder: $0) }) declareEncodable(ExportedInvitation.self, f: { ExportedInvitation(decoder: $0) })
declareEncodable(CachedDisplayAsPeers.self, f: { CachedDisplayAsPeers(decoder: $0) }) declareEncodable(CachedDisplayAsPeers.self, f: { CachedDisplayAsPeers(decoder: $0) })
declareEncodable(WallpapersState.self, f: { WallpapersState(decoder: $0) })
return return
}() }()

View File

@ -246,7 +246,7 @@ public final class PrincipalThemeEssentialGraphics {
let emptyImage = UIImage() let emptyImage = UIImage()
if preview { if preview {
self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundIncomingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) self.chatMessageBackgroundIncomingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) self.chatMessageBackgroundIncomingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
@ -263,30 +263,33 @@ public final class PrincipalThemeEssentialGraphics {
self.checkBubbleFullImage = generateCheckImage(partial: false, color: theme.message.outgoingCheckColor, width: 11.0)! self.checkBubbleFullImage = generateCheckImage(partial: false, color: theme.message.outgoingCheckColor, width: 11.0)!
self.checkBubblePartialImage = generateCheckImage(partial: true, color: theme.message.outgoingCheckColor, width: 11.0)! self.checkBubblePartialImage = generateCheckImage(partial: true, color: theme.message.outgoingCheckColor, width: 11.0)!
self.chatMessageBackgroundIncomingHighlightedImage = emptyImage self.chatMessageBackgroundIncomingHighlightedImage = emptyImage
self.chatMessageBackgroundIncomingMergedTopMaskImage = emptyImage self.chatMessageBackgroundIncomingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) self.chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundIncomingMergedTopShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundIncomingMergedTopShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundIncomingMergedTopHighlightedImage = emptyImage self.chatMessageBackgroundIncomingMergedTopHighlightedImage = emptyImage
self.chatMessageBackgroundIncomingMergedTopSideMaskImage = emptyImage self.chatMessageBackgroundIncomingMergedTopSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedTopSideImage = emptyImage self.chatMessageBackgroundIncomingMergedTopSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedTopSideOutlineImage = emptyImage self.chatMessageBackgroundIncomingMergedTopSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundIncomingMergedTopSideShadowImage = emptyImage self.chatMessageBackgroundIncomingMergedTopSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = emptyImage self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = emptyImage
self.chatMessageBackgroundIncomingMergedBottomMaskImage = emptyImage self.chatMessageBackgroundIncomingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) self.chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundIncomingMergedBottomShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundIncomingMergedBottomShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = emptyImage self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = emptyImage
self.chatMessageBackgroundIncomingMergedBothMaskImage = emptyImage self.chatMessageBackgroundIncomingMergedBothMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedBothImage = emptyImage
self.chatMessageBackgroundIncomingMergedBothOutlineImage = emptyImage self.chatMessageBackgroundIncomingMergedBothImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedBothShadowImage = emptyImage self.chatMessageBackgroundIncomingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundIncomingMergedBothShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundIncomingMergedBothHighlightedImage = emptyImage self.chatMessageBackgroundIncomingMergedBothHighlightedImage = emptyImage
self.chatMessageBackgroundIncomingMergedSideMaskImage = emptyImage self.chatMessageBackgroundIncomingMergedSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .side, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedSideImage = emptyImage self.chatMessageBackgroundIncomingMergedSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedSideOutlineImage = emptyImage
self.chatMessageBackgroundIncomingMergedSideShadowImage = emptyImage self.chatMessageBackgroundIncomingMergedSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundIncomingMergedSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundOutgoingMergedSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .side, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedSideHighlightedImage = emptyImage self.chatMessageBackgroundIncomingMergedSideHighlightedImage = emptyImage
self.chatMessageBackgroundOutgoingHighlightedImage = emptyImage self.chatMessageBackgroundOutgoingHighlightedImage = emptyImage
self.chatMessageBackgroundOutgoingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
@ -294,10 +297,10 @@ public final class PrincipalThemeEssentialGraphics {
self.chatMessageBackgroundOutgoingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundOutgoingMergedTopShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundOutgoingMergedTopShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = emptyImage self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = emptyImage
self.chatMessageBackgroundOutgoingMergedTopSideMaskImage = emptyImage self.chatMessageBackgroundOutgoingMergedTopSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundOutgoingMergedTopSideImage = emptyImage self.chatMessageBackgroundOutgoingMergedTopSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
self.chatMessageBackgroundOutgoingMergedTopSideOutlineImage = emptyImage self.chatMessageBackgroundOutgoingMergedTopSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundOutgoingMergedTopSideShadowImage = emptyImage self.chatMessageBackgroundOutgoingMergedTopSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = emptyImage self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = emptyImage
self.chatMessageBackgroundOutgoingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
@ -309,10 +312,9 @@ public final class PrincipalThemeEssentialGraphics {
self.chatMessageBackgroundOutgoingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundOutgoingMergedBothShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundOutgoingMergedBothShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = emptyImage self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = emptyImage
self.chatMessageBackgroundOutgoingMergedSideMaskImage = emptyImage self.chatMessageBackgroundOutgoingMergedSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
self.chatMessageBackgroundOutgoingMergedSideImage = emptyImage self.chatMessageBackgroundOutgoingMergedSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundOutgoingMergedSideOutlineImage = emptyImage self.chatMessageBackgroundOutgoingMergedSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundOutgoingMergedSideShadowImage = emptyImage
self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = emptyImage self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = emptyImage
self.checkMediaFullImage = emptyImage self.checkMediaFullImage = emptyImage
self.checkMediaPartialImage = emptyImage self.checkMediaPartialImage = emptyImage

View File

@ -64,9 +64,10 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
private weak var backgroundNode: WallpaperBackgroundNode? private weak var backgroundNode: WallpaperBackgroundNode?
private var maskView: UIImageView? private var maskView: UIImageView?
private var fixedMaskMode: Bool? private var fixedMaskMode: Bool?
private var absolutePosition: (CGRect, CGSize)?
var hasImage: Bool { var hasImage: Bool {
return self.backgroundContent != nil return self.backgroundContent != nil
} }
@ -145,12 +146,18 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
case .incoming: case .incoming:
if let backgroundContent = backgroundNode?.makeBubbleBackground(for: .incoming) { if let backgroundContent = backgroundNode?.makeBubbleBackground(for: .incoming) {
backgroundContent.frame = self.bounds backgroundContent.frame = self.bounds
if let (rect, containerSize) = self.absolutePosition {
backgroundContent.update(rect: rect, within: containerSize)
}
self.backgroundContent = backgroundContent self.backgroundContent = backgroundContent
self.insertSubnode(backgroundContent, at: 0) self.insertSubnode(backgroundContent, at: 0)
} }
case .outgoing: case .outgoing:
if let backgroundContent = backgroundNode?.makeBubbleBackground(for: .outgoing) { if let backgroundContent = backgroundNode?.makeBubbleBackground(for: .outgoing) {
backgroundContent.frame = self.bounds backgroundContent.frame = self.bounds
if let (rect, containerSize) = self.absolutePosition {
backgroundContent.update(rect: rect, within: containerSize)
}
self.backgroundContent = backgroundContent self.backgroundContent = backgroundContent
self.insertSubnode(backgroundContent, at: 0) self.insertSubnode(backgroundContent, at: 0)
} }
@ -164,18 +171,15 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
} }
func update(rect: CGRect, within containerSize: CGSize) { func update(rect: CGRect, within containerSize: CGSize) {
//self.backgroundContent.frame = CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize) self.absolutePosition = (rect, containerSize)
self.backgroundContent?.update(rect: rect, within: containerSize) self.backgroundContent?.update(rect: rect, within: containerSize)
} }
func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
//let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: animationCurve)
//transition.animatePositionAdditive(node: self.backgroundContent, offset: CGPoint(x: -value.x, y: -value.y))
self.backgroundContent?.offset(value: value, animationCurve: animationCurve, duration: duration) self.backgroundContent?.offset(value: value, animationCurve: animationCurve, duration: duration)
} }
func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) { func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
//self.backgroundContent.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: 0.0, y: value)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true)
self.backgroundContent?.offsetSpring(value: value, duration: duration, damping: damping) self.backgroundContent?.offsetSpring(value: value, duration: duration, damping: damping)
} }

View File

@ -28,6 +28,8 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
private weak var backgroundNode: WallpaperBackgroundNode? private weak var backgroundNode: WallpaperBackgroundNode?
private var index: SparseBag<BubbleBackgroundNode>.Index? private var index: SparseBag<BubbleBackgroundNode>.Index?
private var currentLayout: (rect: CGRect, containerSize: CGSize)?
init(backgroundNode: WallpaperBackgroundNode, bubbleType: BubbleType) { init(backgroundNode: WallpaperBackgroundNode, bubbleType: BubbleType) {
self.backgroundNode = backgroundNode self.backgroundNode = backgroundNode
self.bubbleType = bubbleType self.bubbleType = bubbleType
@ -135,9 +137,15 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
cleanWallpaperNode.removeFromSupernode() cleanWallpaperNode.removeFromSupernode()
} }
} }
if let (rect, containerSize) = self.currentLayout {
self.update(rect: rect, within: containerSize)
}
} }
public func update(rect: CGRect, within containerSize: CGSize) { public func update(rect: CGRect, within containerSize: CGSize) {
self.currentLayout = (rect, containerSize)
self.contentNode.frame = CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize) self.contentNode.frame = CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize)
if let cleanWallpaperNode = self.cleanWallpaperNode { if let cleanWallpaperNode = self.cleanWallpaperNode {
cleanWallpaperNode.frame = CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize) cleanWallpaperNode.frame = CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize)
@ -183,6 +191,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
private let contentNode: ASDisplayNode private let contentNode: ASDisplayNode
private var gradientBackgroundNode: GradientBackgroundNode? private var gradientBackgroundNode: GradientBackgroundNode?
private let patternImageNode: TransformImageNode private let patternImageNode: TransformImageNode
private var invertPattern: Bool = false
private var validLayout: CGSize? private var validLayout: CGSize?
private var wallpaper: TelegramWallpaper? private var wallpaper: TelegramWallpaper?
@ -255,7 +264,6 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
self.contentNode.contentMode = self.imageContentMode self.contentNode.contentMode = self.imageContentMode
self.patternImageNode = TransformImageNode() self.patternImageNode = TransformImageNode()
self.patternImageNode.layer.compositingFilter = "softLightBlendMode"
super.init() super.init()
@ -369,10 +377,31 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
let signal = patternWallpaperImage(account: self.context.account, accountManager: self.context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true) let signal = patternWallpaperImage(account: self.context.account, accountManager: self.context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true)
self.patternImageNode.setSignal(signal) self.patternImageNode.setSignal(signal)
} }
self.patternImageNode.alpha = CGFloat(settings.intensity ?? 50) / 100.0 let intensity = CGFloat(settings.intensity ?? 50) / 100.0
if intensity < 0 {
self.patternImageNode.alpha = 1.0
self.patternImageNode.layer.compositingFilter = nil
} else {
self.patternImageNode.alpha = intensity
self.patternImageNode.layer.compositingFilter = "softLightBlendMode"
}
self.patternImageNode.isHidden = false self.patternImageNode.isHidden = false
self.invertPattern = intensity < 0
if self.invertPattern {
self.backgroundColor = .black
let contentAlpha = abs(intensity)
self.gradientBackgroundNode?.contentView.alpha = contentAlpha
self.contentNode.alpha = contentAlpha
} else {
self.backgroundColor = nil
self.gradientBackgroundNode?.contentView.alpha = 1.0
self.contentNode.alpha = 1.0
}
default: default:
self.patternImageNode.isHidden = true self.patternImageNode.isHidden = true
self.backgroundColor = nil
self.gradientBackgroundNode?.contentView.alpha = 1.0
self.contentNode.alpha = 1.0
} }
self.updateBubbles() self.updateBubbles()
@ -395,7 +424,16 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
} }
let makeImageLayout = self.patternImageNode.asyncLayout() let makeImageLayout = self.patternImageNode.asyncLayout()
let applyImage = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets(), custom: PatternWallpaperArguments(colors: [.clear], rotation: nil, customPatternColor: .black, preview: false))) let patternBackgroundColor: UIColor
let patternColor: UIColor
if self.invertPattern {
patternColor = .clear
patternBackgroundColor = .black
} else {
patternColor = .black
patternBackgroundColor = .clear
}
let applyImage = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets(), custom: PatternWallpaperArguments(colors: [patternBackgroundColor], rotation: nil, customPatternColor: patternColor, preview: false)))
applyImage() applyImage()
transition.updateFrame(node: self.patternImageNode, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(node: self.patternImageNode, frame: CGRect(origin: CGPoint(), size: size))

View File

@ -314,6 +314,9 @@ public struct PatternWallpaperArguments: TransformImageCustomArguments {
let array = NSMutableArray() let array = NSMutableArray()
array.addObjects(from: self.colors) array.addObjects(from: self.colors)
array.add(NSNumber(value: self.rotation ?? 0)) array.add(NSNumber(value: self.rotation ?? 0))
if let customPatternColor = customPatternColor {
array.add(NSNumber(value: customPatternColor.argb))
}
array.add(NSNumber(value: self.preview)) array.add(NSNumber(value: self.preview))
return array return array
} }
@ -500,7 +503,11 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da
c.interpolationQuality = customArguments.preview ? .low : .medium c.interpolationQuality = customArguments.preview ? .low : .medium
c.clip(to: fittedRect, mask: image) c.clip(to: fittedRect, mask: image)
if let customPatternColor = customArguments.customPatternColor, customPatternColor.alpha < 1.0 {
c.setBlendMode(.copy)
} else {
c.setBlendMode(.normal) c.setBlendMode(.normal)
}
if colors.count >= 3 && customArguments.customPatternColor == nil { if colors.count >= 3 && customArguments.customPatternColor == nil {
c.setBlendMode(.softLight) c.setBlendMode(.softLight)