Finalize theme color selection UI

This commit is contained in:
Ilya Laktyushin 2019-11-17 22:23:26 +04:00
parent 91a292b503
commit 98a72955f5
41 changed files with 3658 additions and 3248 deletions

View File

@ -5114,3 +5114,4 @@ Any member of this group will be able to see messages in the channel.";
"Theme.Colors.Messages" = "Messages";
"Theme.Colors.ColorWallpaperWarning" = "Are you sure you want to change your chat wallpaper to a color?";
"Theme.Colors.ColorWallpaperWarningProceed" = "Proceed";

View File

@ -435,6 +435,7 @@ public protocol SharedAccountContext: class {
func makeChatListController(context: AccountContext, groupId: PeerGroupId, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool, previewing: Bool, enableDebugActions: Bool) -> ChatListController
func makeChatController(context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, botStart: ChatControllerInitialBotStart?, mode: ChatControllerPresentationMode) -> ChatController
func makeChatMessagePreviewItem(context: AccountContext, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?) -> ListViewItem
func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader
func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController?
func makeContactSelectionController(_ params: ContactSelectionControllerParams) -> ContactSelectionController
func makeContactMultiselectionController(_ params: ContactMultiselectionControllerParams) -> ContactMultiselectionController

View File

@ -42,6 +42,10 @@ public final class ChatListSearchItemHeader: ListViewItemHeader {
public func node() -> ListViewItemHeaderNode {
return ChatListSearchItemHeaderNode(type: self.type, theme: self.theme, strings: self.strings, actionTitle: self.actionTitle, action: self.action)
}
public func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) {
}
}
public final class ChatListSearchItemHeaderNode: ListViewItemHeaderNode {

View File

@ -22,6 +22,10 @@ final class ContactListNameIndexHeader: Equatable, ListViewItemHeader {
return ContactListNameIndexHeaderNode(theme: self.theme, letter: self.letter)
}
func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) {
}
static func ==(lhs: ContactListNameIndexHeader, rhs: ContactListNameIndexHeader) -> Bool {
return lhs.id == rhs.id
}

View File

@ -297,6 +297,43 @@ public func generateTintedImage(image: UIImage?, color: UIColor, backgroundColor
return tintedImage
}
public func generateGradientTintedImage(image: UIImage?, colors: [UIColor]) -> UIImage? {
guard let image = image else {
return nil
}
let imageSize = image.size
UIGraphicsBeginImageContextWithOptions(imageSize, false, image.scale)
if let context = UIGraphicsGetCurrentContext() {
let imageRect = CGRect(origin: CGPoint(), size: imageSize)
context.saveGState()
context.translateBy(x: imageRect.midX, y: imageRect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -imageRect.midX, y: -imageRect.midY)
context.clip(to: imageRect, mask: image.cgImage!)
let gradientColors = colors.map { $0.cgColor } as CFArray
let delta: CGFloat = 1.0 / (CGFloat(colors.count) - 1.0)
var locations: [CGFloat] = []
for i in 0 ..< colors.count {
locations.append(delta * CGFloat(i))
}
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: imageRect.height), end: CGPoint(x: 0.0, y: 0.0), options: CGGradientDrawingOptions())
context.restoreGState()
}
let tintedImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return tintedImage
}
public func generateScaledImage(image: UIImage?, size: CGSize, opaque: Bool = true, scale: CGFloat? = nil) -> UIImage? {
guard let image = image else {
return nil

View File

@ -15,6 +15,7 @@ public protocol ListViewItemHeader: class {
var height: CGFloat { get }
func node() -> ListViewItemHeaderNode
func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?)
}
open class ListViewItemHeaderNode: ASDisplayNode {
@ -55,7 +56,7 @@ open class ListViewItemHeaderNode: ASDisplayNode {
} else {
super.init()
self.setViewBlock({
self.setViewBlock({
return CASeeThroughTracingView()
})
}

View File

@ -38,6 +38,12 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
}
}
public var imageContentMode: UIView.ContentMode {
didSet {
self.contentNode.contentMode = self.imageContentMode
}
}
func updateScale() {
if self.motionEnabled {
let scale = (self.frame.width + motionAmount * 2.0) / self.frame.width
@ -48,8 +54,10 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
}
public override init() {
self.imageContentMode = .scaleAspectFill
self.contentNode = ASDisplayNode()
self.contentNode.contentMode = .scaleAspectFill
self.contentNode.contentMode = self.imageContentMode
super.init()

View File

@ -144,7 +144,9 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
}
public var selectedIndexChanged: (Int) -> Void = { _ in }
public var selectedIndexShouldChange: (Int) -> Bool = { _ in return true }
public var selectedIndexShouldChange: (Int, @escaping (Bool) -> Void) -> Void = { _, f in
f(true)
}
public init(theme: SegmentedControlTheme, items: [SegmentedControlItem], selectedIndex: Int) {
self.theme = theme
@ -348,15 +350,15 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
return
}
guard self.selectedIndexShouldChange(index) else {
return
}
self._selectedIndex = index
self.selectedIndexChanged(index)
if let layout = self.validLayout {
let _ = self.updateLayout(layout, transition: .animated(duration: 0.2, curve: .slide))
}
self.selectedIndexShouldChange(index, { [weak self] commit in
if let strongSelf = self, commit {
strongSelf._selectedIndex = index
strongSelf.selectedIndexChanged(index)
if let layout = strongSelf.validLayout {
let _ = strongSelf.updateLayout(layout, transition: .animated(duration: 0.2, curve: .slide))
}
}
})
}
public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
@ -386,14 +388,18 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
case .ended:
if let gestureSelectedIndex = self.gestureSelectedIndex {
if gestureSelectedIndex != self.selectedIndex {
if self.selectedIndexShouldChange(gestureSelectedIndex) {
self._selectedIndex = gestureSelectedIndex
self.selectedIndexChanged(self._selectedIndex)
} else {
if let layout = self.validLayout {
let _ = self.updateLayout(layout, transition: .animated(duration: 0.35, curve: .slide))
self.selectedIndexShouldChange(gestureSelectedIndex, { [weak self] commit in
if let strongSelf = self {
if commit {
strongSelf._selectedIndex = gestureSelectedIndex
strongSelf.selectedIndexChanged(gestureSelectedIndex)
} else {
if let layout = strongSelf.validLayout {
let _ = strongSelf.updateLayout(layout, transition: .animated(duration: 0.2, curve: .slide))
}
}
}
}
})
}
self.gestureSelectedIndex = nil
}

View File

@ -9,43 +9,43 @@ import SyncCore
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
import PresentationDataUtils
private let colors: [Int32] = [0x007aff, 0x00c2ed, 0x29b327, 0xeb6ca4, 0xf08200, 0x9472ee, 0xd33213, 0xedb400, 0x6d839e]
final class ThemeAccentColorController: ViewController {
private let context: AccountContext
private let currentTheme: PresentationThemeReference
private let themeReference: PresentationThemeReference
private let section: ThemeColorSection
let segmentedTitleView: ThemeColorSegmentedTitleView
private let initialBackgroundColor: UIColor?
private var presentationData: PresentationData
private var controllerNode: ThemeAccentColorControllerNode {
return self.displayNode as! ThemeAccentColorControllerNode
}
private var presentationData: PresentationData
let segmentedTitleView: ThemeColorSegmentedTitleView
private let _ready = Promise<Bool>()
override public var ready: Promise<Bool> {
return self._ready
}
init(context: AccountContext, currentTheme: PresentationThemeReference, section: ThemeColorSection) {
var completion: (() -> Void)?
init(context: AccountContext, themeReference: PresentationThemeReference, section: ThemeColorSection) {
self.context = context
self.currentTheme = currentTheme
self.themeReference = themeReference
self.section = section
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
// var color: UIColor
// if let currentColor = currentColor {
// color = currentColor
// } else if let randomColor = colors.randomElement() {
// color = UIColor(rgb: UInt32(bitPattern: randomColor))
// } else {
// color = defaultDayAccentColor
// }
self.segmentedTitleView = ThemeColorSegmentedTitleView(theme: self.presentationData.theme, strings: self.presentationData.strings, selectedSection: section)
self.segmentedTitleView = ThemeColorSegmentedTitleView(theme: self.presentationData.theme, strings: self.presentationData.strings, selectedSection: .accent)
if section == .background {
self.initialBackgroundColor = colors.randomElement().flatMap { UIColor(rgb: UInt32(bitPattern: $0)) }
} else {
self.initialBackgroundColor = nil
}
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings))
@ -58,8 +58,30 @@ final class ThemeAccentColorController: ViewController {
}
}
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView())
self.segmentedTitleView.shouldUpdateSection = { [weak self] section, f in
guard let strongSelf = self else {
f(false)
return
}
guard section == .background else {
f(true)
return
}
if strongSelf.controllerNode.requiresWallpaperChange {
let controller = textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Theme_Colors_ColorWallpaperWarning, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
f(false)
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Theme_Colors_ColorWallpaperWarningProceed, action: {
f(true)
})])
strongSelf.present(controller, in: .window(.root))
} else {
f(true)
}
}
self.navigationItem.titleView = self.segmentedTitleView
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView())
}
required init(coder aDecoder: NSCoder) {
@ -69,7 +91,7 @@ final class ThemeAccentColorController: ViewController {
override func loadDisplayNode() {
super.loadDisplayNode()
self.displayNode = ThemeAccentColorControllerNode(context: self.context, currentTheme: self.currentTheme, theme: self.presentationData.theme, dismiss: { [weak self] in
self.displayNode = ThemeAccentColorControllerNode(context: self.context, themeReference: self.themeReference, theme: self.presentationData.theme, dismiss: { [weak self] in
if let strongSelf = self {
strongSelf.dismiss()
}
@ -85,18 +107,34 @@ final class ThemeAccentColorController: ViewController {
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
var themeSpecificAccentColors = current.themeSpecificAccentColors
//let color = PresentationThemeAccentColor(baseColor: .custom, value: Int32(bitPattern: strongSelf.controllerNode.color))
//themeSpecificAccentColors[currentTheme.index] = color
var themeSpecificBubbleColors = current.themeSpecificBubbleColors
let color = PresentationThemeAccentColor(baseColor: .custom, value: Int32(bitPattern: state.accentColor.rgb))
themeSpecificAccentColors[currentTheme.index] = color
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: nil, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil) ?? defaultPresentationTheme
if let wallpaper = current.themeSpecificChatWallpapers[currentTheme.index], wallpaper.hasWallpaper {
} else {
themeSpecificChatWallpapers[currentTheme.index] = theme.chat.defaultWallpaper
var wallpaper = themeSpecificChatWallpapers[currentTheme.index]
if let backgroundColors = state.backgroundColors {
if let bottomColor = backgroundColors.1 {
wallpaper = .gradient(Int32(bitPattern: backgroundColors.0.rgb), Int32(bitPattern: bottomColor.rgb))
} else {
wallpaper = .color(Int32(bitPattern: backgroundColors.0.rgb))
}
}
themeSpecificChatWallpapers[currentTheme.index] = wallpaper
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
var bubbleColors: PresentationThemeColorPair?
if let messagesColors = state.messagesColors {
if let secondColor = messagesColors.1 {
bubbleColors = PresentationThemeColorPair(color: Int32(bitPattern: messagesColors.0.rgb), optionalColor: Int32(bitPattern: secondColor.rgb))
} else {
bubbleColors = PresentationThemeColorPair(color: Int32(bitPattern: messagesColors.0.rgb), optionalColor: nil)
}
}
themeSpecificBubbleColors[currentTheme.index] = bubbleColors
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificBubbleColors: themeSpecificBubbleColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
}) |> deliverOnMainQueue).start(completed: { [weak self] in
if let strongSelf = self {
strongSelf.completion?()
strongSelf.dismiss()
}
})
@ -128,17 +166,21 @@ final class ThemeAccentColorController: ViewController {
if let customWallpaper = settings.themeSpecificChatWallpapers[themeReference.index] {
wallpaper = customWallpaper
} else {
let theme = makePresentationTheme(mediaBox: strongSelf.context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: nil, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil) ?? defaultPresentationTheme
let theme = makePresentationTheme(mediaBox: strongSelf.context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: nil, bubbleColors: nil, serviceBackgroundColor: defaultServiceBackgroundColor) ?? defaultPresentationTheme
wallpaper = theme.chat.defaultWallpaper
}
let backgroundColors: (UIColor, UIColor?)?
if case let .color(color) = wallpaper {
backgroundColors = (UIColor(rgb: UInt32(bitPattern: color)), nil)
} else if case let .gradient(topColor, bottomColor) = wallpaper {
backgroundColors = (UIColor(rgb: UInt32(bitPattern: topColor)), UIColor(rgb: UInt32(bitPattern: bottomColor)))
if let initialBackgroundColor = strongSelf.initialBackgroundColor {
backgroundColors = (initialBackgroundColor, nil)
} else {
backgroundColors = nil
if case let .color(color) = wallpaper {
backgroundColors = (UIColor(rgb: UInt32(bitPattern: color)), nil)
} else if case let .gradient(topColor, bottomColor) = wallpaper {
backgroundColors = (UIColor(rgb: UInt32(bitPattern: topColor)), UIColor(rgb: UInt32(bitPattern: bottomColor)))
} else {
backgroundColors = nil
}
}
let messageColors: (UIColor, UIColor?)?

View File

@ -34,12 +34,14 @@ enum ThemeColorSection: Int {
struct ThemeColorState {
fileprivate var section: ThemeColorSection?
fileprivate var colorPanelCollapsed: Bool
var accentColor: UIColor
var backgroundColors: (UIColor, UIColor?)?
var messagesColors: (UIColor, UIColor?)?
init() {
self.section = nil
self.colorPanelCollapsed = false
self.accentColor = .clear
self.backgroundColors = nil
self.messagesColors = nil
@ -47,6 +49,7 @@ struct ThemeColorState {
init(section: ThemeColorSection, accentColor: UIColor, backgroundColors: (UIColor, UIColor?)?, messagesColors: (UIColor, UIColor?)?) {
self.section = section
self.colorPanelCollapsed = false
self.accentColor = accentColor
self.backgroundColors = backgroundColors
self.messagesColors = messagesColors
@ -71,14 +74,14 @@ struct ThemeColorState {
return false
}
if let lhsMessagesColors = self.messagesColors, let rhsMessagesColors = otherState.messagesColors {
if lhsMessagesColors.0 != lhsMessagesColors.0 {
if lhsMessagesColors.0 != rhsMessagesColors.0 {
return false
}
if let lhsSecondColor = lhsMessagesColors.1, let rhsSecondColor = lhsMessagesColors.1 {
if let lhsSecondColor = lhsMessagesColors.1, let rhsSecondColor = rhsMessagesColors.1 {
if lhsSecondColor != rhsSecondColor {
return false
}
} else if (lhsMessagesColors.1 == nil) != (lhsMessagesColors.1 == nil) {
} else if (lhsMessagesColors.1 == nil) != (rhsMessagesColors.1 == nil) {
return false
}
} else if (self.messagesColors == nil) != (otherState.messagesColors == nil) {
@ -91,44 +94,49 @@ struct ThemeColorState {
final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate {
private let context: AccountContext
private var theme: PresentationTheme
private let currentTheme: PresentationThemeReference
private let themeReference: PresentationThemeReference
private var presentationData: PresentationData
private var state: ThemeColorState
private let referenceTimestamp: Int32
private let scrollNode: ASScrollNode
private let pageControlBackgroundNode: ASDisplayNode
private let pageControlNode: PageControlNode
private let chatListBackgroundNode: ASDisplayNode
private var chatNodes: [ListViewItemNode]?
private let maskNode: ASImageNode
private let chatBackgroundNode: WallpaperBackgroundNode
private let messagesContainerNode: ASDisplayNode
private var dateHeaderNode: ListViewItemHeaderNode?
private var messageNodes: [ListViewItemNode]?
private var colorPanelNode: WallpaperColorPanelNode
private let toolbarNode: WallpaperGalleryToolbarNode
private var validLayout: (ContainerViewLayout, CGFloat, CGFloat)?
private var serviceColorDisposable: Disposable?
private var colorsDisposable: Disposable?
private let colors = Promise<(UIColor, (UIColor, UIColor?)?, (UIColor, UIColor?)?)>()
private let themePromise = Promise<PresentationTheme>()
private var wallpaper: TelegramWallpaper
private var tapGestureRecognizer: UITapGestureRecognizer?
var themeUpdated: ((PresentationTheme) -> Void)?
init(context: AccountContext, currentTheme: PresentationThemeReference, theme: PresentationTheme, dismiss: @escaping () -> Void, apply: @escaping (ThemeColorState) -> Void) {
private var validLayout: (ContainerViewLayout, CGFloat, CGFloat)?
var requiresWallpaperChange: Bool {
return self.state.backgroundColors == nil && self.chatBackgroundNode.image != nil
}
init(context: AccountContext, themeReference: PresentationThemeReference, theme: PresentationTheme, dismiss: @escaping () -> Void, apply: @escaping (ThemeColorState) -> Void) {
self.context = context
self.currentTheme = currentTheme
self.themeReference = themeReference
self.state = ThemeColorState()
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.theme = theme
self.wallpaper = self.presentationData.chatWallpaper
let calendar = Calendar(identifier: .gregorian)
var components = calendar.dateComponents(Set([.era, .year, .month, .day, .hour, .minute, .second]), from: Date())
@ -147,6 +155,11 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
self.chatListBackgroundNode = ASDisplayNode()
self.chatBackgroundNode = WallpaperBackgroundNode()
self.chatBackgroundNode.displaysAsynchronously = false
self.messagesContainerNode = ASDisplayNode()
self.messagesContainerNode.clipsToBounds = true
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
if case .color = self.presentationData.chatWallpaper {
} else {
self.chatBackgroundNode.image = chatControllerBackgroundImage(theme: theme, wallpaper: self.presentationData.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: false)
@ -182,7 +195,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
self.scrollNode.addSubnode(self.chatListBackgroundNode)
self.scrollNode.addSubnode(self.chatBackgroundNode)
self.scrollNode.addSubnode(self.messagesContainerNode)
self.colorPanelNode.colorsChanged = { [weak self] firstColor, secondColor, _ in
if let strongSelf = self, let section = strongSelf.state.section {
switch section {
@ -208,6 +222,16 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
}
}
self.colorPanelNode.colorSelected = { [weak self] in
if let strongSelf = self, strongSelf.state.colorPanelCollapsed {
strongSelf.updateState({ current in
var updated = current
updated.colorPanelCollapsed = false
return updated
}, animated: true)
}
}
self.toolbarNode.cancel = {
dismiss()
}
@ -219,25 +243,31 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
self.colorsDisposable = (self.colors.get()
|> deliverOn(Queue.concurrentDefaultQueue())
|> map { accentColor, backgroundColors, messagesColors -> (PresentationTheme, TelegramWallpaper?) in
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: accentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, preview: true) ?? defaultPresentationTheme
|> map { accentColor, backgroundColors, messagesColors -> (PresentationTheme, (TelegramWallpaper, UIImage?)) in
var wallpaper = context.sharedContext.currentPresentationData.with { $0 }.chatWallpaper
var wallpaperImage: UIImage?
if let backgroundColors = backgroundColors {
if let bottomColor = backgroundColors.1 {
wallpaper = .gradient(Int32(bitPattern: backgroundColors.0.rgb), Int32(bitPattern: bottomColor.rgb))
wallpaperImage = chatControllerBackgroundImage(theme: nil, wallpaper: wallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: false)
} else {
wallpaper = .color(Int32(bitPattern: backgroundColors.0.rgb))
}
}
let serviceBackgroundColor = serviceColor(for: (wallpaper, wallpaperImage))
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: accentColor, bubbleColors: messagesColors, serviceBackgroundColor: serviceBackgroundColor, preview: true) ?? defaultPresentationTheme
let _ = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: theme, wallpaper: wallpaper)
return (theme, wallpaper)
return (theme, (wallpaper, wallpaperImage))
}
|> deliverOnMainQueue).start(next: { [weak self] theme, wallpaper in
|> deliverOnMainQueue).start(next: { [weak self] theme, wallpaperAndImage in
guard let strongSelf = self else {
return
}
let (wallpaper, wallpaperImage) = wallpaperAndImage
strongSelf.theme = theme
strongSelf.themeUpdated?(theme)
strongSelf.themePromise.set(.single(theme))
@ -248,19 +278,17 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
strongSelf.chatListBackgroundNode.backgroundColor = theme.chatList.backgroundColor
strongSelf.maskNode.image = generateMaskImage(color: theme.chatList.backgroundColor)
if let wallpaper = wallpaper {
if case let .color(value) = wallpaper {
strongSelf.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
strongSelf.chatBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
strongSelf.chatBackgroundNode.image = nil
} else {
strongSelf.chatBackgroundNode.image = chatControllerBackgroundImage(theme: theme, wallpaper: wallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: false)
}
if case let .color(value) = wallpaper {
strongSelf.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
strongSelf.chatBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
strongSelf.chatBackgroundNode.image = nil
} else if let wallpaperImage = wallpaperImage {
strongSelf.chatBackgroundNode.imageContentMode = .scaleToFill
strongSelf.chatBackgroundNode.image = wallpaperImage
}
strongSelf.wallpaper = wallpaper
if let (layout, navigationBarHeight, messagesBottomInset) = strongSelf.validLayout {
strongSelf.pageControlNode.dotColor = UIColor.white
strongSelf.pageControlNode.inactiveDotColor = UIColor.white.withAlphaComponent(0.4)
strongSelf.updateChatsLayout(layout: layout, topInset: navigationBarHeight, transition: .immediate)
strongSelf.updateMessagesLayout(layout: layout, bottomInset: messagesBottomInset, transition: .immediate)
}
@ -291,6 +319,10 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
self.scrollNode.view.delegate = self
self.pageControlNode.setPage(0.0)
self.colorPanelNode.view.disablesInteractiveTransitionGestureRecognizer = true
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.chatTapped))
self.scrollNode.view.addGestureRecognizer(tapGestureRecognizer)
self.tapGestureRecognizer = tapGestureRecognizer
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
@ -303,34 +335,72 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
func updateState(_ f: (ThemeColorState) -> ThemeColorState, animated: Bool = false) {
let previousState = self.state
self.state = f(self.state)
var needsLayout = false
var animationCurve = ContainedViewLayoutTransitionCurve.easeInOut
var animationDuration: Double = 0.3
let colorsChanged = !previousState.areColorsEqual(to: self.state)
if colorsChanged {
self.colors.set(.single((self.state.accentColor, self.state.backgroundColors, self.state.messagesColors)))
}
let colorPanelCollapsed = self.state.colorPanelCollapsed
let sectionChanged = previousState.section != self.state.section
if sectionChanged, let section = self.state.section {
let firstColor: UIColor
self.view.endEditing(true)
let firstColor: UIColor?
let secondColor: UIColor?
var defaultColor: UIColor?
switch section {
case .accent:
firstColor = self.state.accentColor ?? .white
firstColor = self.state.accentColor ?? .blue
secondColor = nil
case .background:
firstColor = self.state.backgroundColors?.0 ?? .white
secondColor = self.state.backgroundColors?.1 ?? .white
if let backgroundColors = self.state.backgroundColors {
firstColor = backgroundColors.0
secondColor = backgroundColors.1
} else if let image = self.chatBackgroundNode.image {
firstColor = averageColor(from: image)
secondColor = nil
} else {
firstColor = .white
secondColor = nil
}
case .messages:
firstColor = self.state.messagesColors?.0 ?? .blue
secondColor = self.state.messagesColors?.1 ?? .blue
defaultColor = self.state.accentColor ?? .blue
if let messagesColors = self.state.messagesColors {
firstColor = messagesColors.0
secondColor = messagesColors.1
} else {
firstColor = nil
secondColor = nil
}
}
let colorPanelState = WallpaperColorPanelNodeState(selection: .first, firstColor: firstColor, firstColorRemovable: self.state.section == .messages, secondColor: secondColor, secondColorAvailable: self.state.section != .accent)
self.colorPanelNode.updateState({ _ in
return colorPanelState
return WallpaperColorPanelNodeState(selection: colorPanelCollapsed ? .none : .first, firstColor: firstColor, defaultColor: defaultColor, secondColor: secondColor, secondColorAvailable: self.state.section != .accent)
}, animated: animated)
if let (layout, navigationBarHeight, _) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate)
}
needsLayout = true
}
if previousState.colorPanelCollapsed != self.state.colorPanelCollapsed {
animationCurve = .spring
animationDuration = 0.45
needsLayout = true
self.colorPanelNode.updateState({ current in
var updated = current
updated.selection = colorPanelCollapsed ? .none : .first
return updated
}, animated: animated)
}
if needsLayout, let (layout, navigationBarHeight, _) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: animated ? .animated(duration: animationDuration, curve: animationCurve) : .immediate)
}
}
@ -416,6 +486,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
}
private func updateMessagesLayout(layout: ContainerViewLayout, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) {
let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.referenceTimestamp, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder)
var items: [ListViewItem] = []
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 1)
let otherPeerId = self.context.account.peerId
@ -428,20 +500,20 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.theme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.theme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.theme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local)))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local)))
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.theme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
if let messageNodes = self.messageNodes {
@ -468,22 +540,36 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
itemNode = node
apply().1(ListViewItemApply(isOnScreen: true))
})
itemNode!.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
itemNode!.isUserInteractionEnabled = false
messageNodes.append(itemNode!)
self.chatBackgroundNode.addSubnode(itemNode!)
self.messagesContainerNode.addSubnode(itemNode!)
}
self.messageNodes = messageNodes
}
var bottomOffset: CGFloat = 9.0 + bottomInset
if let messageNodes = self.messageNodes {
var bottomOffset: CGFloat = layout.size.height - bottomInset - 9.0
for itemNode in messageNodes {
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomOffset - itemNode.frame.height), size: itemNode.frame.size))
bottomOffset -= itemNode.frame.height
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: itemNode.frame.size))
bottomOffset += itemNode.frame.height
itemNode.updateFrame(itemNode.frame, within: layout.size)
}
}
let dateHeaderNode: ListViewItemHeaderNode
if let currentDateHeaderNode = self.dateHeaderNode {
dateHeaderNode = currentDateHeaderNode
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
} else {
dateHeaderNode = headerItem.node()
dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
self.messagesContainerNode.addSubnode(dateHeaderNode)
self.dateHeaderNode = dateHeaderNode
}
transition.updateFrame(node: dateHeaderNode, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: CGSize(width: layout.size.width, height: headerItem.height)))
dateHeaderNode.updateLayout(size: self.messagesContainerNode.frame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
@ -492,7 +578,6 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let toolbarHeight = 49.0 + layout.intrinsicInsets.bottom
self.chatListBackgroundNode.frame = CGRect(x: bounds.width, y: 0.0, width: bounds.width, height: bounds.height)
self.chatBackgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height)
self.scrollNode.view.contentSize = CGSize(width: bounds.width * 2.0, height: bounds.height)
@ -516,14 +601,23 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
var bottomInset = toolbarHeight
let standardInputHeight = layout.deviceMetrics.keyboardHeight(inLandscape: false)
let height = max(standardInputHeight, layout.inputHeight ?? 0.0) - bottomInset + 47.0
let colorPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - bottomInset - height), size: CGSize(width: layout.size.width, height: height))
bottomInset += height
let inputFieldPanelHeight: CGFloat = 47.0
let colorPanelHeight = max(standardInputHeight, layout.inputHeight ?? 0.0) - bottomInset + inputFieldPanelHeight
var colorPanelOffset: CGFloat = 0.0
if self.state.colorPanelCollapsed {
colorPanelOffset = colorPanelHeight - inputFieldPanelHeight
}
let colorPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - bottomInset - colorPanelHeight + colorPanelOffset), size: CGSize(width: layout.size.width, height: colorPanelHeight))
bottomInset += (colorPanelHeight - colorPanelOffset)
transition.updateFrame(node: self.colorPanelNode, frame: colorPanelFrame)
self.colorPanelNode.updateLayout(size: colorPanelFrame.size, keyboardHeight: layout.inputHeight ?? 0.0, transition: transition)
self.colorPanelNode.updateLayout(size: colorPanelFrame.size, transition: transition)
var messagesBottomInset = bottomInset
transition.updateFrame(node: self.messagesContainerNode, frame: CGRect(x: 0.0, y: navigationBarHeight, width: bounds.width, height: bounds.height - bottomInset - navigationBarHeight))
transition.updateFrame(node: self.chatBackgroundNode, frame: CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height - (colorPanelHeight - colorPanelOffset)))
var messagesBottomInset: CGFloat = 0.0
if pageControlAlpha > 0.0 {
messagesBottomInset += 37.0
}
@ -534,11 +628,19 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let pageControlSize = self.pageControlNode.measure(CGSize(width: bounds.width, height: 100.0))
let pageControlFrame = CGRect(origin: CGPoint(x: floor((bounds.width - pageControlSize.width) / 2.0), y: layout.size.height - bottomInset - 28.0), size: pageControlSize)
self.pageControlNode.frame = pageControlFrame
self.pageControlBackgroundNode.frame = CGRect(x: pageControlFrame.minX - 7.0, y: pageControlFrame.minY - 7.0, width: pageControlFrame.width + 14.0, height: 21.0)
transition.updateFrame(node: self.pageControlNode, frame: pageControlFrame)
transition.updateFrame(node: self.pageControlBackgroundNode, frame: CGRect(x: pageControlFrame.minX - 7.0, y: pageControlFrame.minY - 7.0, width: pageControlFrame.width + 14.0, height: 21.0))
transition.updateAlpha(node: self.pageControlNode, alpha: pageControlAlpha)
transition.updateAlpha(node: self.pageControlBackgroundNode, alpha: pageControlAlpha)
transition.updateFrame(node: self.maskNode, frame: CGRect(x: 0.0, y: layout.size.height - bottomInset - 80.0, width: bounds.width, height: 80.0))
}
@objc private func chatTapped() {
self.updateState({ current in
var updated = current
updated.colorPanelCollapsed = !updated.colorPanelCollapsed
return updated
}, animated: true)
}
}

View File

@ -68,7 +68,7 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
case settingInfo(PresentationTheme, String)
case themeHeader(PresentationTheme, String)
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor])
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor], [Int64: PresentationThemeColorPair])
var section: ItemListSectionId {
switch self {
@ -186,8 +186,8 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
} else {
return false
}
case let .themeItem(lhsTheme, lhsStrings, lhsThemes, lhsCurrentTheme, lhsThemeAccentColors):
if case let .themeItem(rhsTheme, rhsStrings, rhsThemes, rhsCurrentTheme, rhsThemeAccentColors) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsThemes == rhsThemes, lhsCurrentTheme == rhsCurrentTheme, lhsThemeAccentColors == rhsThemeAccentColors {
case let .themeItem(lhsTheme, lhsStrings, lhsThemes, lhsCurrentTheme, lhsThemeAccentColors, lhsThemeBubbleColors):
if case let .themeItem(rhsTheme, rhsStrings, rhsThemes, rhsCurrentTheme, rhsThemeAccentColors, rhsThemeBubbleColors) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsThemes == rhsThemes, lhsCurrentTheme == rhsCurrentTheme, lhsThemeAccentColors == rhsThemeAccentColors, lhsThemeBubbleColors == rhsThemeBubbleColors {
return true
} else {
return false
@ -244,8 +244,8 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
case let .themeHeader(theme, title):
return ItemListSectionHeaderItem(theme: theme, text: title, sectionId: self.section)
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors):
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, displayUnsupported: false, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updatedTheme: { theme in
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors, themeSpecificBubbleColors):
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, displayUnsupported: false, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificBubbleColors: themeSpecificBubbleColors, currentTheme: currentTheme, updatedTheme: { theme in
arguments.updateTheme(theme)
}, contextAction: nil)
}
@ -313,7 +313,7 @@ private func themeAutoNightSettingsControllerEntries(theme: PresentationTheme, s
break
case .system, .timeBased, .brightness:
entries.append(.themeHeader(theme, strings.AutoNightTheme_PreferredTheme))
entries.append(.themeItem(theme, strings, availableThemes, switchSetting.theme, settings.themeSpecificAccentColors))
entries.append(.themeItem(theme, strings, availableThemes, switchSetting.theme, settings.themeSpecificAccentColors, settings.themeSpecificBubbleColors))
}
return entries
@ -523,7 +523,7 @@ public func themeAutoNightSettingsController(context: AccountContext) -> ViewCon
}
}))
}, updateTheme: { theme in
guard let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: theme, accentColor: nil, serviceBackgroundColor: .black, baseColor: nil) else {
guard let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: theme, accentColor: nil, bubbleColors: nil, serviceBackgroundColor: .black) else {
return
}

View File

@ -22,7 +22,7 @@ final class ThemeColorSegmentedTitleView: UIView {
}
var sectionUpdated: ((ThemeColorSection) -> Void)?
var shouldUpdateSection: ((ThemeColorSection) -> Void)?
var shouldUpdateSection: ((ThemeColorSection, @escaping (Bool) -> Void) -> Void)?
init(theme: PresentationTheme, strings: PresentationStrings, selectedSection: ThemeColorSection) {
self.theme = theme
@ -38,6 +38,14 @@ final class ThemeColorSegmentedTitleView: UIView {
}
}
self.segmentedControlNode.selectedIndexShouldChange = { [weak self] index, f in
if let section = ThemeColorSection(rawValue: index) {
self?.shouldUpdateSection?(section, f)
} else {
f(false)
}
}
self.addSubnode(self.segmentedControlNode)
}

View File

@ -8,6 +8,7 @@ import SyncCore
import SwiftSignalKit
import LegacyComponents
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
private func availableColors() -> [Int32] {
@ -127,13 +128,29 @@ final class ThemeColorsGridController: ViewController {
}
}, presentColorPicker: { [weak self] in
if let strongSelf = self {
let controller = WallpaperGalleryController(context: strongSelf.context, source: .customColor(randomColor()))
controller.apply = { _, _, _ in
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
let _ = navigationController.popViewController(animated: true)
let _ = (strongSelf.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings])
|> deliverOnMainQueue).start(next: { [weak self] sharedData in
guard let strongSelf = self else {
return
}
}
self?.present(controller, in: .window(.root), blockInteraction: true)
let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings
let autoNightModeTriggered = strongSelf.presentationData.autoNightModeTriggered
let themeReference: PresentationThemeReference
if autoNightModeTriggered {
themeReference = settings.automaticThemeSwitchSetting.theme
} else {
themeReference = settings.theme
}
let controller = ThemeAccentColorController(context: strongSelf.context, themeReference: themeReference, section: .background)
controller.completion = { [weak self] in
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
let _ = navigationController.popViewController(animated: true)
}
}
strongSelf.push(controller)
})
}
})

View File

@ -97,6 +97,7 @@ public final class ThemePreviewController: ViewController {
let titleView = CounterContollerTitleView(theme: self.previewTheme)
titleView.title = CounterContollerTitle(title: themeName, counter: isPreview ? "" : " ")
self.navigationItem.titleView = titleView
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView())
self.statusBar.statusBarStyle = self.previewTheme.rootController.statusBarStyle.style
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)

View File

@ -49,9 +49,11 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
private let separatorNode: ASDisplayNode
private let chatContainerNode: ASDisplayNode
private let messagesContainerNode: ASDisplayNode
private let instantChatBackgroundNode: WallpaperBackgroundNode
private let remoteChatBackgroundNode: TransformImageNode
private let blurredNode: BlurredImageNode
private var dateHeaderNode: ListViewItemHeaderNode?
private var messageNodes: [ListViewItemNode]?
private let toolbarNode: WallpaperGalleryToolbarNode
@ -81,17 +83,25 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.pageControlBackgroundNode = ASDisplayNode()
self.pageControlBackgroundNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.3)
self.pageControlBackgroundNode.cornerRadius = 8.0
self.pageControlBackgroundNode.cornerRadius = 10.5
self.pageControlNode = PageControlNode(dotColor: previewTheme.chatList.unreadBadgeActiveBackgroundColor, inactiveDotColor: previewTheme.list.pageIndicatorInactiveColor)
self.pageControlNode = PageControlNode(dotSpacing: 7.0, dotColor: .white, inactiveDotColor: UIColor.white.withAlphaComponent(0.4))
self.chatListBackgroundNode = ASDisplayNode()
self.chatContainerNode = ASDisplayNode()
self.chatContainerNode.clipsToBounds = true
self.messagesContainerNode = ASDisplayNode()
self.messagesContainerNode.clipsToBounds = true
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
self.instantChatBackgroundNode = WallpaperBackgroundNode()
self.instantChatBackgroundNode.displaysAsynchronously = false
self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: previewTheme, wallpaper: previewTheme.chat.defaultWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
if case .gradient = previewTheme.chat.defaultWallpaper {
self.instantChatBackgroundNode.imageContentMode = .scaleToFill
}
self.instantChatBackgroundNode.motionEnabled = previewTheme.chat.defaultWallpaper.settings?.motion ?? false
self.instantChatBackgroundNode.view.contentMode = .scaleAspectFill
@ -150,6 +160,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.chatContainerNode.addSubnode(self.instantChatBackgroundNode)
self.chatContainerNode.addSubnode(self.remoteChatBackgroundNode)
self.chatContainerNode.addSubnode(self.messagesContainerNode)
self.addSubnode(self.separatorNode)
@ -190,11 +201,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
|> deliverOnMainQueue).start(next: { [weak self] color in
if let strongSelf = self {
if strongSelf.previewTheme.chat.defaultWallpaper.hasWallpaper {
strongSelf.pageControlBackgroundNode.backgroundColor = color
} else {
strongSelf.pageControlBackgroundNode.backgroundColor = .clear
}
strongSelf.pageControlBackgroundNode.backgroundColor = color
}
})
@ -284,9 +291,6 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.backgroundColor = self.previewTheme.list.plainBackgroundColor
self.pageControlNode.dotColor = self.previewTheme.chatList.unreadBadgeActiveBackgroundColor
self.pageControlNode.inactiveDotColor = self.previewTheme.list.pageIndicatorInactiveColor
self.chatListBackgroundNode.backgroundColor = self.previewTheme.chatList.backgroundColor
self.maskNode.image = generateMaskImage(color: self.previewTheme.chatList.backgroundColor)
if case let .color(value) = self.previewTheme.chat.defaultWallpaper {
@ -418,6 +422,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
private func updateMessagesLayout(layout: ContainerViewLayout, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) {
let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.referenceTimestamp, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder)
var items: [ListViewItem] = []
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 1)
let otherPeerId = self.context.account.peerId
@ -477,22 +483,36 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
itemNode = node
apply().1(ListViewItemApply(isOnScreen: true))
})
itemNode!.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
itemNode!.isUserInteractionEnabled = false
messageNodes.append(itemNode!)
self.chatContainerNode.addSubnode(itemNode!)
self.messagesContainerNode.addSubnode(itemNode!)
}
self.messageNodes = messageNodes
}
var bottomOffset: CGFloat = 9.0 + bottomInset
if let messageNodes = self.messageNodes {
var bottomOffset: CGFloat = layout.size.height - bottomInset - 9.0
for itemNode in messageNodes {
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomOffset - itemNode.frame.height), size: itemNode.frame.size))
bottomOffset -= itemNode.frame.height
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: itemNode.frame.size))
bottomOffset += itemNode.frame.height
itemNode.updateFrame(itemNode.frame, within: layout.size)
}
}
let dateHeaderNode: ListViewItemHeaderNode
if let currentDateHeaderNode = self.dateHeaderNode {
dateHeaderNode = currentDateHeaderNode
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
} else {
dateHeaderNode = headerItem.node()
dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
//self.messagesContainerNode.addSubnode(dateHeaderNode)
self.dateHeaderNode = dateHeaderNode
}
transition.updateFrame(node: dateHeaderNode, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: CGSize(width: layout.size.width, height: headerItem.height)))
dateHeaderNode.updateLayout(size: self.messagesContainerNode.frame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
@ -530,6 +550,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
bottomInset = 66.0
}
self.messagesContainerNode.frame = self.chatContainerNode.bounds
self.instantChatBackgroundNode.frame = self.chatContainerNode.bounds
self.remoteChatBackgroundNode.frame = self.chatContainerNode.bounds
self.blurredNode.frame = self.chatContainerNode.bounds
@ -543,7 +564,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
let pageControlSize = self.pageControlNode.measure(CGSize(width: bounds.width, height: 100.0))
let pageControlFrame = CGRect(origin: CGPoint(x: floor((bounds.width - pageControlSize.width) / 2.0), y: layout.size.height - toolbarHeight - 42.0), size: pageControlSize)
self.pageControlNode.frame = pageControlFrame
self.pageControlBackgroundNode.frame = CGRect(x: pageControlFrame.minX - 11.0, y: pageControlFrame.minY - 12.0, width: pageControlFrame.width + 22.0, height: 30.0)
self.pageControlBackgroundNode.frame = CGRect(x: pageControlFrame.minX - 7.0, y: pageControlFrame.minY - 7.0, width: pageControlFrame.width + 14.0, height: 21.0)
transition.updateFrame(node: self.maskNode, frame: CGRect(x: 0.0, y: layout.size.height - toolbarHeight - 80.0, width: bounds.width, height: 80.0))
}
}

View File

@ -72,7 +72,7 @@ private final class ThemeSettingsControllerArguments {
let selectFontSize: (PresentationFontSize) -> Void
let openWallpaperSettings: () -> Void
let selectAccentColor: (PresentationThemeAccentColor) -> Void
let openAccentColorPicker: (PresentationThemeReference, PresentationThemeAccentColor?) -> Void
let openAccentColorPicker: (PresentationThemeReference) -> Void
let openAutoNightTheme: () -> Void
let toggleLargeEmoji: (Bool) -> Void
let disableAnimations: (Bool) -> Void
@ -80,7 +80,7 @@ private final class ThemeSettingsControllerArguments {
let editTheme: (PresentationCloudTheme) -> Void
let contextAction: (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void
init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, PresentationThemeAccentColor?) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, contextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void) {
init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, contextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void) {
self.context = context
self.selectTheme = selectTheme
self.selectFontSize = selectFontSize
@ -130,7 +130,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
case wallpaper(PresentationTheme, String)
case accentColor(PresentationTheme, PresentationThemeReference, String, PresentationThemeAccentColor?)
case autoNightTheme(PresentationTheme, String, String)
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor], PresentationThemeAccentColor?)
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor], [Int64: PresentationThemeColorPair], PresentationThemeAccentColor?)
case iconHeader(PresentationTheme, String)
case iconItem(PresentationTheme, PresentationStrings, [PresentationAppIcon], String?)
case otherHeader(PresentationTheme, String)
@ -218,8 +218,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
} else {
return false
}
case let .themeItem(lhsTheme, lhsStrings, lhsThemes, lhsCurrentTheme, lhsThemeAccentColors, lhsCurrentColor):
if case let .themeItem(rhsTheme, rhsStrings, rhsThemes, rhsCurrentTheme, rhsThemeAccentColors, rhsCurrentColor) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsThemes == rhsThemes, lhsCurrentTheme == rhsCurrentTheme, lhsThemeAccentColors == rhsThemeAccentColors, lhsCurrentColor == rhsCurrentColor {
case let .themeItem(lhsTheme, lhsStrings, lhsThemes, lhsCurrentTheme, lhsThemeAccentColors, lhsThemeBubbleColors, lhsCurrentColor):
if case let .themeItem(rhsTheme, rhsStrings, rhsThemes, rhsCurrentTheme, rhsThemeAccentColors, rhsThemeBubbleColors, rhsCurrentColor) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsThemes == rhsThemes, lhsCurrentTheme == rhsCurrentTheme, lhsThemeAccentColors == rhsThemeAccentColors, lhsThemeBubbleColors == rhsThemeBubbleColors, lhsCurrentColor == rhsCurrentColor {
return true
} else {
return false
@ -315,7 +315,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
return ThemeSettingsAccentColorItem(theme: theme, sectionId: self.section, colors: colors, currentColor: currentColor, updated: { color in
arguments.selectAccentColor(color)
}, openColorPicker: {
arguments.openAccentColorPicker(currentTheme, currentColor)
arguments.openAccentColorPicker(currentTheme)
}, tag: ThemeSettingsEntryTag.accentColor)
case let .autoNightTheme(theme, text, value):
return ItemListDisclosureItem(theme: theme, icon: nil, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
@ -323,8 +323,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
})
case let .themeListHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors, _):
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, displayUnsupported: true, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updatedTheme: { theme in
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors, themeSpecificBubbleColors, _):
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, displayUnsupported: true, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificBubbleColors: themeSpecificBubbleColors, currentTheme: currentTheme, updatedTheme: { theme in
if case let .cloud(theme) = theme, theme.theme.file == nil {
if theme.theme.isCreator {
arguments.editTheme(theme)
@ -357,14 +357,14 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
}
}
private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeReference: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], availableThemes: [PresentationThemeReference], autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, largeEmoji: Bool, disableAnimations: Bool, availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] {
private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeReference: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificBubbleColors: [Int64: PresentationThemeColorPair], availableThemes: [PresentationThemeReference], autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, largeEmoji: Bool, disableAnimations: Bool, availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] {
var entries: [ThemeSettingsControllerEntry] = []
let title = presentationData.autoNightModeTriggered ? strings.Appearance_ColorThemeNight.uppercased() : strings.Appearance_ColorTheme.uppercased()
entries.append(.themeListHeader(presentationData.theme, title))
entries.append(.chatPreview(presentationData.theme, theme, wallpaper, fontSize, presentationData.strings, dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (presentationData.strings.Appearance_PreviewReplyAuthor, presentationData.strings.Appearance_PreviewReplyText), text: presentationData.strings.Appearance_PreviewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: presentationData.strings.Appearance_PreviewOutgoingText)]))
entries.append(.themeItem(presentationData.theme, presentationData.strings, availableThemes, themeReference, themeSpecificAccentColors, themeSpecificAccentColors[themeReference.index]))
entries.append(.themeItem(presentationData.theme, presentationData.strings, availableThemes, themeReference, themeSpecificAccentColors, themeSpecificBubbleColors, themeSpecificAccentColors[themeReference.index]))
if case let .builtin(theme) = themeReference, theme != .dayClassic {
entries.append(.accentColor(presentationData.theme, themeReference, strings.Appearance_AccentColor, themeSpecificAccentColors[themeReference.index]))
@ -448,7 +448,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
currentTheme = current.automaticThemeSwitchSetting.theme
}
guard let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: color.color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor) else {
guard let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: color.color, bubbleColors: nil, serviceBackgroundColor: defaultServiceBackgroundColor) else {
return current
}
@ -463,8 +463,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
}).start()
}, openAccentColorPicker: { themeReference, currentColor in
let controller = ThemeAccentColorController(context: context, currentTheme: themeReference, section: .accent)
}, openAccentColorPicker: { themeReference in
let controller = ThemeAccentColorController(context: context, themeReference: themeReference, section: .accent)
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}, openAutoNightTheme: {
pushControllerImpl?(themeAutoNightSettingsController(context: context))
@ -488,8 +488,15 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
})
pushControllerImpl?(controller)
}, contextAction: { isCurrent, reference, node, gesture in
let _ = (context.account.postbox.transaction { transaction in
return makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: reference, accentColor: nil, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: .blue)
let _ = (context.sharedContext.accountManager.transaction { transaction in
let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings) as? PresentationThemeSettings ?? PresentationThemeSettings.defaultSettings
let accentColor = settings.themeSpecificAccentColors[reference.index]?.color
let bubbleColors = settings.themeSpecificBubbleColors[reference.index]?.colors
return (accentColor, bubbleColors)
} |> map { accentColor, bubbleColors in
return makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: reference, accentColor: accentColor, bubbleColors: bubbleColors, serviceBackgroundColor: defaultServiceBackgroundColor)
}
|> deliverOnMainQueue).start(next: { theme in
guard let theme = theme else {
@ -605,7 +612,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
availableThemes.append(contentsOf: cloudThemes)
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeReference: themeReference, themeSpecificAccentColors: settings.themeSpecificAccentColors, availableThemes: availableThemes, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, largeEmoji: largeEmoji, disableAnimations: disableAnimations, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeReference: themeReference, themeSpecificAccentColors: settings.themeSpecificAccentColors, themeSpecificBubbleColors: settings.themeSpecificBubbleColors, availableThemes: availableThemes, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, largeEmoji: largeEmoji, disableAnimations: disableAnimations, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
return (controllerState, (listState, arguments))
}
@ -625,7 +632,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
return controller?.navigationController as? NavigationController
}
selectThemeImpl = { theme in
guard let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: theme, accentColor: nil, serviceBackgroundColor: .black, baseColor: nil) else {
guard let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: theme, accentColor: nil, bubbleColors: nil, serviceBackgroundColor: .black) else {
return
}

View File

@ -89,18 +89,20 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
let themes: [PresentationThemeReference]
let displayUnsupported: Bool
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
let themeSpecificBubbleColors: [Int64: PresentationThemeColorPair]
let currentTheme: PresentationThemeReference
let updatedTheme: (PresentationThemeReference) -> Void
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
let tag: ItemListItemTag?
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], displayUnsupported: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], displayUnsupported: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificBubbleColors: [Int64: PresentationThemeColorPair], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
self.context = context
self.theme = theme
self.strings = strings
self.themes = themes
self.displayUnsupported = displayUnsupported
self.themeSpecificAccentColors = themeSpecificAccentColors
self.themeSpecificBubbleColors = themeSpecificBubbleColors
self.currentTheme = currentTheme
self.updatedTheme = updatedTheme
self.contextAction = contextAction
@ -153,6 +155,7 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
private var theme: PresentationThemeReference?
private var currentTheme: PresentationTheme?
private var accentColor: UIColor?
private var bubbleColors: (UIColor, UIColor)?
private var bordered: Bool?
private var selected: Bool?
@ -187,7 +190,7 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
}
}
func setup(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?, currentTheme: PresentationTheme, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) {
func setup(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?, bubbleColors: (UIColor, UIColor)?, currentTheme: PresentationTheme, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) {
let updatedTheme = self.currentTheme == nil || currentTheme !== self.currentTheme!
var contextActionEnabled = true
if case let .cloud(theme) = theme, theme.theme.file == nil {
@ -199,7 +202,7 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
}
} else {
if theme != self.theme || accentColor != self.accentColor {
self.imageNode.setSignal(themeIconImage(account: context.account, accountManager: context.sharedContext.accountManager, theme: theme, accentColor: accentColor))
self.imageNode.setSignal(themeIconImage(account: context.account, accountManager: context.sharedContext.accountManager, theme: theme, accentColor: accentColor, bubbleColors: bubbleColors))
self.theme = theme
self.accentColor = accentColor
}
@ -411,7 +414,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
}
let name = themeDisplayName(strings: item.strings, reference: theme)
imageNode.setup(context: item.context, theme: theme, accentColor: item.themeSpecificAccentColors[theme.index]?.color, currentTheme: item.theme, title: NSAttributedString(string: name, font: selected ? selectedTextFont : textFont, textColor: selected ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center), bordered: true, selected: selected, action: { [weak self, weak imageNode] in
imageNode.setup(context: item.context, theme: theme, accentColor: item.themeSpecificAccentColors[theme.index]?.color, bubbleColors: item.themeSpecificBubbleColors[theme.index]?.plainColors, currentTheme: item.theme, title: NSAttributedString(string: name, font: selected ? selectedTextFont : textFont, textColor: selected ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center), bordered: true, selected: selected, action: { [weak self, weak imageNode] in
item.updatedTheme(theme)
if let imageNode = imageNode {
self?.scrollToNode(imageNode, animated: true)

View File

@ -42,14 +42,18 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
private let measureNode: ImmediateTextNode
private let prefixNode: ASTextNode
private var validLayout: CGSize?
private var previousColor: UIColor?
private var gestureRecognizer: UITapGestureRecognizer?
var colorChanged: ((UIColor, Bool) -> Void)?
var colorRemoved: (() -> Void)?
var colorSelected: (() -> Void)?
private var isDefault = false {
didSet {
self.updateSelectionVisibility()
}
}
var color: UIColor = .white {
didSet {
self.setColor(self.color, update: false)
@ -64,12 +68,17 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
var isSelected: Bool = false {
didSet {
self.selectionNode.isHidden = !self.isSelected
self.updateSelectionVisibility()
self.gestureRecognizer?.isEnabled = !self.isSelected
if !self.isSelected {
self.textFieldNode.textField.resignFirstResponder()
}
}
}
private var previousIsDefault: Bool?
private var previousColor: UIColor?
private var validLayout: CGSize?
init(theme: PresentationTheme) {
self.theme = theme
@ -95,7 +104,7 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
self.removeButton = HighlightableButtonNode()
self.removeButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeColorRemoveIcon"), color: theme.chat.inputPanel.inputControlColor), for: .normal)
super.init()
self.addSubnode(self.textBackgroundNode)
@ -122,6 +131,10 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
self.textFieldNode.textField.addTarget(self, action: #selector(self.textFieldTextChanged(_:)), for: .editingChanged)
self.textFieldNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0)
self.textFieldNode.textField.tintColor = self.theme.list.itemAccentColor
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapped))
self.view.addGestureRecognizer(gestureRecognizer)
self.gestureRecognizer = gestureRecognizer
}
func updateTheme(_ theme: PresentationTheme) {
@ -129,16 +142,18 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
self.textBackgroundNode.image = textInputBackgroundImage(fieldColor: self.theme.chat.inputPanel.inputBackgroundColor, strokeColor: self.theme.chat.inputPanel.inputStrokeColor, diameter: 33.0)
self.textFieldNode.textField.textColor = self.theme.chat.inputPanel.inputTextColor
self.textFieldNode.textField.textColor = self.isDefault ? self.theme.chat.inputPanel.inputPlaceholderColor : self.theme.chat.inputPanel.inputTextColor
self.textFieldNode.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance
self.textFieldNode.textField.tintColor = self.theme.list.itemAccentColor
self.selectionNode.backgroundColor = theme.chat.inputPanel.panelControlAccentColor.withAlphaComponent(0.2)
}
func setColor(_ color: UIColor, update: Bool = true, ended: Bool = true) {
func setColor(_ color: UIColor, isDefault: Bool = false, update: Bool = true, ended: Bool = true) {
self.isDefault = isDefault
let text = color.hexString.uppercased()
self.textFieldNode.textField.text = text
self.textFieldNode.textField.textColor = isDefault ? self.theme.chat.inputPanel.inputPlaceholderColor : self.theme.chat.inputPanel.inputTextColor
if let size = self.validLayout {
self.updateSelectionLayout(size: size, transition: .immediate)
}
@ -151,12 +166,17 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
@objc private func removePressed() {
self.colorRemoved?()
}
@objc private func tapped() {
self.colorSelected?()
}
@objc internal func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
var updated = textField.text ?? ""
updated.replaceSubrange(updated.index(updated.startIndex, offsetBy: range.lowerBound) ..< updated.index(updated.startIndex, offsetBy: range.upperBound), with: string)
if updated.count <= 6 && updated.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil {
textField.text = updated.uppercased()
textField.textColor = self.theme.chat.inputPanel.inputTextColor
if let size = self.validLayout {
self.updateSelectionLayout(size: size, transition: .immediate)
@ -183,6 +203,10 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if self.isSelected {
self.previousColor = self.color
self.previousIsDefault = self.isDefault
textField.textColor = self.theme.chat.inputPanel.inputTextColor
return true
} else {
self.colorSelected?()
@ -194,7 +218,7 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
if let text = self.textFieldNode.textField.text, text.count == 6, let color = UIColor(hexString: text) {
self.setColor(color)
} else {
self.setColor(self.previousColor ?? .black)
self.setColor(self.previousColor ?? .black, isDefault: self.previousIsDefault ?? false)
}
}
@ -204,6 +228,10 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
transition.updateFrame(node: self.selectionNode, frame: CGRect(x: 47.0, y: 6.0, width: max(45.0, size.width), height: 20.0))
}
private func updateSelectionVisibility() {
self.selectionNode.isHidden = !self.isSelected || self.isDefault
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = size
@ -231,8 +259,8 @@ enum WallpaperColorPanelNodeSelectionState {
struct WallpaperColorPanelNodeState {
var selection: WallpaperColorPanelNodeSelectionState
var firstColor: UIColor
var firstColorRemovable: Bool
var firstColor: UIColor?
var defaultColor: UIColor?
var secondColor: UIColor?
var secondColorAvailable: Bool
}
@ -253,8 +281,9 @@ final class WallpaperColorPanelNode: ASDisplayNode {
private let colorPickerNode: WallpaperColorPickerNode
var colorsChanged: ((UIColor, UIColor?, Bool) -> Void)?
var colorSelected: (() -> Void)?
private var validLayout: (CGSize, CGFloat)?
private var validLayout: CGSize?
init(theme: PresentationTheme, strings: PresentationStrings) {
self.theme = theme
@ -280,7 +309,7 @@ final class WallpaperColorPanelNode: ASDisplayNode {
self.firstColorFieldNode = ColorInputFieldNode(theme: theme)
self.secondColorFieldNode = ColorInputFieldNode(theme: theme)
self.state = WallpaperColorPanelNodeState(selection: .first, firstColor: .white, firstColorRemovable: false, secondColor: nil, secondColorAvailable: true)
self.state = WallpaperColorPanelNodeState(selection: .first, firstColor: nil, secondColor: nil, secondColorAvailable: false)
super.init()
@ -312,10 +341,14 @@ final class WallpaperColorPanelNode: ASDisplayNode {
strongSelf.updateState({ current in
var updated = current
updated.selection = .first
updated.firstColor = updated.secondColor ?? updated.firstColor
if let defaultColor = current.defaultColor {
updated.firstColor = nil
} else {
updated.firstColor = updated.secondColor ?? updated.firstColor
}
updated.secondColor = nil
return updated
})
}, animated: strongSelf.state.defaultColor == nil)
}
}
self.firstColorFieldNode.colorSelected = { [weak self] in
@ -325,6 +358,8 @@ final class WallpaperColorPanelNode: ASDisplayNode {
updated.selection = .first
return updated
})
strongSelf.colorSelected?()
}
}
@ -354,6 +389,8 @@ final class WallpaperColorPanelNode: ASDisplayNode {
updated.selection = .second
return updated
})
strongSelf.colorSelected?()
}
}
@ -401,37 +438,47 @@ final class WallpaperColorPanelNode: ASDisplayNode {
}
func updateState(_ f: (WallpaperColorPanelNodeState) -> WallpaperColorPanelNodeState, updateLayout: Bool = true, animated: Bool = true) {
let firstColor = self.state.firstColor.rgb
let secondColor = self.state.secondColor?.rgb
let previousFirstColor = self.state.firstColor
let previousSecondColor = self.state.secondColor
self.state = f(self.state)
self.firstColorFieldNode.setColor(self.state.firstColor, update: false)
if let secondColor = self.state.secondColor {
let firstColor: UIColor
if let color = self.state.firstColor {
firstColor = color
} else if let defaultColor = self.state.defaultColor {
firstColor = defaultColor
} else {
firstColor = .white
}
let secondColor = self.state.secondColor
self.firstColorFieldNode.setColor(firstColor, isDefault: self.state.firstColor == nil, update: false)
if let secondColor = secondColor {
self.secondColorFieldNode.setColor(secondColor, update: false)
}
if updateLayout, let (size, keyboardHeight) = self.validLayout {
if updateLayout, let size = self.validLayout {
switch self.state.selection {
case .first:
self.colorPickerNode.color = self.state.firstColor
self.colorPickerNode.color = firstColor
case .second:
if let secondColor = self.state.secondColor {
if let secondColor = secondColor {
self.colorPickerNode.color = secondColor
}
default:
break
}
self.updateLayout(size: size, keyboardHeight: keyboardHeight, transition: animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate)
self.updateLayout(size: size, transition: animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate)
}
if self.state.firstColor.rgb != firstColor || self.state.secondColor?.rgb != secondColor {
self.colorsChanged?(self.state.firstColor, self.state.secondColor, updateLayout)
if self.state.firstColor?.rgb != previousFirstColor?.rgb || self.state.secondColor?.rgb != previousSecondColor?.rgb {
self.colorsChanged?(firstColor, secondColor, updateLayout)
}
}
func updateLayout(size: CGSize, keyboardHeight: CGFloat, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, keyboardHeight)
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = size
let separatorHeight = UIScreenPixel
let topPanelHeight: CGFloat = 47.0
@ -440,7 +487,6 @@ final class WallpaperColorPanelNode: ASDisplayNode {
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(x: 0.0, y: topPanelHeight, width: size.width, height: separatorHeight))
let fieldHeight: CGFloat = 33.0
let buttonSpacing: CGFloat = keyboardHeight > 0.0 ? 3.0 : 6.0
let leftInset: CGFloat = 15.0
let rightInset: CGFloat = 15.0
let rightInsetWithButton: CGFloat = 42.0
@ -469,7 +515,7 @@ final class WallpaperColorPanelNode: ASDisplayNode {
transition.updateAlpha(node: self.swapButton, alpha: swapButtonAlpha)
transition.updateAlpha(node: self.addButton, alpha: addButtonAlpha)
self.firstColorFieldNode.isRemovable = self.state.secondColor != nil || self.state.firstColorRemovable
self.firstColorFieldNode.isRemovable = self.state.secondColor != nil || (self.state.defaultColor != nil && self.state.firstColor != nil)
self.secondColorFieldNode.isRemovable = true
self.firstColorFieldNode.isSelected = self.state.selection == .first
@ -500,10 +546,20 @@ final class WallpaperColorPanelNode: ASDisplayNode {
}
@objc private func addPressed() {
self.colorSelected?()
self.updateState({ current in
var updated = current
updated.selection = .second
updated.secondColor = current.firstColor
let firstColor = current.firstColor ?? current.defaultColor
if let color = firstColor {
updated.firstColor = color
var hsv = color.hsv
updated.secondColor = UIColor(hue: hsv.0, saturation: hsv.1, brightness: hsv.2 < 0.4 ? hsv.2 + 0.4 : hsv.2 - 0.4 , alpha: 1.0)
}
return updated
})
}

View File

@ -280,7 +280,7 @@ final class WallpaperColorPickerNode: ASDisplayNode {
colorKnobFrame.origin = origin
transition.updateFrame(node: self.colorKnobNode, frame: colorKnobFrame)
let inset: CGFloat = 42.0
let inset: CGFloat = 15.0
let brightnessKnobSize = CGSize(width: 12.0, height: 55.0)
let brightnessKnobFrame = CGRect(x: inset - brightnessKnobSize.width / 2.0 + (size.width - inset * 2.0) * (1.0 - self.colorHSV.2), y: size.height - 65.0, width: brightnessKnobSize.width, height: brightnessKnobSize.height)
transition.updateFrame(node: self.brightnessKnobNode, frame: brightnessKnobFrame)

View File

@ -715,7 +715,7 @@ public class WallpaperGalleryController: ViewController {
}
transition.updateFrame(node: colorPanelNode, frame: colorPanelFrame)
colorPanelNode.updateLayout(size: colorPanelFrame.size, keyboardHeight: layout.inputHeight ?? 0.0, transition: transition)
colorPanelNode.updateLayout(size: colorPanelFrame.size, transition: transition)
}
let currentPatternPanelNode: WallpaperPatternPanelNode

View File

@ -11,9 +11,9 @@ import AppBundle
private var backgroundImageForWallpaper: (TelegramWallpaper, Bool, UIImage)?
public func chatControllerBackgroundImage(theme: PresentationTheme, wallpaper initialWallpaper: TelegramWallpaper, mediaBox: MediaBox, composed: Bool = true, knockoutMode: Bool) -> UIImage? {
public func chatControllerBackgroundImage(theme: PresentationTheme?, wallpaper initialWallpaper: TelegramWallpaper, mediaBox: MediaBox, composed: Bool = true, knockoutMode: Bool) -> UIImage? {
var wallpaper = initialWallpaper
if knockoutMode {
if knockoutMode, let theme = theme {
switch theme.name {
case let .builtin(name):
switch name {

View File

@ -36,10 +36,14 @@ public func messageBubbleImage(incoming: Bool, fillColor: UIColor, strokeColor:
return generateImage(CGSize(width: 42.0 + inset * 2.0, height: diameter + inset * 2.0), contextGenerator: { rawSize, context in
var drawWithClearColor = false
if knockout, case let .color(color) = wallpaper {
if knockout {
drawWithClearColor = !mask
context.setFillColor(UIColor(rgb: UInt32(color)).cgColor)
context.fill(CGRect(origin: CGPoint(), size: rawSize))
if case let .color(color) = wallpaper {
context.setFillColor(UIColor(rgb: UInt32(color)).cgColor)
context.fill(CGRect(origin: CGPoint(), size: rawSize))
} else {
context.clear(CGRect(origin: CGPoint(), size: rawSize))
}
} else {
context.clear(CGRect(origin: CGPoint(), size: rawSize))
}

View File

@ -2,7 +2,7 @@ import Foundation
import UIKit
import TelegramUIPreferences
private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: PresentationThemeBaseColor?, preview: Bool) -> PresentationTheme {
private func makeDarkPresentationTheme(accentColor: UIColor, bubbleColors: (UIColor, UIColor?)?, preview: Bool) -> PresentationTheme {
let destructiveColor: UIColor = UIColor(rgb: 0xeb5545)
let constructiveColor: UIColor = UIColor(rgb: 0x08a723)
let secretColor: UIColor = UIColor(rgb: 0x00b12c)
@ -366,7 +366,6 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
name: .builtin(.night),
referenceTheme: .night,
overallDarkAppearance: true,
baseColor: baseColor,
intro: intro,
passcode: passcode,
rootController: rootController,
@ -380,9 +379,9 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
)
}
public let defaultDarkPresentationTheme = makeDarkPresentationTheme(accentColor: .white, baseColor: .white, preview: false)
public let defaultDarkPresentationTheme = makeDarkPresentationTheme(accentColor: .white, bubbleColors: nil, preview: false)
public func makeDarkPresentationTheme(accentColor: UIColor?, baseColor: PresentationThemeBaseColor?, preview: Bool) -> PresentationTheme {
public func makeDarkPresentationTheme(accentColor: UIColor?, bubbleColors: (UIColor, UIColor?)?, preview: Bool) -> PresentationTheme {
let accentColor = accentColor ?? .white
return makeDarkPresentationTheme(accentColor: accentColor, baseColor: baseColor, preview: preview)
return makeDarkPresentationTheme(accentColor: accentColor, bubbleColors: bubbleColors, preview: preview)
}

View File

@ -2,7 +2,7 @@ import Foundation
import UIKit
import TelegramUIPreferences
private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: PresentationThemeBaseColor?, preview: Bool) -> PresentationTheme {
private func makeDarkPresentationTheme(accentColor: UIColor, bubbleColors: (UIColor, UIColor?)?, preview: Bool) -> PresentationTheme {
let destructiveColor: UIColor = UIColor(rgb: 0xff6767)
let constructiveColor: UIColor = UIColor(rgb: 0x08a723)
let secretColor: UIColor = UIColor(rgb: 0x89df9e)
@ -31,7 +31,14 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
let mainFreeTextColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.097, brightness: 0.56)
let outgoingBubbleColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59)
var outgoingBubbleFillColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59)
var outgoingBubbleFillGradientColor = outgoingBubbleFillColor
if let bubbleColors = bubbleColors {
outgoingBubbleFillColor = bubbleColors.0
outgoingBubbleFillGradientColor = bubbleColors.1 ?? bubbleColors.0
}
let highlightedIncomingBubbleColor = accentColor.withMultiplied(hue: 1.03, saturation: 0.463, brightness: 0.29)
let highlightedOutgoingBubbleColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.609, brightness: 0.63)
@ -199,7 +206,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
let message = PresentationThemeChatMessage(
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), mediaControlInnerBackgroundColor: mainBackgroundColor, pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaControlInnerBackgroundColor: outgoingBubbleColor, pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: .white, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaControlInnerBackgroundColor: outgoingBubbleFillColor, pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: .white, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white),
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)),
infoPrimaryTextColor: UIColor(rgb: 0xffffff),
infoLinkTextColor: accentColor,
@ -344,7 +351,6 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
name: .builtin(.nightAccent),
referenceTheme: .nightAccent,
overallDarkAppearance: true,
baseColor: baseColor,
intro: intro,
passcode: passcode,
rootController: rootController,
@ -359,12 +365,12 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
}
public let defaultDarkAccentColor = UIColor(rgb: 0x2ea6ff)
public let defaultDarkAccentPresentationTheme = makeDarkAccentPresentationTheme(accentColor: UIColor(rgb: 0x2ea6ff), baseColor: .blue, preview: false)
public let defaultDarkAccentPresentationTheme = makeDarkAccentPresentationTheme(accentColor: UIColor(rgb: 0x2ea6ff), bubbleColors: nil, preview: false)
public func makeDarkAccentPresentationTheme(accentColor: UIColor?, baseColor: PresentationThemeBaseColor?, preview: Bool) -> PresentationTheme {
public func makeDarkAccentPresentationTheme(accentColor: UIColor?, bubbleColors: (UIColor, UIColor?)?, preview: Bool) -> PresentationTheme {
var accentColor = accentColor ?? defaultDarkAccentColor
if accentColor == PresentationThemeBaseColor.blue.color {
accentColor = defaultDarkAccentColor
}
return makeDarkPresentationTheme(accentColor: accentColor, baseColor: baseColor, preview: preview)
return makeDarkPresentationTheme(accentColor: accentColor, bubbleColors: bubbleColors, preview: preview)
}

View File

@ -4,7 +4,7 @@ import TelegramCore
import SyncCore
import TelegramUIPreferences
private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgroundColor: UIColor, baseColor: PresentationThemeBaseColor?, day: Bool, preview: Bool) -> PresentationTheme {
private func makeDefaultDayPresentationTheme(accentColor: UIColor, bubbleColors: (UIColor, UIColor?)?, serviceBackgroundColor: UIColor, day: Bool, preview: Bool) -> PresentationTheme {
let destructiveColor: UIColor = UIColor(rgb: 0xff3b30)
let constructiveColor: UIColor = UIColor(rgb: 0x00c900)
let secretColor: UIColor = UIColor(rgb: 0x00b12c)
@ -16,24 +16,35 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
let outgoingLinkTextColor: UIColor
let outgoingCheckColor: UIColor
let outgoingControlColor: UIColor
let outgoingBubbleFillColor: UIColor
var outgoingBubbleFillColor: UIColor
var outgoingBubbleFillGradientColor: UIColor
let outgoingBubbleStrokeColor: UIColor
let outgoingSelectionBaseColor: UIColor
outgoingBubbleFillColor = accentColor.withMultiplied(hue: 0.966, saturation: 0.61, brightness: 0.98)
outgoingBubbleFillGradientColor = accentColor
if let bubbleColors = bubbleColors {
outgoingBubbleFillColor = bubbleColors.0
outgoingBubbleFillGradientColor = bubbleColors.1 ?? bubbleColors.0
}
if accentColor.lightness > 0.705 {
let accentColorHsv = accentColor.hsv
accentColor = UIColor(hue: accentColorHsv.0, saturation: min(1.0, accentColorHsv.1 * 1.1), brightness: min(accentColorHsv.2, 0.6), alpha: 1.0)
}
let outgoingBubbleLightnessColor = outgoingBubbleFillColor.mixedWith(outgoingBubbleFillGradientColor, alpha: 0.5)
if outgoingBubbleLightnessColor.lightness > 0.705 {
outgoingPrimaryTextColor = .black
outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.55)
outgoingLinkTextColor = .black
outgoingCheckColor = .black
outgoingControlColor = outgoingPrimaryTextColor
outgoingBubbleFillColor = accentColor
if outgoingBubbleFillColor.distance(to: UIColor.white) < 200 {
outgoingBubbleStrokeColor = UIColor(rgb: 0xc8c7cc)
} else {
outgoingBubbleStrokeColor = outgoingBubbleFillColor
}
let hsv = accentColor.hsv
accentColor = UIColor(hue: hsv.0, saturation: min(1.0, hsv.1 * 1.1), brightness: min(hsv.2, 0.6), alpha: 1.0)
outgoingSelectionBaseColor = accentColor
} else {
outgoingPrimaryTextColor = .white
@ -41,11 +52,10 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
outgoingLinkTextColor = .white
outgoingCheckColor = .white
outgoingControlColor = outgoingPrimaryTextColor
outgoingBubbleFillColor = accentColor
outgoingBubbleStrokeColor = outgoingBubbleFillColor
outgoingSelectionBaseColor = .white
}
let rootTabBar = PresentationThemeRootTabBar(
backgroundColor: UIColor(rgb: 0xf7f7f7),
separatorColor: UIColor(rgb: 0xa3a3a3),
@ -223,7 +233,7 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
let messageDay = PresentationThemeChatMessage(
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffc738), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), mediaControlInnerBackgroundColor: UIColor(rgb: 0xffffff), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleStrokeColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: outgoingPrimaryTextColor, textHighlightColor: UIColor(rgb: 0xffc738), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, mediaControlInnerBackgroundColor: outgoingBubbleFillColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: outgoingSelectionBaseColor.withAlphaComponent(0.2), textSelectionKnobColor: outgoingSelectionBaseColor),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleStrokeColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: outgoingPrimaryTextColor, textHighlightColor: UIColor(rgb: 0xffc738), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, mediaControlInnerBackgroundColor: .clear, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: outgoingSelectionBaseColor.withAlphaComponent(0.2), textSelectionKnobColor: outgoingSelectionBaseColor),
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xDADADE), stroke: UIColor(rgb: 0xE5E5EA)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xE5E5EA))),
infoPrimaryTextColor: UIColor(rgb: 0x000000),
infoLinkTextColor: UIColor(rgb: 0x004bad),
@ -376,7 +386,6 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
name: .builtin(day ? .day : .dayClassic),
referenceTheme: day ? .day : .dayClassic,
overallDarkAppearance: false,
baseColor: baseColor,
intro: intro,
passcode: passcode,
rootController: rootController,
@ -390,12 +399,12 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
)
}
public let defaultPresentationTheme = makeDefaultDayPresentationTheme(accentColor: UIColor(rgb: 0x007ee5), serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, day: false, preview: false)
public let defaultPresentationTheme = makeDefaultDayPresentationTheme(accentColor: UIColor(rgb: 0x007ee5), bubbleColors: nil, serviceBackgroundColor: defaultServiceBackgroundColor, day: false, preview: false)
public let defaultDayAccentColor = UIColor(rgb: 0x007ee5)
public let defaultServiceBackgroundColor = UIColor(rgb: 0x000000, alpha: 0.3)
public func makeDefaultDayPresentationTheme(accentColor: UIColor? = nil, serviceBackgroundColor: UIColor, baseColor: PresentationThemeBaseColor?, day: Bool, preview: Bool) -> PresentationTheme {
public func makeDefaultDayPresentationTheme(accentColor: UIColor? = nil, bubbleColors: (UIColor, UIColor?)?, serviceBackgroundColor: UIColor, day: Bool, preview: Bool) -> PresentationTheme {
let accentColor = accentColor ?? defaultDayAccentColor
return makeDefaultDayPresentationTheme(accentColor: accentColor, serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, day: day, preview: preview)
return makeDefaultDayPresentationTheme(accentColor: accentColor, bubbleColors: bubbleColors, serviceBackgroundColor: serviceBackgroundColor, day: day, preview: preview)
}

View File

@ -3,26 +3,26 @@ import UIKit
import Postbox
import TelegramUIPreferences
public func makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference, accentColor: UIColor?, serviceBackgroundColor: UIColor, baseColor: PresentationThemeBaseColor?, preview: Bool = false) -> PresentationTheme {
public func makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference, accentColor: UIColor?, bubbleColors: (UIColor, UIColor?)?, serviceBackgroundColor: UIColor, preview: Bool = false) -> PresentationTheme {
let theme: PresentationTheme
switch reference {
case .dayClassic:
theme = makeDefaultDayPresentationTheme(serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, day: false, preview: preview)
theme = makeDefaultDayPresentationTheme(bubbleColors: nil, serviceBackgroundColor: serviceBackgroundColor, day: false, preview: preview)
case .night:
theme = makeDarkPresentationTheme(accentColor: accentColor, baseColor: baseColor, preview: preview)
theme = makeDarkPresentationTheme(accentColor: accentColor, bubbleColors: bubbleColors, preview: preview)
case .nightAccent:
theme = makeDarkAccentPresentationTheme(accentColor: accentColor, baseColor: nil, preview: preview)
theme = makeDarkAccentPresentationTheme(accentColor: accentColor, bubbleColors: bubbleColors, preview: preview)
case .day:
theme = makeDefaultDayPresentationTheme(accentColor: accentColor, serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, day: true, preview: preview)
theme = makeDefaultDayPresentationTheme(accentColor: accentColor, bubbleColors: bubbleColors, serviceBackgroundColor: serviceBackgroundColor, day: true, preview: preview)
}
return theme
}
public func makePresentationTheme(mediaBox: MediaBox, themeReference: PresentationThemeReference, accentColor: UIColor?, serviceBackgroundColor: UIColor, baseColor: PresentationThemeBaseColor?, preview: Bool = false) -> PresentationTheme? {
public func makePresentationTheme(mediaBox: MediaBox, themeReference: PresentationThemeReference, accentColor: UIColor?, bubbleColors: (UIColor, UIColor?)?, serviceBackgroundColor: UIColor, preview: Bool = false) -> PresentationTheme? {
let theme: PresentationTheme
switch themeReference {
case let .builtin(reference):
theme = makeDefaultPresentationTheme(reference: reference, accentColor: accentColor, serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, preview: preview)
theme = makeDefaultPresentationTheme(reference: reference, accentColor: accentColor, bubbleColors: bubbleColors, serviceBackgroundColor: serviceBackgroundColor, preview: preview)
case let .local(info):
if let path = mediaBox.completedResourcePath(info.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data, resolvedWallpaper: info.resolvedWallpaper) {
theme = loadedTheme

View File

@ -240,7 +240,9 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager, s
}
let effectiveAccentColor = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.color
let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.baseColor ?? .blue) ?? defaultPresentationTheme
let effectiveBubbleColors = themeSettings.themeSpecificBubbleColors[effectiveTheme.index]?.colors
let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, bubbleColors: effectiveBubbleColors, serviceBackgroundColor: defaultServiceBackgroundColor) ?? defaultPresentationTheme
let effectiveChatWallpaper: TelegramWallpaper = themeSettings.themeSpecificChatWallpapers[effectiveTheme.index] ?? theme.chat.defaultWallpaper
let dateTimeFormat = currentDateTimeFormat()
@ -379,6 +381,30 @@ public func serviceColor(from image: Signal<UIImage?, NoError>) -> Signal<UIColo
}
}
public func serviceColor(for wallpaper: (TelegramWallpaper, UIImage?)) -> UIColor {
switch wallpaper.0 {
case .builtin:
return UIColor(rgb: 0x748391, alpha: 0.45)
case let .color(color):
return serviceColor(with: UIColor(rgb: UInt32(bitPattern: color)))
case let .gradient(topColor, bottomColor):
let mixedColor = UIColor(rgb: UInt32(bitPattern: topColor)).mixedWith(UIColor(rgb: UInt32(bitPattern: bottomColor)), alpha: 0.5)
return serviceColor(with: mixedColor)
case .image:
if let image = wallpaper.1 {
return serviceColor(with: averageColor(from: image))
} else {
return UIColor(rgb: 0x000000, alpha: 0.3)
}
case .file:
if let image = wallpaper.1 {
return serviceColor(with: averageColor(from: image))
} else {
return UIColor(rgb: 0x000000, alpha: 0.3)
}
}
}
public func serviceColor(with color: UIColor) -> UIColor {
var hue: CGFloat = 0.0
var saturation: CGFloat = 0.0
@ -471,7 +497,7 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI
if let themeSpecificWallpaper = themeSettings.themeSpecificChatWallpapers[themeSettings.theme.index] {
currentWallpaper = themeSpecificWallpaper
} else {
let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: themeSettings.theme, accentColor: nil, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil) ?? defaultPresentationTheme
let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: themeSettings.theme, accentColor: nil, bubbleColors: nil, serviceBackgroundColor: defaultServiceBackgroundColor) ?? defaultPresentationTheme
currentWallpaper = theme.chat.defaultWallpaper
}
@ -498,7 +524,9 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI
}
let effectiveAccentColor = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.color
let themeValue = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, serviceBackgroundColor: serviceBackgroundColor, baseColor: themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.baseColor ?? .blue) ?? defaultPresentationTheme
let effectiveBubbleColors = themeSettings.themeSpecificBubbleColors[effectiveTheme.index]?.colors
let themeValue = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, bubbleColors: effectiveBubbleColors, serviceBackgroundColor: serviceBackgroundColor) ?? defaultPresentationTheme
if effectiveTheme != themeSettings.theme && themeSettings.themeSpecificChatWallpapers[effectiveTheme.index] == nil {
switch effectiveChatWallpaper {

View File

@ -477,11 +477,13 @@ public final class PresentationThemeChatList {
public final class PresentationThemeBubbleColorComponents {
public let fill: UIColor
public let gradientFill: UIColor
public let highlightedFill: UIColor
public let stroke: UIColor
public init(fill: UIColor, highlightedFill: UIColor, stroke: UIColor) {
public init(fill: UIColor, gradientFill: UIColor? = nil, highlightedFill: UIColor, stroke: UIColor) {
self.fill = fill
self.gradientFill = gradientFill ?? fill
self.highlightedFill = highlightedFill
self.stroke = stroke
}
@ -971,7 +973,6 @@ public final class PresentationTheme: Equatable {
public let name: PresentationThemeName
public let referenceTheme: PresentationBuiltinThemeReference
public let overallDarkAppearance: Bool
public let baseColor: PresentationThemeBaseColor?
public let intro: PresentationThemeIntro
public let passcode: PresentationThemePasscode
public let rootController: PresentationThemeRootController
@ -985,11 +986,10 @@ public final class PresentationTheme: Equatable {
public let resourceCache: PresentationsResourceCache = PresentationsResourceCache()
public init(name: PresentationThemeName, referenceTheme: PresentationBuiltinThemeReference, overallDarkAppearance: Bool, baseColor: PresentationThemeBaseColor?, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, contextMenu: PresentationThemeContextMenu, inAppNotification: PresentationThemeInAppNotification, preview: Bool = false) {
public init(name: PresentationThemeName, referenceTheme: PresentationBuiltinThemeReference, overallDarkAppearance: Bool, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, contextMenu: PresentationThemeContextMenu, inAppNotification: PresentationThemeInAppNotification, preview: Bool = false) {
self.name = name
self.referenceTheme = referenceTheme
self.overallDarkAppearance = overallDarkAppearance
self.baseColor = baseColor
self.intro = intro
self.passcode = passcode
self.rootController = rootController
@ -1024,6 +1024,6 @@ public final class PresentationTheme: Equatable {
break
}
}
return PresentationTheme(name: name.flatMap(PresentationThemeName.custom) ?? .custom(self.name.string), referenceTheme: self.referenceTheme, overallDarkAppearance: self.overallDarkAppearance, baseColor: nil, intro: self.intro, passcode: self.passcode, rootController: self.rootController, list: self.list, chatList: self.chatList, chat: self.chat.withUpdatedDefaultWallpaper(defaultWallpaper), actionSheet: self.actionSheet, contextMenu: self.contextMenu, inAppNotification: self.inAppNotification)
return PresentationTheme(name: name.flatMap(PresentationThemeName.custom) ?? .custom(self.name.string), referenceTheme: self.referenceTheme, overallDarkAppearance: self.overallDarkAppearance, intro: self.intro, passcode: self.passcode, rootController: self.rootController, list: self.list, chatList: self.chatList, chat: self.chat.withUpdatedDefaultWallpaper(defaultWallpaper), actionSheet: self.actionSheet, contextMenu: self.contextMenu, inAppNotification: self.inAppNotification)
}
}

View File

@ -43,34 +43,38 @@ extension TelegramWallpaper: Codable {
self = .color(Int32(bitPattern: color.rgb))
} else {
let components = value.components(separatedBy: " ")
var slug: String?
var color: Int32?
var intensity: Int32?
var blur = false
var motion = false
if !components.isEmpty {
slug = components[0]
}
if components.count > 1, !["motion", "blur"].contains(components[1]), components[1].count == 6, let value = UIColor(hexString: components[1]) {
color = Int32(bitPattern: value.rgb)
}
if components.count > 2, !["motion", "blur"].contains(components[2]), let value = Int32(components[2]) {
if value >= 0 && value <= 100 {
intensity = value
} else {
intensity = 50
}
}
if components.contains("motion") {
motion = true
}
if components.contains("blur") {
blur = true
}
if let slug = slug {
self = .file(id: 0, accessHash: 0, isCreator: false, isDefault: false, isPattern: color != nil, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: blur, motion: motion, color: color, intensity: intensity))
if components.count == 2 && [6,7].contains(components[0].count) && [6,7].contains(components[1].count), let topColor = UIColor(hexString: components[0]), let bottomColor = UIColor(hexString: components[1]) {
self = .gradient(Int32(bitPattern: topColor.rgb), Int32(bitPattern: bottomColor.rgb))
} else {
throw PresentationThemeDecodingError.generic
var slug: String?
var color: Int32?
var intensity: Int32?
var blur = false
var motion = false
if !components.isEmpty {
slug = components[0]
}
if components.count > 1, !["motion", "blur"].contains(components[1]), components[1].count == 6, let value = UIColor(hexString: components[1]) {
color = Int32(bitPattern: value.rgb)
}
if components.count > 2, !["motion", "blur"].contains(components[2]), let value = Int32(components[2]) {
if value >= 0 && value <= 100 {
intensity = value
} else {
intensity = 50
}
}
if components.contains("motion") {
motion = true
}
if components.contains("blur") {
blur = true
}
if let slug = slug {
self = .file(id: 0, accessHash: 0, isCreator: false, isDefault: false, isPattern: color != nil, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: blur, motion: motion, color: color, intensity: intensity))
} else {
throw PresentationThemeDecodingError.generic
}
}
}
}
@ -84,8 +88,10 @@ extension TelegramWallpaper: Codable {
switch self {
case .builtin:
try container.encode("builtin")
case let .color(value):
try container.encode(String(format: "%06x", value))
case let .color(color):
try container.encode(String(format: "%06x", color))
case let .gradient(topColor, bottomColor):
try container.encode(String(format: "%06x", topColor) + " " + String(format: "%06x", bottomColor))
case let .file(file):
var components: [String] = []
components.append(file.slug)
@ -916,13 +922,16 @@ extension PresentationThemeChatList: Codable {
extension PresentationThemeBubbleColorComponents: Codable {
enum CodingKeys: String, CodingKey {
case bg
case gradientBg
case highlightedBg
case stroke
}
public convenience init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".")
self.init(fill: try decodeColor(values, .bg),
gradientFill: try decodeColor(values, .gradientBg, decoder: decoder, fallbackKey: codingPath + ".bg"),
highlightedFill: try decodeColor(values, .highlightedBg),
stroke: try decodeColor(values, .stroke))
}
@ -930,6 +939,7 @@ extension PresentationThemeBubbleColorComponents: Codable {
public func encode(to encoder: Encoder) throws {
var values = encoder.container(keyedBy: CodingKeys.self)
try encodeColor(&values, self.fill, .bg)
try encodeColor(&values, self.gradientFill, .gradientBg)
try encodeColor(&values, self.highlightedFill, .highlightedBg)
try encodeColor(&values, self.stroke, .stroke)
}
@ -1654,13 +1664,12 @@ extension PresentationTheme: Codable {
if let decoder = decoder as? PresentationThemeDecoding {
let serviceBackgroundColor = decoder.serviceBackgroundColor ?? .black
decoder.referenceTheme = makeDefaultPresentationTheme(reference: referenceTheme, accentColor: nil, serviceBackgroundColor: serviceBackgroundColor, baseColor: nil)
decoder.referenceTheme = makeDefaultPresentationTheme(reference: referenceTheme, accentColor: nil, bubbleColors: nil, serviceBackgroundColor: serviceBackgroundColor)
}
self.init(name: (try? values.decode(PresentationThemeName.self, forKey: .name)) ?? .custom("Untitled"),
referenceTheme: referenceTheme,
overallDarkAppearance: (try? values.decode(Bool.self, forKey: .dark)) ?? false,
baseColor: nil,
intro: try values.decode(PresentationThemeIntro.self, forKey: .intro),
passcode: try values.decode(PresentationThemePasscode.self, forKey: .passcode),
rootController: try values.decode(PresentationThemeRootController.self, forKey: .root),

View File

@ -149,74 +149,57 @@ public final class PrincipalThemeEssentialGraphics {
let theme = presentationTheme.chat
var wallpaper = initialWallpaper
let incoming: PresentationThemeBubbleColorComponents = wallpaper.isEmpty ? theme.message.incoming.bubble.withoutWallpaper : theme.message.incoming.bubble.withWallpaper
let outgoing: PresentationThemeBubbleColorComponents = wallpaper.isEmpty ? theme.message.outgoing.bubble.withoutWallpaper : theme.message.outgoing.bubble.withWallpaper
if knockoutMode {
let wallpaperImage = chatControllerBackgroundImage(theme: presentationTheme, wallpaper: wallpaper, mediaBox: mediaBox, knockoutMode: false)
self.incomingBubbleGradientImage = wallpaperImage
self.outgoingBubbleGradientImage = wallpaperImage
wallpaper = presentationTheme.chat.defaultWallpaper
} else if case .color = wallpaper {
switch presentationTheme.name {
case let .builtin(name):
switch name {
case .day, .night, .nightAccent:
var incomingGradientColors: (UIColor, UIColor)?
if let incomingGradientColors = incomingGradientColors {
self.incomingBubbleGradientImage = generateImage(CGSize(width: 1.0, height: 512.0), opaque: true, scale: 1.0, rotatedContext: { size, context in
var locations: [CGFloat] = [0.0, 1.0]
let colors = [incomingGradientColors.0.cgColor, incomingGradientColors.1.cgColor] as NSArray
let colorSpace = deviceColorSpace
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
})
} else {
self.incomingBubbleGradientImage = nil
}
} else {
var incomingGradientColors: (UIColor, UIColor)?
if incoming.fill.rgb != incoming.gradientFill.rgb {
incomingGradientColors = (incoming.fill, incoming.gradientFill)
}
if let incomingGradientColors = incomingGradientColors {
self.incomingBubbleGradientImage = generateImage(CGSize(width: 1.0, height: 512.0), opaque: true, scale: 1.0, rotatedContext: { size, context in
var locations: [CGFloat] = [0.0, 1.0]
let colors = [incomingGradientColors.0.cgColor, incomingGradientColors.1.cgColor] as NSArray
var outgoingGradientColors: (UIColor, UIColor)?
if let baseColor = presentationTheme.baseColor {
if presentationTheme.baseColor == .custom {
} else {
let colors = baseColor.outgoingGradientColors
if !colors.0.isEqual(colors.1) {
outgoingGradientColors = colors
}
}
}
if let outgoingGradientColors = outgoingGradientColors {
self.outgoingBubbleGradientImage = generateImage(CGSize(width: 1.0, height: 512.0), opaque: true, scale: 1.0, rotatedContext: { size, context in
var locations: [CGFloat] = [0.0, 1.0]
let colors = [outgoingGradientColors.0.cgColor, outgoingGradientColors.1.cgColor] as NSArray
let colorSpace = deviceColorSpace
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
})
} else {
self.outgoingBubbleGradientImage = nil
}
case .dayClassic:
self.incomingBubbleGradientImage = nil
self.outgoingBubbleGradientImage = nil
}
case .custom:
let colorSpace = deviceColorSpace
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
})
} else {
self.incomingBubbleGradientImage = nil
}
var outgoingGradientColors: (UIColor, UIColor)?
if outgoing.fill.rgb != outgoing.gradientFill.rgb {
outgoingGradientColors = (outgoing.fill, outgoing.gradientFill)
}
if let outgoingGradientColors = outgoingGradientColors {
self.outgoingBubbleGradientImage = generateImage(CGSize(width: 1.0, height: 512.0), opaque: true, scale: 1.0, rotatedContext: { size, context in
var locations: [CGFloat] = [0.0, 1.0]
let colors = [outgoingGradientColors.0.cgColor, outgoingGradientColors.1.cgColor] as NSArray
let colorSpace = deviceColorSpace
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
})
} else {
self.outgoingBubbleGradientImage = nil
}
} else {
self.incomingBubbleGradientImage = nil
self.outgoingBubbleGradientImage = nil
}
let incoming: PresentationThemeBubbleColorComponents = wallpaper.isEmpty ? theme.message.incoming.bubble.withoutWallpaper : theme.message.incoming.bubble.withWallpaper
let outgoing: PresentationThemeBubbleColorComponents = wallpaper.isEmpty ? theme.message.outgoing.bubble.withoutWallpaper : theme.message.outgoing.bubble.withWallpaper
let incomingKnockout = self.incomingBubbleGradientImage != nil
let outgoingKnockout = self.outgoingBubbleGradientImage != nil
let serviceColor = serviceMessageColorComponents(chatTheme: theme, wallpaper: wallpaper)
let emptyImage = UIImage()
if preview {
self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
@ -275,8 +258,6 @@ public final class PrincipalThemeEssentialGraphics {
self.outgoingDateAndStatusImpressionIcon = emptyImage
self.mediaImpressionIcon = emptyImage
self.freeImpressionIcon = emptyImage
self.dateStaticBackground = emptyImage
self.dateFloatingBackground = emptyImage
self.radialIndicatorFileIconIncoming = emptyImage
self.radialIndicatorFileIconOutgoing = emptyImage
} else {
@ -325,7 +306,6 @@ public final class PrincipalThemeEssentialGraphics {
self.checkMediaFullImage = generateCheckImage(partial: false, color: .white)!
self.checkMediaPartialImage = generateCheckImage(partial: true, color: .white)!
let serviceColor = serviceMessageColorComponents(chatTheme: theme, wallpaper: wallpaper)
self.checkFreeFullImage = generateCheckImage(partial: false, color: serviceColor.primaryText)!
self.checkFreePartialImage = generateCheckImage(partial: true, color: serviceColor.primaryText)!
@ -349,22 +329,22 @@ public final class PrincipalThemeEssentialGraphics {
self.mediaImpressionIcon = generateTintedImage(image: impressionCountImage, color: .white)!
self.freeImpressionIcon = generateTintedImage(image: impressionCountImage, color: serviceColor.primaryText)!
let chatDateSize: CGFloat = 20.0
self.dateStaticBackground = generateImage(CGSize(width: chatDateSize, height: chatDateSize), contextGenerator: { size, context -> Void in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(serviceColor.dateFillStatic.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
})!.stretchableImage(withLeftCapWidth: Int(chatDateSize) / 2, topCapHeight: Int(chatDateSize) / 2)
self.dateFloatingBackground = generateImage(CGSize(width: chatDateSize, height: chatDateSize), contextGenerator: { size, context -> Void in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(serviceColor.dateFillFloating.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
})!.stretchableImage(withLeftCapWidth: Int(chatDateSize) / 2, topCapHeight: Int(chatDateSize) / 2)
self.radialIndicatorFileIconIncoming = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocument"), color: theme.message.incoming.mediaControlInnerBackgroundColor)!
self.radialIndicatorFileIconOutgoing = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocument"), color: theme.message.outgoing.mediaControlInnerBackgroundColor)!
}
let chatDateSize: CGFloat = 20.0
self.dateStaticBackground = generateImage(CGSize(width: chatDateSize, height: chatDateSize), contextGenerator: { size, context -> Void in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(serviceColor.dateFillStatic.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
})!.stretchableImage(withLeftCapWidth: Int(chatDateSize) / 2, topCapHeight: Int(chatDateSize) / 2)
self.dateFloatingBackground = generateImage(CGSize(width: chatDateSize, height: chatDateSize), contextGenerator: { size, context -> Void in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(serviceColor.dateFillFloating.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
})!.stretchableImage(withLeftCapWidth: Int(chatDateSize) / 2, topCapHeight: Int(chatDateSize) / 2)
}
}

View File

@ -271,6 +271,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
self.backgroundNode.image = chatControllerBackgroundImage(theme: chatPresentationInterfaceState.theme, wallpaper: chatPresentationInterfaceState.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
if case .gradient = chatPresentationInterfaceState.chatWallpaper {
self.backgroundNode.imageContentMode = .scaleToFill
} else {
self.backgroundNode.imageContentMode = .scaleAspectFill
}
self.backgroundNode.motionEnabled = chatPresentationInterfaceState.chatWallpaper.settings?.motion ?? false
self.historyNode.verticalScrollIndicatorColor = UIColor(white: 0.5, alpha: 0.8)
@ -1427,6 +1432,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
if self.chatPresentationInterfaceState.chatWallpaper != chatPresentationInterfaceState.chatWallpaper {
self.backgroundNode.image = chatControllerBackgroundImage(theme: chatPresentationInterfaceState.theme, wallpaper: chatPresentationInterfaceState.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: self.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
if case .gradient = chatPresentationInterfaceState.chatWallpaper {
self.backgroundNode.imageContentMode = .scaleToFill
} else {
self.backgroundNode.imageContentMode = .scaleAspectFill
}
self.backgroundNode.motionEnabled = chatPresentationInterfaceState.chatWallpaper.settings?.motion ?? false
}

View File

@ -4,7 +4,7 @@ import Display
import Postbox
import TelegramPresentationData
private let maskInset: CGFloat = 1.0
private let maskInset: CGFloat = UIScreenPixel
final class ChatMessageBubbleBackdrop: ASDisplayNode {
private let backgroundContent: ASDisplayNode
@ -19,7 +19,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
override var frame: CGRect {
didSet {
if let maskView = self.maskView {
let maskFrame = self.bounds.insetBy(dx: -1.0, dy: -1.0)
let maskFrame = self.bounds.insetBy(dx: -maskInset, dy: -maskInset)
if maskView.frame != maskFrame {
maskView.frame = maskFrame
}
@ -101,7 +101,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
maskView = current
} else {
maskView = UIImageView()
maskView.frame = self.bounds.insetBy(dx: -1.0, dy: -1.0)
maskView.frame = self.bounds.insetBy(dx: -maskInset, dy: -maskInset)
self.maskView = maskView
self.view.mask = maskView
}
@ -143,7 +143,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
func updateFrame(_ value: CGRect, transition: ContainedViewLayoutTransition) {
if let maskView = self.maskView {
transition.updateFrame(view: maskView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width + 2.0, height: value.size.height + 2.0)))
transition.updateFrame(view: maskView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width + maskInset * 2.0, height: value.size.height + maskInset * 2.0)))
}
transition.updateFrame(node: self, frame: value)
}

View File

@ -234,7 +234,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
guard let strongSelf = self, let item = strongSelf.item else {
return
}
strongSelf.backgroundWallpaperNode.setMaskMode(isExtractedToContextPreview, mediaBox: item.context.account.postbox.mediaBox)
strongSelf.backgroundWallpaperNode.setMaskMode(strongSelf.backgroundMaskMode, mediaBox: item.context.account.postbox.mediaBox)
strongSelf.backgroundNode.setMaskMode(isExtractedToContextPreview)
if !isExtractedToContextPreview, let (rect, size) = strongSelf.absoluteRect {
strongSelf.updateAbsoluteRect(rect, within: size)
@ -1631,7 +1631,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
backgroundType = .incoming(mergeType)
}
strongSelf.backgroundNode.setType(type: backgroundType, highlighted: strongSelf.highlightedState, graphics: graphics, maskMode: strongSelf.contextSourceNode.isExtractedToContextPreview, transition: transition)
strongSelf.backgroundWallpaperNode.setType(type: backgroundType, theme: item.presentationData.theme, mediaBox: item.context.account.postbox.mediaBox, essentialGraphics: graphics, maskMode: strongSelf.contextSourceNode.isExtractedToContextPreview)
strongSelf.backgroundWallpaperNode.setType(type: backgroundType, theme: item.presentationData.theme, mediaBox: item.context.account.postbox.mediaBox, essentialGraphics: graphics, maskMode: strongSelf.backgroundMaskMode)
strongSelf.backgroundType = backgroundType
@ -2847,4 +2847,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
}
return nil
}
private var backgroundMaskMode: Bool {
return self.contextSourceNode.isExtractedToContextPreview || (self.item?.presentationData.theme.wallpaper.hasWallpaper ?? false)
}
}

View File

@ -50,6 +50,13 @@ final class ChatMessageDateHeader: ListViewItemHeader {
func node() -> ListViewItemHeaderNode {
return ChatMessageDateHeaderNode(localTimestamp: self.roundedTimestamp, scheduled: self.scheduled, presentationData: self.presentationData, context: self.context, action: self.action)
}
func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) {
guard let node = node as? ChatMessageDateHeaderNode, let next = next as? ChatMessageDateHeader else {
return
}
node.updatePresentationData(next.presentationData, context: next.context)
}
}
private let titleFont = Font.medium(13.0)
@ -93,6 +100,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
private let localTimestamp: Int32
private var presentationData: ChatPresentationData
private let context: AccountContext
private let text: String
private var flashingOnScrolling = false
private var stickDistanceFactor: CGFloat = 0.0
@ -107,7 +115,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
self.labelNode = TextNode()
self.labelNode.isUserInteractionEnabled = false
self.labelNode.displaysAsynchronously = true
self.labelNode.displaysAsynchronously = !presentationData.isPreview
self.backgroundNode = ASImageNode()
self.backgroundNode.isLayerBacked = true
@ -119,19 +127,6 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
self.stickBackgroundNode.displayWithoutProcessing = true
self.stickBackgroundNode.displaysAsynchronously = false
super.init(layerBacked: false, dynamicBounce: true, isRotated: true, seeThrough: false)
self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
self.backgroundNode.image = graphics.dateStaticBackground
self.stickBackgroundNode.image = graphics.dateFloatingBackground
self.stickBackgroundNode.alpha = 0.0
self.backgroundNode.addSubnode(self.stickBackgroundNode)
self.addSubnode(self.backgroundNode)
self.addSubnode(self.labelNode)
let nowTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
var t: time_t = time_t(localTimestamp)
@ -162,6 +157,20 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
text = presentationData.strings.ScheduledMessages_ScheduledDate(text).0
}
}
self.text = text
super.init(layerBacked: false, dynamicBounce: true, isRotated: true, seeThrough: false)
self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
self.backgroundNode.image = graphics.dateStaticBackground
self.stickBackgroundNode.image = graphics.dateFloatingBackground
self.stickBackgroundNode.alpha = 0.0
self.backgroundNode.addSubnode(self.stickBackgroundNode)
self.addSubnode(self.backgroundNode)
self.addSubnode(self.labelNode)
let attributedString = NSAttributedString(string: text, font: titleFont, textColor: bubbleVariableColor(variableColor: presentationData.theme.theme.chat.serviceMessage.dateTextColor, wallpaper: presentationData.theme.wallpaper))
let labelLayout = TextNode.asyncLayout(self.labelNode)
@ -183,6 +192,13 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
self.backgroundNode.image = graphics.dateStaticBackground
self.stickBackgroundNode.image = graphics.dateFloatingBackground
let attributedString = NSAttributedString(string: self.text, font: titleFont, textColor: bubbleVariableColor(variableColor: presentationData.theme.theme.chat.serviceMessage.dateTextColor, wallpaper: presentationData.theme.wallpaper))
let labelLayout = TextNode.asyncLayout(self.labelNode)
let (size, apply) = labelLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let _ = apply()
self.labelNode.frame = CGRect(origin: CGPoint(), size: size.size)
self.setNeedsLayout()
}

View File

@ -46,6 +46,10 @@ final class ListMessageDateHeader: ListViewItemHeader {
func node() -> ListViewItemHeaderNode {
return ListMessageDateHeaderNode(theme: self.theme, strings: self.strings, roundedTimestamp: self.roundedTimestamp, month: self.month, year: self.year)
}
func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) {
}
}
private let sectionTitleFont = Font.regular(14.0)

View File

@ -1055,6 +1055,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return ChatMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, animatedEmojiScale: 1.0, isPreview: true), context: context, chatLocation: .peer(message.id.peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, isScheduledMessages: false, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus), controllerInteraction: defaultChatControllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: true, additionalContent: nil)
}
public func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader {
return ChatMessageDateHeader(timestamp: timestamp, scheduled: false, presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, animatedEmojiScale: 1.0, isPreview: true), context: context)
}
public func openWallet(context: AccountContext, walletContext: OpenWalletContext, present: @escaping (ViewController) -> Void) {
guard let storedContext = context.tonContext else {
return

View File

@ -345,35 +345,6 @@ public enum PresentationThemeBaseColor: Int32, CaseIterable {
}
return UIColor(rgb: value)
}
public var outgoingGradientColors: (UIColor, UIColor) {
switch self {
case .blue:
return (UIColor(rgb: 0x63BFFB), UIColor(rgb: 0x007AFF))
case .cyan:
return (UIColor(rgb: 0x5CE0E9), UIColor(rgb: 0x00C2ED))
case .green:
return (UIColor(rgb: 0x93D374), UIColor(rgb: 0x29B327))
case .pink:
return (UIColor(rgb: 0xE296C1), UIColor(rgb: 0xEB6CA4))
case .orange:
return (UIColor(rgb: 0xF2A451), UIColor(rgb: 0xF08200))
case .purple:
return (UIColor(rgb: 0xAC98E6), UIColor(rgb: 0x9472EE))
case .red:
return (UIColor(rgb: 0xE06D54), UIColor(rgb: 0xD33213))
case .yellow:
return (UIColor(rgb: 0xF7DA6B), UIColor(rgb: 0xEDB400))
case .gray:
return (UIColor(rgb: 0x7D8E9A), UIColor(rgb: 0x6D839E))
case .black:
return (UIColor(rgb: 0x000000), UIColor(rgb: 0x000000))
case .white:
return (UIColor(rgb: 0xffffff), UIColor(rgb: 0xffffff))
case .custom:
return (UIColor(rgb: 0x000000), UIColor(rgb: 0x000000))
}
}
}
public struct PresentationThemeColorPair: PostboxCoding, Equatable {
@ -399,13 +370,21 @@ public struct PresentationThemeColorPair: PostboxCoding, Equatable {
}
}
public var colors: [UIColor] {
public var colors: (UIColor, UIColor?) {
if let bottomColor = self.optionalColor {
return [UIColor(rgb: UInt32(bitPattern: self.color)), UIColor(rgb: UInt32(bitPattern: bottomColor))]
return (UIColor(rgb: UInt32(bitPattern: self.color)), UIColor(rgb: UInt32(bitPattern: bottomColor)))
} else {
return [UIColor(rgb: UInt32(bitPattern: self.color))]
return (UIColor(rgb: UInt32(bitPattern: self.color)), nil)
}
}
public var plainColors: (UIColor, UIColor) {
if let bottomColor = self.optionalColor {
return (UIColor(rgb: UInt32(bitPattern: self.color)), UIColor(rgb: UInt32(bitPattern: bottomColor)))
} else {
return (UIColor(rgb: UInt32(bitPattern: self.color)), UIColor(rgb: UInt32(bitPattern: self.color)))
}
}
}
public struct PresentationThemeAccentColor: PostboxCoding, Equatable {
@ -520,32 +499,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
}, valueDecoder: { decoder in
return PresentationThemeColorPair(decoder: decoder)
})
if self.themeSpecificAccentColors[PresentationThemeReference.builtin(.day).index] == nil, let themeAccentColor = decoder.decodeOptionalInt32ForKey("themeAccentColor") {
let baseColor: PresentationThemeBaseColor
switch themeAccentColor {
case 0xf83b4c:
baseColor = .red
case 0xff7519:
baseColor = .orange
case 0xeba239:
baseColor = .yellow
case 0x29b327:
baseColor = .green
case 0x00c2ed:
baseColor = .cyan
case 0x007ee5:
baseColor = .blue
case 0x7748ff:
baseColor = .purple
case 0xff5da2:
baseColor = .pink
default:
baseColor = .blue
}
self.themeSpecificAccentColors[PresentationThemeReference.builtin(.day).index] = PresentationThemeAccentColor(baseColor: baseColor)
}
self.fontSize = PresentationFontSize(rawValue: decoder.decodeInt32ForKey("f", orElse: PresentationFontSize.regular.rawValue)) ?? .regular
self.automaticThemeSwitchSetting = (decoder.decodeObjectForKey("automaticThemeSwitchSetting", decoder: { AutomaticThemeSwitchSetting(decoder: $0) }) as? AutomaticThemeSwitchSetting) ?? AutomaticThemeSwitchSetting(trigger: .system, theme: .builtin(.night))
self.largeEmoji = decoder.decodeBoolForKey("largeEmoji", orElse: true)

View File

@ -520,6 +520,10 @@ private final class WalletInfoTransactionDateHeader: ListViewItemHeader {
func node() -> ListViewItemHeaderNode {
return WalletInfoTransactionDateHeaderNode(theme: self.theme, strings: self.strings, roundedTimestamp: self.localTimestamp)
}
func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) {
}
}
private let sectionTitleFont = Font.semibold(17.0)

View File

@ -505,7 +505,7 @@ public func gradientImage(_ colors: [UIColor]) -> Signal<(TransformImageArgument
let context = DrawingContext(size: arguments.drawingSize, clear: true)
context.withFlippedContext { c in
let gradientColors = colors as CFArray
let gradientColors = colors.map { $0.cgColor } as CFArray
let delta: CGFloat = 1.0 / (CGFloat(colors.count) - 1.0)
var locations: [CGFloat] = []
@ -690,9 +690,15 @@ public func drawThemeImage(context c: CGContext, theme: PresentationTheme, wallp
let size = image.size.aspectFilled(drawingRect.size)
c.draw(cgImage, in: CGRect(origin: CGPoint(x: (drawingRect.size.width - size.width) / 2.0, y: (drawingRect.size.height - size.height) / 2.0), size: size))
}
case let .color(value):
c.setFillColor(UIColor(rgb: UInt32(bitPattern: value)).cgColor)
case let .color(color):
c.setFillColor(UIColor(rgb: UInt32(bitPattern: color)).cgColor)
c.fill(drawingRect)
case let .gradient(topColor, bottomColor):
let gradientColors = [UIColor(rgb: UInt32(bitPattern: topColor)), UIColor(rgb: UInt32(bitPattern: bottomColor))].map { $0.cgColor } as CFArray
var locations: [CGFloat] = [0.0, 1.0]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: drawingRect.height), end: CGPoint(x: 0.0, y: 0.0), options: CGGradientDrawingOptions())
case .file:
c.setFillColor(theme.chatList.backgroundColor.cgColor)
c.fill(drawingRect)
@ -739,30 +745,58 @@ public func drawThemeImage(context c: CGContext, theme: PresentationTheme, wallp
c.draw(image, in: CGRect(origin: CGPoint(x: drawingRect.width - 3.0 - 29.0, y: 7.0 + UIScreenPixel), size: microphone.size.fitted(CGSize(width: 30.0, height: 30.0))))
}
let incoming = theme.chat.message.incoming.bubble.withoutWallpaper
let outgoing = theme.chat.message.outgoing.bubble.withoutWallpaper
c.saveGState()
c.setFillColor(theme.chat.message.incoming.bubble.withoutWallpaper.fill.cgColor)
c.setStrokeColor(theme.chat.message.incoming.bubble.withoutWallpaper.stroke.cgColor)
c.translateBy(x: 5.0, y: 65.0)
c.translateBy(x: 114.0, y: 32.0)
c.scaleBy(x: 1.0, y: -1.0)
c.translateBy(x: -114.0, y: -32.0)
let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ")
c.strokePath()
let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ")
c.fillPath()
if incoming.fill.rgb != incoming.gradientFill.rgb {
let gradientColors = [incoming.fill, incoming.gradientFill].map { $0.cgColor } as CFArray
var locations: [CGFloat] = [0.0, 1.0]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: 32.0), options: CGGradientDrawingOptions())
} else {
c.setFillColor(incoming.fill.cgColor)
c.setStrokeColor(incoming.stroke.cgColor)
c.strokePath()
let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ")
c.fillPath()
}
c.restoreGState()
c.saveGState()
c.setFillColor(theme.chat.message.outgoing.bubble.withoutWallpaper.fill.cgColor)
c.setStrokeColor(theme.chat.message.outgoing.bubble.withoutWallpaper.stroke.cgColor)
c.translateBy(x: drawingRect.width - 114.0 - 5.0, y: 25.0)
c.translateBy(x: 114.0, y: 32.0)
c.scaleBy(x: -1.0, y: -1.0)
c.translateBy(x: 0, y: -32.0)
let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ")
c.strokePath()
let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ")
c.fillPath()
if outgoing.fill.rgb != outgoing.gradientFill.rgb {
c.clip()
let gradientColors = [outgoing.fill, outgoing.gradientFill].map { $0.cgColor } as CFArray
var locations: [CGFloat] = [0.0, 1.0]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: 32.0), options: CGGradientDrawingOptions())
} else {
c.setFillColor(outgoing.fill.cgColor)
c.setStrokeColor(outgoing.stroke.cgColor)
c.strokePath()
let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ")
c.fillPath()
}
c.restoreGState()
}
@ -961,20 +995,33 @@ public func themeImage(account: Account, accountManager: AccountManager, fileRef
}
}
public func themeIconImage(account: Account, accountManager: AccountManager, theme: PresentationThemeReference, accentColor: UIColor?) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
let signal: Signal<(UIColor, UIColor, UIColor, UIImage?), NoError>
public func themeIconImage(account: Account, accountManager: AccountManager, theme: PresentationThemeReference, accentColor: UIColor?, bubbleColors: (UIColor, UIColor)?) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
let colorsSignal: Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?), NoError>
if case let .builtin(theme) = theme {
let backgroundColor: UIColor
let incomingColor: UIColor
let outgoingColor: (UIColor, UIColor)
switch theme {
case .dayClassic:
signal = .single((UIColor(rgb: 0xd6e2ee), UIColor(rgb: 0xffffff), UIColor(rgb: 0xe1ffc7), nil))
backgroundColor = UIColor(rgb: 0xd6e2ee)
incomingColor = UIColor(rgb: 0xffffff)
outgoingColor = (UIColor(rgb: 0xe1ffc7), UIColor(rgb: 0xe1ffc7))
case .day:
signal = .single((.white, UIColor(rgb: 0xd5dde6), accentColor ?? UIColor(rgb: 0x007aff), nil))
backgroundColor = UIColor(rgb: 0xffffff)
incomingColor = UIColor(rgb: 0xd5dde6)
outgoingColor = bubbleColors ?? (UIColor(rgb: 0x007aff), UIColor(rgb: 0x007aff))
case .night:
signal = .single((.black, UIColor(rgb: 0x1f1f1f), accentColor ?? UIColor(rgb: 0x313131), nil))
backgroundColor = UIColor(rgb: 0x000000)
incomingColor = UIColor(rgb: 0x1f1f1f)
outgoingColor = bubbleColors ?? (UIColor(rgb: 0x313131), UIColor(rgb: 0x313131))
case .nightAccent:
let accentColor = accentColor ?? UIColor(rgb: 0x007aff)
signal = .single((accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18), accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25), accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59), nil))
backgroundColor = accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18)
incomingColor = accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25)
let accentBubbleColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59)
outgoingColor = bubbleColors ?? (accentBubbleColor, accentBubbleColor)
}
colorsSignal = .single(((backgroundColor, nil), (incomingColor, incomingColor), outgoingColor, nil))
} else {
var resource: MediaResource?
if case let .local(theme) = theme {
@ -983,27 +1030,27 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
resource = theme.theme.file?.resource
}
if let resource = resource {
signal = telegramThemeData(account: account, accountManager: accountManager, resource: resource, synchronousLoad: false)
|> mapToSignal { data -> Signal<(UIColor, UIColor, UIColor, UIImage?), NoError> in
colorsSignal = telegramThemeData(account: account, accountManager: accountManager, resource: resource, synchronousLoad: false)
|> mapToSignal { data -> Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?), NoError> in
if let data = data, let theme = makePresentationTheme(data: data) {
var wallpaperSignal: Signal<(UIColor, UIColor, UIColor, UIImage?), NoError> = .complete()
let backgroundColor: UIColor
let incomingColor = theme.chat.message.incoming.bubble.withoutWallpaper.fill
let outgoingColor = theme.chat.message.outgoing.bubble.withoutWallpaper.fill
var wallpaperSignal: Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?), NoError> = .complete()
let backgroundColor: (UIColor, UIColor?)
let incomingColor = (theme.chat.message.incoming.bubble.withoutWallpaper.fill, theme.chat.message.incoming.bubble.withoutWallpaper.gradientFill)
let outgoingColor = (theme.chat.message.outgoing.bubble.withoutWallpaper.fill, theme.chat.message.outgoing.bubble.withoutWallpaper.gradientFill)
switch theme.chat.defaultWallpaper {
case .builtin:
backgroundColor = UIColor(rgb: 0xd6e2ee)
backgroundColor = (UIColor(rgb: 0xd6e2ee), nil)
case let .color(color):
backgroundColor = UIColor(rgb: UInt32(bitPattern: color))
backgroundColor = (UIColor(rgb: UInt32(bitPattern: color)), nil)
case let .gradient(topColor, bottomColor):
backgroundColor = UIColor(rgb: UInt32(bitPattern: topColor))
backgroundColor = (UIColor(rgb: UInt32(bitPattern: topColor)), UIColor(rgb: UInt32(bitPattern: bottomColor)))
case .image:
backgroundColor = .black
backgroundColor = (.black, nil)
case let .file(file):
if file.isPattern, let color = file.settings.color {
backgroundColor = UIColor(rgb: UInt32(bitPattern: color))
backgroundColor = (UIColor(rgb: UInt32(bitPattern: color)), nil)
} else {
backgroundColor = theme.chatList.backgroundColor
backgroundColor = (theme.chatList.backgroundColor, nil)
}
wallpaperSignal = cachedWallpaper(account: account, slug: file.slug, settings: file.settings)
|> mapToSignal { wallpaper in
@ -1011,7 +1058,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
var convertedRepresentations: [ImageRepresentationWithReference] = []
convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource), reference: .media(media: .standalone(media: file.file), resource: file.file.resource)))
return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
|> mapToSignal { _, fullSizeData, complete -> Signal<(UIColor, UIColor, UIColor, UIImage?), NoError> in
|> mapToSignal { _, fullSizeData, complete -> Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?), NoError> in
guard complete, let fullSizeData = fullSizeData else {
return .complete()
}
@ -1050,55 +1097,66 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
}
}
} else {
signal = .never()
colorsSignal = .never()
}
}
return signal
|> map { colors in
return { arguments in
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: arguments.emptyColor == nil)
let drawingRect = arguments.drawingRect
context.withContext { c in
c.setFillColor(colors.0.cgColor)
return colorsSignal
|> map { colors in
return { arguments in
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: arguments.emptyColor == nil)
let drawingRect = arguments.drawingRect
context.withContext { c in
if let secondBackgroundColor = colors.0.1 {
let gradientColors = [colors.0.0, secondBackgroundColor].map { $0.cgColor } as CFArray
var locations: [CGFloat] = [0.0, 1.0]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: drawingRect.height), options: CGGradientDrawingOptions())
} else {
c.setFillColor(colors.0.0.cgColor)
c.fill(drawingRect)
if let image = colors.3 {
let initialThumbnailContextFittingSize = arguments.imageSize.fitted(CGSize(width: 90.0, height: 90.0))
let thumbnailContextSize = image.size.aspectFilled(initialThumbnailContextFittingSize)
let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0)
thumbnailContext.withFlippedContext { c in
c.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: thumbnailContextSize))
}
telegramFastBlurMore(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes)
if let blurredThumbnailImage = thumbnailContext.generateImage(), let cgImage = blurredThumbnailImage.cgImage {
let fittedSize = thumbnailContext.size.aspectFilled(CGSize(width: drawingRect.size.width + 1.0, height: drawingRect.size.height + 1.0))
c.saveGState()
c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
c.scaleBy(x: 1.0, y: -1.0)
c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
c.draw(cgImage, in: CGRect(origin: CGPoint(x: (drawingRect.size.width - fittedSize.width) / 2.0, y: (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize))
c.restoreGState()
}
}
let incoming = generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), color: colors.1)
let outgoing = generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), color: colors.2)
c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
c.scaleBy(x: 1.0, y: -1.0)
c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
c.draw(incoming!.cgImage!, in: CGRect(x: 9.0, y: 34.0, width: 57.0, height: 16.0))
c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
c.scaleBy(x: -1.0, y: 1.0)
c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
c.draw(outgoing!.cgImage!, in: CGRect(x: 9.0, y: 12.0, width: 57.0, height: 16.0))
}
return context
if let image = colors.3 {
let initialThumbnailContextFittingSize = arguments.imageSize.fitted(CGSize(width: 90.0, height: 90.0))
let thumbnailContextSize = image.size.aspectFilled(initialThumbnailContextFittingSize)
let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0)
thumbnailContext.withFlippedContext { c in
c.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: thumbnailContextSize))
}
telegramFastBlurMore(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes)
if let blurredThumbnailImage = thumbnailContext.generateImage(), let cgImage = blurredThumbnailImage.cgImage {
let fittedSize = thumbnailContext.size.aspectFilled(CGSize(width: drawingRect.size.width + 1.0, height: drawingRect.size.height + 1.0))
c.saveGState()
c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
c.scaleBy(x: 1.0, y: -1.0)
c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
c.draw(cgImage, in: CGRect(origin: CGPoint(x: (drawingRect.size.width - fittedSize.width) / 2.0, y: (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize))
c.restoreGState()
}
}
let incomingColors = [colors.1.0, colors.1.1]
let incoming = generateGradientTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), colors: incomingColors)
let outgoingColors = [colors.2.0, colors.2.1]
let outgoing = generateGradientTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), colors: outgoingColors)
c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
c.scaleBy(x: 1.0, y: -1.0)
c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
c.draw(incoming!.cgImage!, in: CGRect(x: 9.0, y: 34.0, width: 57.0, height: 16.0))
c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
c.scaleBy(x: -1.0, y: 1.0)
c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
c.draw(outgoing!.cgImage!, in: CGRect(x: 9.0, y: 12.0, width: 57.0, height: 16.0))
}
return context
}
}
}