mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Add support for gradient wallpapers
Implement new colors selection UI
This commit is contained in:
parent
692b37fa4d
commit
78a09a38b3
@ -144,6 +144,7 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
|
||||
}
|
||||
|
||||
public var selectedIndexChanged: (Int) -> Void = { _ in }
|
||||
public var selectedIndexShouldChange: (Int) -> Bool = { _ in return true }
|
||||
|
||||
public init(theme: SegmentedControlTheme, items: [SegmentedControlItem], selectedIndex: Int) {
|
||||
self.theme = theme
|
||||
@ -347,6 +348,10 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
|
||||
return
|
||||
}
|
||||
|
||||
guard self.selectedIndexShouldChange(index) else {
|
||||
return
|
||||
}
|
||||
|
||||
self._selectedIndex = index
|
||||
self.selectedIndexChanged(index)
|
||||
if let layout = self.validLayout {
|
||||
@ -355,8 +360,7 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
|
||||
}
|
||||
|
||||
public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
let location = gestureRecognizer.location(in: self.view)
|
||||
return self.selectionNode.frame.contains(location)
|
||||
return self.selectionNode.frame.contains(gestureRecognizer.location(in: self.view))
|
||||
}
|
||||
|
||||
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
@ -382,8 +386,14 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
|
||||
case .ended:
|
||||
if let gestureSelectedIndex = self.gestureSelectedIndex {
|
||||
if gestureSelectedIndex != self.selectedIndex {
|
||||
self._selectedIndex = gestureSelectedIndex
|
||||
self.selectedIndexChanged(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.gestureSelectedIndex = nil
|
||||
}
|
||||
|
@ -138,6 +138,7 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE
|
||||
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data)
|
||||
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
|
||||
|
||||
let autoNightModeTriggered = context.sharedContext.currentPresentationData.with {$0 }.autoNightModeTriggered
|
||||
let accountManager = context.sharedContext.accountManager
|
||||
let account = context.account
|
||||
let updateWallpaper: (TelegramWallpaper) -> Void = { wallpaper in
|
||||
@ -155,8 +156,12 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE
|
||||
|
||||
let _ = (updatePresentationThemeSettingsInteractively(accountManager: accountManager, { current in
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
themeSpecificChatWallpapers[current.theme.index] = wallpaper
|
||||
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
if autoNightModeTriggered {
|
||||
themeSpecificChatWallpapers[current.automaticThemeSwitchSetting.theme.index] = wallpaper
|
||||
} else {
|
||||
themeSpecificChatWallpapers[current.theme.index] = wallpaper
|
||||
}
|
||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
})).start()
|
||||
}
|
||||
|
||||
|
@ -464,11 +464,11 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
||||
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data)
|
||||
}
|
||||
let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper))
|
||||
var chatWallpaper = current.chatWallpaper
|
||||
if let theme = theme {
|
||||
chatWallpaper = resolvedWallpaper ?? theme.chat.defaultWallpaper
|
||||
}
|
||||
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
themeSpecificChatWallpapers[themeReference.index] = nil
|
||||
|
||||
return PresentationThemeSettings(theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
})
|
||||
} |> deliverOnMainQueue).start(completed: {
|
||||
if !hasCustomFile {
|
||||
@ -505,11 +505,11 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
||||
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data)
|
||||
}
|
||||
let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper))
|
||||
var chatWallpaper = current.chatWallpaper
|
||||
if let theme = theme {
|
||||
chatWallpaper = resolvedWallpaper ?? theme.chat.defaultWallpaper
|
||||
}
|
||||
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
themeSpecificChatWallpapers[themeReference.index] = nil
|
||||
|
||||
return PresentationThemeSettings(theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
})
|
||||
} |> deliverOnMainQueue).start(completed: {
|
||||
if let themeResource = themeResource, !hasCustomFile {
|
||||
|
@ -99,7 +99,12 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
|
||||
self.backgroundNode.isHidden = false
|
||||
self.backgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: color))
|
||||
}
|
||||
|
||||
case let .gradient(topColor, bottomColor):
|
||||
self.imageNode.isHidden = false
|
||||
self.backgroundNode.isHidden = true
|
||||
self.imageNode.setSignal(gradientImage([UIColor(rgb: UInt32(bitPattern: topColor)), UIColor(rgb: UInt32(bitPattern: bottomColor))]))
|
||||
let apply = self.imageNode.asyncLayout()(TransformImageArguments(corners: corners, imageSize: CGSize(), boundingSize: size, intrinsicInsets: UIEdgeInsets()))
|
||||
apply()
|
||||
case let .image(representations, _):
|
||||
self.imageNode.isHidden = false
|
||||
self.backgroundNode.isHidden = true
|
||||
@ -144,7 +149,7 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
|
||||
}
|
||||
} else if let wallpaper = self.wallpaper {
|
||||
switch wallpaper {
|
||||
case .builtin, .color:
|
||||
case .builtin, .color, .gradient:
|
||||
let apply = self.imageNode.asyncLayout()(TransformImageArguments(corners: corners, imageSize: CGSize(), boundingSize: size, intrinsicInsets: UIEdgeInsets()))
|
||||
apply()
|
||||
case let .image(representations, _):
|
||||
|
@ -15,8 +15,9 @@ private let colors: [Int32] = [0x007aff, 0x00c2ed, 0x29b327, 0xeb6ca4, 0xf08200,
|
||||
final class ThemeAccentColorController: ViewController {
|
||||
private let context: AccountContext
|
||||
private let currentTheme: PresentationThemeReference
|
||||
private let initialColor: UIColor
|
||||
private let initialTheme: PresentationTheme
|
||||
private let section: ThemeColorSection
|
||||
|
||||
let segmentedTitleView: ThemeColorSegmentedTitleView
|
||||
|
||||
private var controllerNode: ThemeAccentColorControllerNode {
|
||||
return self.displayNode as! ThemeAccentColorControllerNode
|
||||
@ -24,30 +25,41 @@ final class ThemeAccentColorController: ViewController {
|
||||
|
||||
private var presentationData: PresentationData
|
||||
|
||||
init(context: AccountContext, currentTheme: PresentationThemeReference, currentColor: UIColor?) {
|
||||
private let _ready = Promise<Bool>()
|
||||
override public var ready: Promise<Bool> {
|
||||
return self._ready
|
||||
}
|
||||
|
||||
init(context: AccountContext, currentTheme: PresentationThemeReference, section: ThemeColorSection) {
|
||||
self.context = context
|
||||
self.currentTheme = currentTheme
|
||||
|
||||
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.initialColor = color
|
||||
self.initialTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, preview: true) ?? defaultPresentationTheme
|
||||
|
||||
self.section = section
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.initialTheme, presentationStrings: self.presentationData.strings))
|
||||
// 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.title = self.presentationData.strings.AccentColor_Title
|
||||
self.segmentedTitleView = ThemeColorSegmentedTitleView(theme: self.presentationData.theme, strings: self.presentationData.strings, selectedSection: .accent)
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings))
|
||||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
|
||||
self.segmentedTitleView.sectionUpdated = { [weak self] section in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerNode.updateSection(section)
|
||||
}
|
||||
}
|
||||
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView())
|
||||
self.navigationItem.titleView = self.segmentedTitleView
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
@ -57,11 +69,11 @@ final class ThemeAccentColorController: ViewController {
|
||||
override func loadDisplayNode() {
|
||||
super.loadDisplayNode()
|
||||
|
||||
self.displayNode = ThemeAccentColorControllerNode(context: self.context, currentTheme: self.currentTheme, color: self.initialColor, theme: self.initialTheme, dismiss: { [weak self] in
|
||||
self.displayNode = ThemeAccentColorControllerNode(context: self.context, currentTheme: self.currentTheme, theme: self.presentationData.theme, dismiss: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.dismiss()
|
||||
}
|
||||
}, apply: { [weak self] in
|
||||
}, apply: { [weak self] state in
|
||||
if let strongSelf = self {
|
||||
let context = strongSelf.context
|
||||
let _ = (updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||
@ -70,22 +82,19 @@ final class ThemeAccentColorController: ViewController {
|
||||
if autoNightModeTriggered {
|
||||
currentTheme = current.automaticThemeSwitchSetting.theme
|
||||
}
|
||||
|
||||
var themeSpecificAccentColors = current.themeSpecificAccentColors
|
||||
let color = PresentationThemeAccentColor(baseColor: .custom, value: Int32(bitPattern: strongSelf.controllerNode.color))
|
||||
themeSpecificAccentColors[currentTheme.index] = color
|
||||
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
var themeSpecificAccentColors = current.themeSpecificAccentColors
|
||||
//let color = PresentationThemeAccentColor(baseColor: .custom, value: Int32(bitPattern: strongSelf.controllerNode.color))
|
||||
//themeSpecificAccentColors[currentTheme.index] = color
|
||||
|
||||
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: UIColor(rgb: strongSelf.controllerNode.color), serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor) ?? defaultPresentationTheme
|
||||
var chatWallpaper = current.chatWallpaper
|
||||
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 {
|
||||
chatWallpaper = theme.chat.defaultWallpaper
|
||||
themeSpecificChatWallpapers[currentTheme.index] = chatWallpaper
|
||||
themeSpecificChatWallpapers[currentTheme.index] = theme.chat.defaultWallpaper
|
||||
}
|
||||
|
||||
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: strongSelf.currentTheme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificBubbleColors: current.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.dismiss()
|
||||
@ -98,11 +107,59 @@ final class ThemeAccentColorController: ViewController {
|
||||
strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: theme, presentationStrings: strongSelf.presentationData.strings))
|
||||
}
|
||||
}
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
private func updateStrings() {
|
||||
|
||||
let _ = (self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings])
|
||||
|> deliverOnMainQueue).start(next: { [weak self] sharedData in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
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 accentColor = settings.themeSpecificAccentColors[themeReference.index]?.color ?? defaultDayAccentColor
|
||||
let wallpaper: TelegramWallpaper
|
||||
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
|
||||
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)))
|
||||
} else {
|
||||
backgroundColors = nil
|
||||
}
|
||||
|
||||
let messageColors: (UIColor, UIColor?)?
|
||||
if let bubbleColors = settings.themeSpecificBubbleColors[themeReference.index] {
|
||||
if let bottomColor = bubbleColors.optionalColor {
|
||||
messageColors = (UIColor(rgb: UInt32(bitPattern: bubbleColors.color)), UIColor(rgb: UInt32(bitPattern: bottomColor)))
|
||||
} else {
|
||||
messageColors = (UIColor(rgb: UInt32(bitPattern: bubbleColors.color)), nil)
|
||||
}
|
||||
} else {
|
||||
messageColors = nil
|
||||
}
|
||||
|
||||
let initialState = ThemeColorState(section: strongSelf.section, accentColor: accentColor, backgroundColors: backgroundColors, messagesColors: messageColors)
|
||||
|
||||
strongSelf.controllerNode.updateState({ _ in
|
||||
return initialState
|
||||
}, animated: false)
|
||||
strongSelf._ready.set(.single(true))
|
||||
})
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
|
@ -25,6 +25,33 @@ private func generateMaskImage(color: UIColor) -> UIImage? {
|
||||
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: 80.0), options: CGGradientDrawingOptions())
|
||||
})
|
||||
}
|
||||
|
||||
enum ThemeColorSection: Int {
|
||||
case accent
|
||||
case background
|
||||
case messages
|
||||
}
|
||||
|
||||
struct ThemeColorState {
|
||||
fileprivate var section: ThemeColorSection?
|
||||
var accentColor: UIColor
|
||||
var backgroundColors: (UIColor, UIColor?)?
|
||||
var messagesColors: (UIColor, UIColor?)?
|
||||
|
||||
init() {
|
||||
self.section = nil
|
||||
self.accentColor = .clear
|
||||
self.backgroundColors = nil
|
||||
self.messagesColors = nil
|
||||
}
|
||||
|
||||
init(section: ThemeColorSection, accentColor: UIColor, backgroundColors: (UIColor, UIColor?)?, messagesColors: (UIColor, UIColor?)?) {
|
||||
self.section = section
|
||||
self.accentColor = accentColor
|
||||
self.backgroundColors = backgroundColors
|
||||
self.messagesColors = messagesColors
|
||||
}
|
||||
}
|
||||
|
||||
final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private let context: AccountContext
|
||||
@ -32,6 +59,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
private let currentTheme: PresentationThemeReference
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private var state: ThemeColorState
|
||||
|
||||
private let referenceTimestamp: Int32
|
||||
|
||||
private let scrollNode: ASScrollNode
|
||||
@ -51,20 +80,20 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
private var validLayout: (ContainerViewLayout, CGFloat, CGFloat)?
|
||||
|
||||
private var serviceColorDisposable: Disposable?
|
||||
private var colorDisposable: Disposable?
|
||||
private let colorValue = ValuePromise<UIColor>(ignoreRepeated: true)
|
||||
private var colorsDisposable: Disposable?
|
||||
private let colors = Promise<(UIColor, (UIColor, UIColor?)?, (UIColor, UIColor?)?)>()
|
||||
|
||||
private let themePromise = Promise<PresentationTheme>()
|
||||
|
||||
var themeUpdated: ((PresentationTheme) -> Void)?
|
||||
var color: UInt32 {
|
||||
return self.colorPanelNode.color.rgb
|
||||
}
|
||||
|
||||
init(context: AccountContext, currentTheme: PresentationThemeReference, color: UIColor, theme: PresentationTheme, dismiss: @escaping () -> Void, apply: @escaping () -> Void) {
|
||||
init(context: AccountContext, currentTheme: PresentationThemeReference, theme: PresentationTheme, dismiss: @escaping () -> Void, apply: @escaping (ThemeColorState) -> Void) {
|
||||
self.context = context
|
||||
self.currentTheme = currentTheme
|
||||
self.state = ThemeColorState()
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.theme = theme
|
||||
self.colorValue.set(color)
|
||||
|
||||
let calendar = Calendar(identifier: .gregorian)
|
||||
var components = calendar.dateComponents(Set([.era, .year, .month, .day, .hour, .minute, .second]), from: Date())
|
||||
@ -76,21 +105,20 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
self.scrollNode = ASScrollNode()
|
||||
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: self.theme.chatList.unreadBadgeActiveBackgroundColor, inactiveDotColor: self.presentationData.theme.list.pageIndicatorInactiveColor)
|
||||
self.pageControlNode = PageControlNode(dotSpacing: 7.0, dotColor: .white, inactiveDotColor: UIColor.white.withAlphaComponent(0.4))
|
||||
|
||||
self.chatListBackgroundNode = ASDisplayNode()
|
||||
self.chatBackgroundNode = WallpaperBackgroundNode()
|
||||
self.chatBackgroundNode.displaysAsynchronously = false
|
||||
if case .color = self.presentationData.chatWallpaper {
|
||||
} else {
|
||||
self.chatBackgroundNode.image = chatControllerBackgroundImage(theme: theme, wallpaper: self.presentationData.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
|
||||
self.chatBackgroundNode.image = chatControllerBackgroundImage(theme: theme, wallpaper: self.presentationData.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: false)
|
||||
self.chatBackgroundNode.motionEnabled = self.presentationData.chatWallpaper.settings?.motion ?? false
|
||||
}
|
||||
|
||||
self.colorPanelNode = WallpaperColorPanelNode(theme: self.theme, strings: self.presentationData.strings)
|
||||
self.colorPanelNode.color = color
|
||||
self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.theme, strings: self.presentationData.strings)
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
@ -105,13 +133,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
})
|
||||
|
||||
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
|
||||
self.chatListBackgroundNode.backgroundColor = self.presentationData.theme.chatList.backgroundColor
|
||||
|
||||
if case let .color(value) = self.presentationData.theme.chat.defaultWallpaper {
|
||||
self.chatBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
|
||||
}
|
||||
|
||||
self.pageControlNode.isUserInteractionEnabled = false
|
||||
self.pageControlNode.pagesCount = 2
|
||||
|
||||
@ -125,67 +148,100 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
self.scrollNode.addSubnode(self.chatListBackgroundNode)
|
||||
self.scrollNode.addSubnode(self.chatBackgroundNode)
|
||||
|
||||
self.colorPanelNode.colorChanged = { [weak self] color, ended in
|
||||
if let strongSelf = self {
|
||||
strongSelf.colorValue.set(color)
|
||||
self.colorPanelNode.colorsChanged = { [weak self] firstColor, secondColor, _ in
|
||||
if let strongSelf = self, let section = strongSelf.state.section {
|
||||
switch section {
|
||||
case .accent:
|
||||
strongSelf.updateState({ current in
|
||||
var updated = current
|
||||
updated.accentColor = firstColor
|
||||
return updated
|
||||
})
|
||||
case .background:
|
||||
strongSelf.updateState({ current in
|
||||
var updated = current
|
||||
updated.backgroundColors = (firstColor, secondColor)
|
||||
return updated
|
||||
})
|
||||
case .messages:
|
||||
strongSelf.updateState({ current in
|
||||
var updated = current
|
||||
updated.messagesColors = (firstColor, secondColor)
|
||||
return updated
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.toolbarNode.cancel = {
|
||||
dismiss()
|
||||
}
|
||||
self.toolbarNode.done = {
|
||||
apply()
|
||||
self.toolbarNode.done = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
apply(strongSelf.state)
|
||||
}
|
||||
}
|
||||
|
||||
self.colorDisposable = (self.colorValue.get()
|
||||
self.colorsDisposable = (self.colors.get()
|
||||
|> deliverOn(Queue.concurrentDefaultQueue())
|
||||
|> map { color -> PresentationTheme in
|
||||
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, preview: true) ?? defaultPresentationTheme
|
||||
|> 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
|
||||
|
||||
let wallpaper = context.sharedContext.currentPresentationData.with { $0 }.chatWallpaper
|
||||
var wallpaper = context.sharedContext.currentPresentationData.with { $0 }.chatWallpaper
|
||||
if let backgroundColors = 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))
|
||||
}
|
||||
}
|
||||
let _ = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: theme, wallpaper: wallpaper, gradientBubbles: context.sharedContext.immediateExperimentalUISettings.gradientBubbles)
|
||||
|
||||
return theme
|
||||
return (theme, wallpaper)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] theme in
|
||||
if let strongSelf = self {
|
||||
strongSelf.theme = theme
|
||||
strongSelf.themeUpdated?(theme)
|
||||
|
||||
strongSelf.colorPanelNode.updateTheme(theme)
|
||||
strongSelf.toolbarNode.updateThemeAndStrings(theme: theme, strings: strongSelf.presentationData.strings)
|
||||
strongSelf.maskNode.image = generateMaskImage(color: theme.chatList.backgroundColor)
|
||||
|
||||
if case let .color(value) = theme.chat.defaultWallpaper {
|
||||
strongSelf.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
|
||||
strongSelf.chatListBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
|
||||
strongSelf.chatBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
|
||||
}
|
||||
|
||||
if let (layout, navigationBarHeight, messagesBottomInset) = strongSelf.validLayout {
|
||||
strongSelf.pageControlNode.dotColor = theme.chatList.unreadBadgeActiveBackgroundColor
|
||||
strongSelf.pageControlNode.inactiveDotColor = theme.list.pageIndicatorInactiveColor
|
||||
strongSelf.updateChatsLayout(layout: layout, topInset: navigationBarHeight, transition: .immediate)
|
||||
strongSelf.updateMessagesLayout(layout: layout, bottomInset: messagesBottomInset, transition: .immediate)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] theme, wallpaper in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.theme = theme
|
||||
strongSelf.themeUpdated?(theme)
|
||||
strongSelf.themePromise.set(.single(theme))
|
||||
|
||||
strongSelf.colorPanelNode.updateTheme(theme)
|
||||
strongSelf.toolbarNode.updateThemeAndStrings(theme: theme, strings: strongSelf.presentationData.strings)
|
||||
strongSelf.maskNode.image = generateMaskImage(color: theme.chatList.backgroundColor)
|
||||
|
||||
if case let .color(value) = theme.chat.defaultWallpaper {
|
||||
strongSelf.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
|
||||
strongSelf.chatListBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
|
||||
strongSelf.chatBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
||||
self.serviceColorDisposable = (chatServiceBackgroundColor(wallpaper: self.presentationData.chatWallpaper, mediaBox: context.account.postbox.mediaBox)
|
||||
self.serviceColorDisposable = (self.themePromise.get()
|
||||
|> mapToSignal { theme -> Signal<UIColor, NoError> in
|
||||
return chatServiceBackgroundColor(wallpaper: self.presentationData.chatWallpaper, mediaBox: context.account.postbox.mediaBox)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] color in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.presentationData.chatWallpaper.hasWallpaper {
|
||||
//if strongSelf.presentationData.chatWallpaper.hasWallpaper {
|
||||
strongSelf.pageControlBackgroundNode.backgroundColor = color
|
||||
} else {
|
||||
strongSelf.pageControlBackgroundNode.backgroundColor = .clear
|
||||
}
|
||||
//} else {
|
||||
// strongSelf.pageControlBackgroundNode.backgroundColor = .clear
|
||||
//}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.colorDisposable?.dispose()
|
||||
self.colorsDisposable?.dispose()
|
||||
self.serviceColorDisposable?.dispose()
|
||||
}
|
||||
|
||||
@ -207,6 +263,48 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
}
|
||||
}
|
||||
|
||||
func updateState(_ f: (ThemeColorState) -> ThemeColorState, animated: Bool = false) {
|
||||
let previousState = self.state
|
||||
self.state = f(self.state)
|
||||
|
||||
let colorsChanged = previousState.accentColor != self.state.accentColor
|
||||
if colorsChanged {
|
||||
self.colors.set(.single((self.state.accentColor, self.state.backgroundColors, self.state.messagesColors)))
|
||||
}
|
||||
|
||||
let sectionChanged = previousState.section != self.state.section
|
||||
if sectionChanged, let section = self.state.section {
|
||||
let firstColor: UIColor
|
||||
let secondColor: UIColor?
|
||||
switch section {
|
||||
case .accent:
|
||||
firstColor = self.state.accentColor ?? .white
|
||||
secondColor = nil
|
||||
case .background:
|
||||
firstColor = self.state.backgroundColors?.0 ?? .white
|
||||
secondColor = self.state.backgroundColors?.1 ?? .white
|
||||
case .messages:
|
||||
firstColor = self.state.messagesColors?.0 ?? .blue
|
||||
secondColor = self.state.messagesColors?.1 ?? .blue
|
||||
}
|
||||
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
|
||||
}, animated: animated)
|
||||
if let (layout, navigationBarHeight, _) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateSection(_ section: ThemeColorSection) {
|
||||
self.updateState({ current in
|
||||
var updated = current
|
||||
updated.section = section
|
||||
return updated
|
||||
}, animated: true)
|
||||
}
|
||||
|
||||
private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
var items: [ChatListItem] = []
|
||||
|
||||
@ -361,6 +459,21 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
|
||||
self.scrollNode.view.contentSize = CGSize(width: bounds.width * 2.0, height: bounds.height)
|
||||
|
||||
var pageControlAlpha: CGFloat = 1.0
|
||||
if self.state.section != .accent {
|
||||
pageControlAlpha = 0.0
|
||||
}
|
||||
self.scrollNode.view.isScrollEnabled = pageControlAlpha > 0.0
|
||||
|
||||
var messagesTransition = transition
|
||||
if !self.scrollNode.view.isScrollEnabled && self.scrollNode.view.contentOffset.x > 0.0 {
|
||||
var bounds = self.scrollNode.bounds
|
||||
bounds.origin.x = 0.0
|
||||
transition.updateBounds(node: scrollNode, bounds: bounds)
|
||||
messagesTransition = .immediate
|
||||
self.pageControlNode.setPage(0.0)
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.toolbarNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight), size: CGSize(width: layout.size.width, height: 49.0 + layout.intrinsicInsets.bottom)))
|
||||
self.toolbarNode.updateLayout(size: CGSize(width: layout.size.width, height: 49.0), layout: layout, transition: transition)
|
||||
|
||||
@ -373,17 +486,22 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
transition.updateFrame(node: self.colorPanelNode, frame: colorPanelFrame)
|
||||
self.colorPanelNode.updateLayout(size: colorPanelFrame.size, keyboardHeight: layout.inputHeight ?? 0.0, transition: transition)
|
||||
|
||||
let messagesBottomInset = bottomInset + 36.0
|
||||
var messagesBottomInset = bottomInset
|
||||
if pageControlAlpha > 0.0 {
|
||||
messagesBottomInset += 37.0
|
||||
}
|
||||
self.updateChatsLayout(layout: layout, topInset: navigationBarHeight, transition: transition)
|
||||
self.updateMessagesLayout(layout: layout, bottomInset: messagesBottomInset, transition: transition)
|
||||
self.updateMessagesLayout(layout: layout, bottomInset: messagesBottomInset, transition: messagesTransition)
|
||||
|
||||
self.validLayout = (layout, navigationBarHeight, messagesBottomInset)
|
||||
|
||||
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 - 27.0), size: pageControlSize)
|
||||
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 - 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.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))
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SegmentedControlNode
|
||||
import TelegramPresentationData
|
||||
|
||||
final class ThemeColorSegmentedTitleView: UIView {
|
||||
private let segmentedControlNode: SegmentedControlNode
|
||||
|
||||
var theme: PresentationTheme {
|
||||
didSet {
|
||||
self.segmentedControlNode.updateTheme(SegmentedControlTheme(theme: self.theme))
|
||||
}
|
||||
}
|
||||
|
||||
var index: Int {
|
||||
get {
|
||||
return self.segmentedControlNode.selectedIndex
|
||||
}
|
||||
set {
|
||||
self.segmentedControlNode.selectedIndex = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var sectionUpdated: ((ThemeColorSection) -> Void)?
|
||||
var shouldUpdateSection: ((ThemeColorSection) -> Void)?
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, selectedSection: ThemeColorSection) {
|
||||
self.theme = theme
|
||||
|
||||
let sections = [strings.Theme_Colors_Accent, strings.Theme_Colors_Background, strings.Theme_Colors_Messages]
|
||||
self.segmentedControlNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: theme), items: sections.map { SegmentedControlItem(title: $0) }, selectedIndex: selectedSection.rawValue)
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.segmentedControlNode.selectedIndexChanged = { [weak self] index in
|
||||
if let section = ThemeColorSection(rawValue: index) {
|
||||
self?.sectionUpdated?(section)
|
||||
}
|
||||
}
|
||||
|
||||
self.addSubnode(self.segmentedControlNode)
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
let size = self.bounds.size
|
||||
let controlSize = self.segmentedControlNode.updateLayout(.stretchToFill(width: size.width + 20.0), transition: .immediate)
|
||||
self.segmentedControlNode.frame = CGRect(origin: CGPoint(x: floor((size.width - controlSize.width) / 2.0), y: floor((size.height - controlSize.height) / 2.0)), size: controlSize)
|
||||
}
|
||||
}
|
@ -181,13 +181,9 @@ final class ThemeGridController: ViewController {
|
||||
if wallpaper == strongSelf.presentationData.chatWallpaper {
|
||||
let presentationData = strongSelf.presentationData
|
||||
let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in
|
||||
var fallbackWallpaper = presentationData.theme.chat.defaultWallpaper
|
||||
if case let .cloud(info) = current.theme, let resolvedWallpaper = info.resolvedWallpaper {
|
||||
fallbackWallpaper = resolvedWallpaper
|
||||
}
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
themeSpecificChatWallpapers[current.theme.index] = nil
|
||||
return PresentationThemeSettings(chatWallpaper: fallbackWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
})).start()
|
||||
break
|
||||
}
|
||||
@ -245,13 +241,9 @@ final class ThemeGridController: ViewController {
|
||||
} else {
|
||||
current = PresentationThemeSettings.defaultSettings
|
||||
}
|
||||
var fallbackWallpaper = presentationData.theme.chat.defaultWallpaper
|
||||
if case let .cloud(info) = current.theme, let resolvedWallpaper = info.resolvedWallpaper {
|
||||
fallbackWallpaper = resolvedWallpaper
|
||||
}
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
themeSpecificChatWallpapers[current.theme.index] = nil
|
||||
return PresentationThemeSettings(chatWallpaper: fallbackWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: [:], fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: [:], fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
})
|
||||
}).start()
|
||||
|
||||
|
@ -30,6 +30,12 @@ private func areWallpapersEqual(_ lhs: TelegramWallpaper, _ rhs: TelegramWallpap
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .gradient(topColor, bottomColor):
|
||||
if case .gradient(topColor, bottomColor) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .image(representations, _):
|
||||
if case .image(representations, _) = rhs {
|
||||
return true
|
||||
@ -103,14 +109,18 @@ private struct ThemeGridControllerEntry: Comparable, Identifiable {
|
||||
return 0
|
||||
case let .color(color):
|
||||
return (Int64(1) << 32) | Int64(bitPattern: UInt64(UInt32(bitPattern: color)))
|
||||
case let .gradient(topColor, bottomColor):
|
||||
var hash: UInt32 = UInt32(bitPattern: topColor)
|
||||
hash = hash &* 31 &+ UInt32(bitPattern: bottomColor)
|
||||
return (Int64(2) << 32) | Int64(hash)
|
||||
case let .file(id, _, _, _, _, _, _, _, settings):
|
||||
var hash: Int = id.hashValue
|
||||
hash = hash &* 31 &+ (settings.color?.hashValue ?? 0)
|
||||
hash = hash &* 31 &+ (settings.intensity?.hashValue ?? 0)
|
||||
return (Int64(2) << 32) | Int64(hash)
|
||||
return (Int64(3) << 32) | Int64(hash)
|
||||
case let .image(representations, _):
|
||||
if let largest = largestImageRepresentation(representations) {
|
||||
return (Int64(3) << 32) | Int64(largest.resource.id.hashValue)
|
||||
return (Int64(4) << 32) | Int64(largest.resource.id.hashValue)
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
|
@ -289,17 +289,28 @@ public final class ThemePreviewController: ViewController {
|
||||
return .single(theme)
|
||||
}
|
||||
}
|
||||
|> mapToSignal { theme -> Signal<Void, NoError> in
|
||||
if case let .cloud(info) = theme {
|
||||
|> mapToSignal { updatedTheme -> Signal<Void, NoError> in
|
||||
if case let .cloud(info) = updatedTheme {
|
||||
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: info.theme).start()
|
||||
let _ = saveThemeInteractively(account: context.account, accountManager: context.sharedContext.accountManager, theme: info.theme).start()
|
||||
}
|
||||
return context.sharedContext.accountManager.transaction { transaction -> Void in
|
||||
let autoNightModeTriggered = context.sharedContext.currentPresentationData.with { $0 }.autoNightModeTriggered
|
||||
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
|
||||
let current = entry as? PresentationThemeSettings ?? PresentationThemeSettings.defaultSettings
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
themeSpecificChatWallpapers[theme.index] = nil
|
||||
return PresentationThemeSettings(chatWallpaper: resolvedWallpaper ?? previewTheme.chat.defaultWallpaper, theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
themeSpecificChatWallpapers[updatedTheme.index] = nil
|
||||
|
||||
var theme = current.theme
|
||||
var automaticThemeSwitchSetting = current.automaticThemeSwitchSetting
|
||||
if autoNightModeTriggered {
|
||||
automaticThemeSwitchSetting.theme = updatedTheme
|
||||
} else {
|
||||
theme = updatedTheme
|
||||
}
|
||||
|
||||
return PresentationThemeSettings(theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -436,7 +436,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
selectThemeImpl?(theme)
|
||||
}, selectFontSize: { size in
|
||||
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: size, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: size, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
}).start()
|
||||
}, openWallpaperSettings: {
|
||||
pushControllerImpl?(ThemeGridController(context: context))
|
||||
@ -452,30 +452,29 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
return current
|
||||
}
|
||||
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
var themeSpecificAccentColors = current.themeSpecificAccentColors
|
||||
themeSpecificAccentColors[currentTheme.index] = color
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
var chatWallpaper = current.chatWallpaper
|
||||
|
||||
if let wallpaper = current.themeSpecificChatWallpapers[currentTheme.index], wallpaper.hasWallpaper {
|
||||
} else {
|
||||
chatWallpaper = theme.chat.defaultWallpaper
|
||||
themeSpecificChatWallpapers[currentTheme.index] = chatWallpaper
|
||||
themeSpecificChatWallpapers[currentTheme.index] = theme.chat.defaultWallpaper
|
||||
}
|
||||
|
||||
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
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, currentColor: currentColor?.color)
|
||||
let controller = ThemeAccentColorController(context: context, currentTheme: themeReference, section: .accent)
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, openAutoNightTheme: {
|
||||
pushControllerImpl?(themeAutoNightSettingsController(context: context))
|
||||
}, toggleLargeEmoji: { largeEmoji in
|
||||
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: largeEmoji, disableAnimations: current.disableAnimations)
|
||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: largeEmoji, disableAnimations: current.disableAnimations)
|
||||
}).start()
|
||||
}, disableAnimations: { value in
|
||||
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: value)
|
||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: value)
|
||||
}).start()
|
||||
}, selectAppIcon: { name in
|
||||
currentAppIconName.set(name)
|
||||
@ -584,7 +583,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
|
||||
let theme = presentationData.theme
|
||||
let accentColor = settings.themeSpecificAccentColors[themeReference.index]?.color
|
||||
let wallpaper = settings.themeSpecificChatWallpapers[themeReference.index] ?? settings.chatWallpaper
|
||||
let wallpaper = presentationData.chatWallpaper
|
||||
|
||||
let rightNavigationButton = ItemListNavigationButton(content: .icon(.action), style: .regular, enabled: true, action: {
|
||||
moreImpl?()
|
||||
@ -670,16 +669,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
} else {
|
||||
theme = updatedTheme
|
||||
}
|
||||
|
||||
let chatWallpaper: TelegramWallpaper
|
||||
if let themeSpecificWallpaper = current.themeSpecificChatWallpapers[updatedTheme.index] {
|
||||
chatWallpaper = themeSpecificWallpaper
|
||||
} else {
|
||||
let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: updatedTheme, accentColor: current.themeSpecificAccentColors[updatedTheme.index]?.color, serviceBackgroundColor: .black, baseColor: nil) ?? defaultPresentationTheme
|
||||
chatWallpaper = resolvedWallpaper ?? presentationTheme.chat.defaultWallpaper
|
||||
}
|
||||
|
||||
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
|
||||
return PresentationThemeSettings(theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
})
|
||||
})
|
||||
}).start()
|
||||
|
@ -31,74 +31,83 @@ private func textInputBackgroundImage(fieldColor: UIColor, strokeColor: UIColor,
|
||||
}
|
||||
}
|
||||
|
||||
final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate {
|
||||
private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
||||
private var theme: PresentationTheme
|
||||
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topSeparatorNode: ASDisplayNode
|
||||
private let bottomSeparatorNode: ASDisplayNode
|
||||
private let swatchNode: ASDisplayNode
|
||||
private let removeButton: HighlightableButtonNode
|
||||
private let textBackgroundNode: ASImageNode
|
||||
private let textFieldNode: TextFieldNode
|
||||
private let selectionNode: ASDisplayNode
|
||||
let textFieldNode: TextFieldNode
|
||||
private let measureNode: ImmediateTextNode
|
||||
private let prefixNode: ASTextNode
|
||||
private let doneButton: HighlightableButtonNode
|
||||
private let colorPickerNode: WallpaperColorPickerNode
|
||||
|
||||
var previousColor: UIColor?
|
||||
var color: UIColor {
|
||||
get {
|
||||
return self.colorPickerNode.color
|
||||
}
|
||||
set {
|
||||
self.setColor(newValue)
|
||||
private var validLayout: CGSize?
|
||||
|
||||
private var previousColor: UIColor?
|
||||
|
||||
var colorChanged: ((UIColor, Bool) -> Void)?
|
||||
var colorRemoved: (() -> Void)?
|
||||
var colorSelected: (() -> Void)?
|
||||
|
||||
var color: UIColor = .white {
|
||||
didSet {
|
||||
self.setColor(self.color, update: false)
|
||||
}
|
||||
}
|
||||
|
||||
var colorChanged: ((UIColor, Bool) -> Void)?
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings) {
|
||||
|
||||
var isRemovable: Bool = false {
|
||||
didSet {
|
||||
self.removeButton.isUserInteractionEnabled = self.isRemovable
|
||||
}
|
||||
}
|
||||
|
||||
var isSelected: Bool = false {
|
||||
didSet {
|
||||
self.selectionNode.isHidden = !self.isSelected
|
||||
if !self.isSelected {
|
||||
self.textFieldNode.textField.resignFirstResponder()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(theme: PresentationTheme) {
|
||||
self.theme = theme
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.backgroundColor = theme.chat.inputPanel.panelBackgroundColor
|
||||
|
||||
self.topSeparatorNode = ASDisplayNode()
|
||||
self.topSeparatorNode.backgroundColor = theme.chat.inputPanel.panelSeparatorColor
|
||||
self.bottomSeparatorNode = ASDisplayNode()
|
||||
self.bottomSeparatorNode.backgroundColor = theme.chat.inputPanel.panelSeparatorColor
|
||||
|
||||
self.textBackgroundNode = ASImageNode()
|
||||
self.textBackgroundNode.image = textInputBackgroundImage(fieldColor: theme.chat.inputPanel.inputBackgroundColor, strokeColor: theme.chat.inputPanel.inputStrokeColor, diameter: 33.0)
|
||||
self.textBackgroundNode.displayWithoutProcessing = true
|
||||
self.textBackgroundNode.displaysAsynchronously = false
|
||||
|
||||
self.selectionNode = ASDisplayNode()
|
||||
self.selectionNode.backgroundColor = theme.chat.inputPanel.panelControlAccentColor.withAlphaComponent(0.2)
|
||||
self.selectionNode.cornerRadius = 3.0
|
||||
self.selectionNode.isUserInteractionEnabled = false
|
||||
|
||||
self.textFieldNode = TextFieldNode()
|
||||
self.measureNode = ImmediateTextNode()
|
||||
|
||||
self.prefixNode = ASTextNode()
|
||||
self.prefixNode.attributedText = NSAttributedString(string: "#", font: Font.regular(17.0), textColor: self.theme.chat.inputPanel.inputTextColor)
|
||||
|
||||
self.doneButton = HighlightableButtonNode()
|
||||
self.doneButton.setImage(PresentationResourcesChat.chatInputPanelApplyButtonImage(theme), for: .normal)
|
||||
|
||||
self.colorPickerNode = WallpaperColorPickerNode(strings: strings)
|
||||
|
||||
self.swatchNode = ASDisplayNode()
|
||||
self.swatchNode.cornerRadius = 10.5
|
||||
|
||||
self.removeButton = HighlightableButtonNode()
|
||||
self.removeButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeColorRemoveIcon"), color: theme.chat.inputPanel.inputControlColor), for: .normal)
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.topSeparatorNode)
|
||||
self.addSubnode(self.bottomSeparatorNode)
|
||||
self.addSubnode(self.textBackgroundNode)
|
||||
self.addSubnode(self.selectionNode)
|
||||
self.addSubnode(self.textFieldNode)
|
||||
self.addSubnode(self.prefixNode)
|
||||
self.addSubnode(self.doneButton)
|
||||
self.addSubnode(self.colorPickerNode)
|
||||
self.addSubnode(self.swatchNode)
|
||||
self.addSubnode(self.removeButton)
|
||||
|
||||
self.colorPickerNode.colorChanged = { [weak self] color in
|
||||
self?.setColor(color, updatePicker: false, ended: false)
|
||||
}
|
||||
self.colorPickerNode.colorChangeEnded = { [weak self] color in
|
||||
self?.setColor(color, updatePicker: false, ended: true)
|
||||
}
|
||||
self.removeButton.addTarget(self, action: #selector(self.removePressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
@ -117,53 +126,41 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate {
|
||||
|
||||
func updateTheme(_ theme: PresentationTheme) {
|
||||
self.theme = theme
|
||||
self.backgroundNode.backgroundColor = self.theme.chat.inputPanel.panelBackgroundColor
|
||||
self.topSeparatorNode.backgroundColor = self.theme.chat.inputPanel.panelSeparatorColor
|
||||
self.bottomSeparatorNode.backgroundColor = self.theme.chat.inputPanel.panelSeparatorColor
|
||||
|
||||
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.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance
|
||||
self.textFieldNode.textField.tintColor = self.theme.list.itemAccentColor
|
||||
|
||||
self.selectionNode.backgroundColor = theme.chat.inputPanel.panelControlAccentColor.withAlphaComponent(0.2)
|
||||
}
|
||||
|
||||
private func setColor(_ color: UIColor, updatePicker: Bool = true, ended: Bool = true) {
|
||||
self.textFieldNode.textField.text = color.hexString.uppercased()
|
||||
if updatePicker {
|
||||
self.colorPickerNode.color = color
|
||||
func setColor(_ color: UIColor, update: Bool = true, ended: Bool = true) {
|
||||
let text = color.hexString.uppercased()
|
||||
self.textFieldNode.textField.text = text
|
||||
if let size = self.validLayout {
|
||||
self.updateSelectionLayout(size: size, transition: .immediate)
|
||||
}
|
||||
self.colorChanged?(color, ended)
|
||||
if update {
|
||||
self.colorChanged?(color, ended)
|
||||
}
|
||||
self.swatchNode.backgroundColor = color
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, keyboardHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
let separatorHeight = UIScreenPixel
|
||||
let topPanelHeight: CGFloat = 47.0
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: topPanelHeight))
|
||||
transition.updateFrame(node: self.topSeparatorNode, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: separatorHeight))
|
||||
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 = 5.0
|
||||
let rightInset: CGFloat = 5.0
|
||||
|
||||
transition.updateFrame(node: self.textBackgroundNode, frame: CGRect(x: leftInset, y: (topPanelHeight - fieldHeight) / 2.0, width: size.width - leftInset - rightInset, height: fieldHeight))
|
||||
transition.updateFrame(node: self.textFieldNode, frame: CGRect(x: leftInset + 24.0, y: (topPanelHeight - fieldHeight) / 2.0 + 1.0, width: size.width - leftInset - rightInset - 36.0, height: fieldHeight - 2.0))
|
||||
|
||||
let prefixSize = self.prefixNode.measure(CGSize(width: size.width, height: fieldHeight))
|
||||
transition.updateFrame(node: self.prefixNode, frame: CGRect(origin: CGPoint(x: leftInset + 13.0, y: 12.0 + UIScreenPixel), size: prefixSize))
|
||||
transition.updateFrame(node: self.doneButton, frame: CGRect(x: 0.0, y: size.width - rightInset + buttonSpacing, width: topPanelHeight, height: topPanelHeight))
|
||||
|
||||
let colorPickerSize = CGSize(width: size.width, height: size.height - topPanelHeight - separatorHeight)
|
||||
transition.updateFrame(node: self.colorPickerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight + separatorHeight), size: colorPickerSize))
|
||||
self.colorPickerNode.updateLayout(size: colorPickerSize, transition: transition)
|
||||
@objc private func removePressed() {
|
||||
self.colorRemoved?()
|
||||
}
|
||||
|
||||
|
||||
@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()
|
||||
|
||||
if let size = self.validLayout {
|
||||
self.updateSelectionLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -172,6 +169,10 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate {
|
||||
if let text = sender.text, text.count == 6, let color = UIColor(hexString: text) {
|
||||
self.setColor(color)
|
||||
}
|
||||
|
||||
if let size = self.validLayout {
|
||||
self.updateSelectionLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
@ -180,8 +181,13 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate {
|
||||
}
|
||||
|
||||
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
|
||||
self.previousColor = self.color
|
||||
return true
|
||||
if self.isSelected {
|
||||
self.previousColor = self.color
|
||||
return true
|
||||
} else {
|
||||
self.colorSelected?()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@objc func textFieldDidEndEditing(_ textField: UITextField) {
|
||||
@ -191,4 +197,314 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate {
|
||||
self.setColor(self.previousColor ?? .black)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateSelectionLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.measureNode.attributedText = NSAttributedString(string: self.textFieldNode.textField.text ?? "", font: self.textFieldNode.textField.font)
|
||||
let size = self.measureNode.updateLayout(size)
|
||||
transition.updateFrame(node: self.selectionNode, frame: CGRect(x: 47.0, y: 6.0, width: max(45.0, size.width), height: 20.0))
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = size
|
||||
|
||||
transition.updateFrame(node: self.swatchNode, frame: CGRect(origin: CGPoint(x: 6.0, y: 6.0), size: CGSize(width: 21.0, height: 21.0)))
|
||||
|
||||
transition.updateFrame(node: self.textBackgroundNode, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
|
||||
transition.updateFrame(node: self.textFieldNode, frame: CGRect(x: 47.0, y: 1.0, width: size.width - 61.0, height: size.height - 2.0))
|
||||
|
||||
self.updateSelectionLayout(size: size, transition: transition)
|
||||
|
||||
let prefixSize = self.prefixNode.measure(size)
|
||||
transition.updateFrame(node: self.prefixNode, frame: CGRect(origin: CGPoint(x: 37.0 - UIScreenPixel, y: 6.0), size: prefixSize))
|
||||
|
||||
let removeSize = CGSize(width: 33.0, height: 33.0)
|
||||
transition.updateFrame(node: self.removeButton, frame: CGRect(origin: CGPoint(x: size.width - removeSize.width, y: 0.0), size: removeSize))
|
||||
transition.updateAlpha(node: self.removeButton, alpha: self.isRemovable ? 1.0 : 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
enum WallpaperColorPanelNodeSelectionState {
|
||||
case none
|
||||
case first
|
||||
case second
|
||||
}
|
||||
|
||||
struct WallpaperColorPanelNodeState {
|
||||
var selection: WallpaperColorPanelNodeSelectionState
|
||||
var firstColor: UIColor
|
||||
var firstColorRemovable: Bool
|
||||
var secondColor: UIColor?
|
||||
var secondColorAvailable: Bool
|
||||
}
|
||||
|
||||
final class WallpaperColorPanelNode: ASDisplayNode {
|
||||
private var theme: PresentationTheme
|
||||
|
||||
private var state: WallpaperColorPanelNodeState
|
||||
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topSeparatorNode: ASDisplayNode
|
||||
private let bottomSeparatorNode: ASDisplayNode
|
||||
private let firstColorFieldNode: ColorInputFieldNode
|
||||
private let secondColorFieldNode: ColorInputFieldNode
|
||||
private let swapButton: HighlightableButtonNode
|
||||
private let addButton: HighlightableButtonNode
|
||||
private let doneButton: HighlightableButtonNode
|
||||
private let colorPickerNode: WallpaperColorPickerNode
|
||||
|
||||
var colorsChanged: ((UIColor, UIColor?, Bool) -> Void)?
|
||||
|
||||
private var validLayout: (CGSize, CGFloat)?
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings) {
|
||||
self.theme = theme
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.backgroundColor = theme.chat.inputPanel.panelBackgroundColor
|
||||
|
||||
self.topSeparatorNode = ASDisplayNode()
|
||||
self.topSeparatorNode.backgroundColor = theme.chat.inputPanel.panelSeparatorColor
|
||||
self.bottomSeparatorNode = ASDisplayNode()
|
||||
self.bottomSeparatorNode.backgroundColor = theme.chat.inputPanel.panelSeparatorColor
|
||||
|
||||
self.doneButton = HighlightableButtonNode()
|
||||
self.doneButton.setImage(PresentationResourcesChat.chatInputPanelApplyButtonImage(theme), for: .normal)
|
||||
|
||||
self.colorPickerNode = WallpaperColorPickerNode(strings: strings)
|
||||
|
||||
self.swapButton = HighlightableButtonNode()
|
||||
self.swapButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeColorSwapIcon"), color: theme.chat.inputPanel.panelControlColor), for: .normal)
|
||||
self.addButton = HighlightableButtonNode()
|
||||
self.addButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeColorAddIcon"), color: theme.chat.inputPanel.panelControlColor), for: .normal)
|
||||
|
||||
self.firstColorFieldNode = ColorInputFieldNode(theme: theme)
|
||||
self.secondColorFieldNode = ColorInputFieldNode(theme: theme)
|
||||
|
||||
self.state = WallpaperColorPanelNodeState(selection: .first, firstColor: .white, firstColorRemovable: false, secondColor: nil, secondColorAvailable: true)
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.topSeparatorNode)
|
||||
self.addSubnode(self.bottomSeparatorNode)
|
||||
self.addSubnode(self.firstColorFieldNode)
|
||||
self.addSubnode(self.secondColorFieldNode)
|
||||
self.addSubnode(self.doneButton)
|
||||
self.addSubnode(self.colorPickerNode)
|
||||
|
||||
self.addSubnode(self.swapButton)
|
||||
self.addSubnode(self.addButton)
|
||||
|
||||
self.swapButton.addTarget(self, action: #selector(self.swapPressed), forControlEvents: .touchUpInside)
|
||||
self.addButton.addTarget(self, action: #selector(self.addPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.firstColorFieldNode.colorChanged = { [weak self] color, ended in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateState({ current in
|
||||
var updated = current
|
||||
updated.firstColor = color
|
||||
return updated
|
||||
})
|
||||
}
|
||||
}
|
||||
self.firstColorFieldNode.colorRemoved = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateState({ current in
|
||||
var updated = current
|
||||
updated.selection = .first
|
||||
updated.firstColor = updated.secondColor ?? updated.firstColor
|
||||
updated.secondColor = nil
|
||||
return updated
|
||||
})
|
||||
}
|
||||
}
|
||||
self.firstColorFieldNode.colorSelected = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateState({ current in
|
||||
var updated = current
|
||||
updated.selection = .first
|
||||
return updated
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
self.secondColorFieldNode.colorChanged = { [weak self] color, ended in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateState({ current in
|
||||
var updated = current
|
||||
updated.secondColor = color
|
||||
return updated
|
||||
})
|
||||
}
|
||||
}
|
||||
self.secondColorFieldNode.colorRemoved = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateState({ current in
|
||||
var updated = current
|
||||
updated.selection = .first
|
||||
updated.secondColor = nil
|
||||
return updated
|
||||
})
|
||||
}
|
||||
}
|
||||
self.secondColorFieldNode.colorSelected = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateState({ current in
|
||||
var updated = current
|
||||
updated.selection = .second
|
||||
return updated
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
self.colorPickerNode.colorChanged = { [weak self] color in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateState({ current in
|
||||
var updated = current
|
||||
switch strongSelf.state.selection {
|
||||
case .first:
|
||||
updated.firstColor = color
|
||||
case .second:
|
||||
updated.secondColor = color
|
||||
default:
|
||||
break
|
||||
}
|
||||
return updated
|
||||
}, updateLayout: false)
|
||||
}
|
||||
}
|
||||
self.colorPickerNode.colorChangeEnded = { [weak self] color in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateState({ current in
|
||||
var updated = current
|
||||
switch strongSelf.state.selection {
|
||||
case .first:
|
||||
updated.firstColor = color
|
||||
case .second:
|
||||
updated.secondColor = color
|
||||
default:
|
||||
break
|
||||
}
|
||||
return updated
|
||||
}, updateLayout: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateTheme(_ theme: PresentationTheme) {
|
||||
self.theme = theme
|
||||
self.backgroundNode.backgroundColor = self.theme.chat.inputPanel.panelBackgroundColor
|
||||
self.topSeparatorNode.backgroundColor = self.theme.chat.inputPanel.panelSeparatorColor
|
||||
self.bottomSeparatorNode.backgroundColor = self.theme.chat.inputPanel.panelSeparatorColor
|
||||
self.firstColorFieldNode.updateTheme(theme)
|
||||
self.secondColorFieldNode.updateTheme(theme)
|
||||
}
|
||||
|
||||
func updateState(_ f: (WallpaperColorPanelNodeState) -> WallpaperColorPanelNodeState, updateLayout: Bool = true, animated: Bool = true) {
|
||||
let firstColor = self.state.firstColor.rgb
|
||||
let secondColor = self.state.secondColor?.rgb
|
||||
self.state = f(self.state)
|
||||
|
||||
self.firstColorFieldNode.setColor(self.state.firstColor, update: false)
|
||||
if let secondColor = self.state.secondColor {
|
||||
self.secondColorFieldNode.setColor(secondColor, update: false)
|
||||
}
|
||||
|
||||
if updateLayout, let (size, keyboardHeight) = self.validLayout {
|
||||
switch self.state.selection {
|
||||
case .first:
|
||||
self.colorPickerNode.color = self.state.firstColor
|
||||
case .second:
|
||||
if let secondColor = self.state.secondColor {
|
||||
self.colorPickerNode.color = secondColor
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
self.updateLayout(size: size, keyboardHeight: keyboardHeight, 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)
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, keyboardHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, keyboardHeight)
|
||||
|
||||
let separatorHeight = UIScreenPixel
|
||||
let topPanelHeight: CGFloat = 47.0
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: topPanelHeight))
|
||||
transition.updateFrame(node: self.topSeparatorNode, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: separatorHeight))
|
||||
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
|
||||
let fieldSpacing: CGFloat = 45.0
|
||||
|
||||
let buttonSize = CGSize(width: 26.0, height: 26.0)
|
||||
let buttonOffset: CGFloat = (rightInsetWithButton - 13.0) / 2.0
|
||||
let swapButtonFrame = CGRect(origin: CGPoint(x: self.state.secondColor != nil ? floor((size.width - 26.0) / 2.0) : (self.state.secondColorAvailable ? size.width - rightInsetWithButton + floor((rightInsetWithButton - buttonSize.width) / 2.0) : size.width + buttonOffset), y: floor((topPanelHeight - buttonSize.height) / 2.0)), size: buttonSize)
|
||||
|
||||
transition.updateFrame(node: self.swapButton, frame: swapButtonFrame)
|
||||
transition.updateFrame(node: self.addButton, frame: swapButtonFrame)
|
||||
|
||||
let swapButtonAlpha: CGFloat
|
||||
let addButtonAlpha: CGFloat
|
||||
if let _ = self.state.secondColor {
|
||||
swapButtonAlpha = 1.0
|
||||
addButtonAlpha = 0.0
|
||||
} else {
|
||||
swapButtonAlpha = 0.0
|
||||
if self.state.secondColorAvailable {
|
||||
addButtonAlpha = 1.0
|
||||
} else {
|
||||
addButtonAlpha = 0.0
|
||||
}
|
||||
}
|
||||
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.secondColorFieldNode.isRemovable = true
|
||||
|
||||
self.firstColorFieldNode.isSelected = self.state.selection == .first
|
||||
self.secondColorFieldNode.isSelected = self.state.selection == .second
|
||||
|
||||
let firstFieldFrame = CGRect(x: leftInset, y: (topPanelHeight - fieldHeight) / 2.0, width: self.state.secondColor != nil ? floorToScreenPixels((size.width - fieldSpacing) / 2.0) - leftInset : size.width - leftInset - (self.state.secondColorAvailable ? rightInsetWithButton : rightInset), height: fieldHeight)
|
||||
transition.updateFrame(node: self.firstColorFieldNode, frame: firstFieldFrame)
|
||||
self.firstColorFieldNode.updateLayout(size: firstFieldFrame.size, transition: transition)
|
||||
|
||||
let secondFieldFrame = CGRect(x: firstFieldFrame.maxX + fieldSpacing, y: (topPanelHeight - fieldHeight) / 2.0, width: firstFieldFrame.width, height: fieldHeight)
|
||||
transition.updateFrame(node: self.secondColorFieldNode, frame: secondFieldFrame)
|
||||
self.secondColorFieldNode.updateLayout(size: secondFieldFrame.size, transition: transition)
|
||||
|
||||
let colorPickerSize = CGSize(width: size.width, height: size.height - topPanelHeight - separatorHeight)
|
||||
transition.updateFrame(node: self.colorPickerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight + separatorHeight), size: colorPickerSize))
|
||||
self.colorPickerNode.updateLayout(size: colorPickerSize, transition: transition)
|
||||
}
|
||||
|
||||
@objc private func swapPressed() {
|
||||
self.updateState({ current in
|
||||
var updated = current
|
||||
if let secondColor = current.secondColor {
|
||||
updated.firstColor = secondColor
|
||||
updated.secondColor = current.firstColor
|
||||
}
|
||||
return updated
|
||||
})
|
||||
}
|
||||
|
||||
@objc private func addPressed() {
|
||||
self.updateState({ current in
|
||||
var updated = current
|
||||
updated.selection = .second
|
||||
updated.secondColor = current.firstColor
|
||||
return updated
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ private let shadowImage: UIImage = {
|
||||
}()
|
||||
|
||||
private let pointerImage: UIImage = {
|
||||
return generateImage(CGSize(width: 12.0, height: 42.0), opaque: false, scale: nil, rotatedContext: { size, context in
|
||||
return generateImage(CGSize(width: 12.0, height: 55.0), opaque: false, scale: nil, rotatedContext: { size, context in
|
||||
context.setBlendMode(.clear)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
@ -29,8 +29,9 @@ private let pointerImage: UIImage = {
|
||||
context.setStrokeColor(UIColor.white.cgColor)
|
||||
context.setLineWidth(lineWidth)
|
||||
context.setLineCap(.round)
|
||||
context.setLineJoin(.round)
|
||||
|
||||
let pointerHeight: CGFloat = 6.0
|
||||
let pointerHeight: CGFloat = 7.0
|
||||
context.move(to: CGPoint(x: lineWidth / 2.0, y: lineWidth / 2.0))
|
||||
context.addLine(to: CGPoint(x: size.width - lineWidth / 2.0, y: lineWidth / 2.0))
|
||||
context.addLine(to: CGPoint(x: size.width / 2.0, y: lineWidth / 2.0 + pointerHeight))
|
||||
@ -100,7 +101,7 @@ private final class WallpaperColorKnobNode: ASDisplayNode {
|
||||
let color = UIColor(hue: parameters.hue, saturation: parameters.saturation, brightness: parameters.value, alpha: 1.0)
|
||||
context.setFillColor(color.cgColor)
|
||||
|
||||
let borderWidth: CGFloat = bounds.width > 30.0 ? 5.0 : 5.0
|
||||
let borderWidth: CGFloat = 7.0
|
||||
context.fillEllipse(in: bounds.insetBy(dx: borderWidth - UIScreenPixel, dy: borderWidth - UIScreenPixel))
|
||||
}
|
||||
}
|
||||
@ -265,11 +266,11 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
self.colorKnobNode.hsv = self.colorHSV
|
||||
}
|
||||
|
||||
func updateKnobLayout(size: CGSize, panningColor: Bool, transition: ContainedViewLayoutTransition) {
|
||||
private func updateKnobLayout(size: CGSize, panningColor: Bool, transition: ContainedViewLayoutTransition) {
|
||||
let knobSize = CGSize(width: 45.0, height: 45.0)
|
||||
|
||||
let colorHeight = size.height - 66.0
|
||||
var colorKnobFrame = CGRect(x: -knobSize.width / 2.0 + size.width * self.colorHSV.0, y: -knobSize.height / 2.0 + (colorHeight * (1.0 - self.colorHSV.1)), width: knobSize.width, height: knobSize.height)
|
||||
var colorKnobFrame = CGRect(x: floorToScreenPixels(-knobSize.width / 2.0 + size.width * self.colorHSV.0), y: floorToScreenPixels(-knobSize.height / 2.0 + (colorHeight * (1.0 - self.colorHSV.1))), width: knobSize.width, height: knobSize.height)
|
||||
var origin = colorKnobFrame.origin
|
||||
if !panningColor {
|
||||
origin = CGPoint(x: max(0.0, min(origin.x, size.width - knobSize.width)), y: max(0.0, min(origin.y, colorHeight - knobSize.height)))
|
||||
@ -280,8 +281,8 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
transition.updateFrame(node: self.colorKnobNode, frame: colorKnobFrame)
|
||||
|
||||
let inset: CGFloat = 42.0
|
||||
let brightnessKnobSize = CGSize(width: 12.0, height: 42.0)
|
||||
let brightnessKnobFrame = CGRect(x: inset - brightnessKnobSize.width / 2.0 + (size.width - inset * 2.0) * (1.0 - self.colorHSV.2), y: size.height - 61.0, width: brightnessKnobSize.width, height: brightnessKnobSize.height)
|
||||
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)
|
||||
}
|
||||
|
||||
@ -291,8 +292,8 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
let colorHeight = size.height - 66.0
|
||||
transition.updateFrame(node: self.colorNode, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: colorHeight))
|
||||
|
||||
let inset: CGFloat = 42.0
|
||||
transition.updateFrame(node: self.brightnessNode, frame: CGRect(x: inset, y: size.height - 55.0, width: size.width - inset * 2.0, height: 29.0))
|
||||
let inset: CGFloat = 15.0
|
||||
transition.updateFrame(node: self.brightnessNode, frame: CGRect(x: inset, y: size.height - 55.0, width: size.width - inset * 2.0, height: 35.0))
|
||||
|
||||
self.updateKnobLayout(size: size, panningColor: false, transition: .immediate)
|
||||
}
|
||||
|
@ -341,13 +341,13 @@ public class WallpaperGalleryController: ViewController {
|
||||
self.galleryNode.addSubnode(overlayNode)
|
||||
|
||||
let colorPanelNode = WallpaperColorPanelNode(theme: presentationData.theme, strings: presentationData.strings)
|
||||
colorPanelNode.colorChanged = { [weak self] color, ended in
|
||||
colorPanelNode.colorsChanged = { [weak self] color, _, ended in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateEntries(color: color, preview: !ended)
|
||||
}
|
||||
}
|
||||
if case let .customColor(colorValue) = self.source, let color = colorValue {
|
||||
colorPanelNode.color = UIColor(rgb: UInt32(bitPattern: color))
|
||||
//colorPanelNode.color = UIColor(rgb: UInt32(bitPattern: color))
|
||||
}
|
||||
self.colorPanelNode = colorPanelNode
|
||||
overlayNode.addSubnode(colorPanelNode)
|
||||
@ -387,15 +387,12 @@ public class WallpaperGalleryController: ViewController {
|
||||
let autoNightModeTriggered = strongSelf.presentationData.autoNightModeTriggered
|
||||
let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
var chatWallpaper = current.chatWallpaper
|
||||
if autoNightModeTriggered {
|
||||
themeSpecificChatWallpapers[current.automaticThemeSwitchSetting.theme.index] = wallpaper
|
||||
} else {
|
||||
themeSpecificChatWallpapers[current.theme.index] = wallpaper
|
||||
chatWallpaper = wallpaper
|
||||
}
|
||||
|
||||
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
}) |> deliverOnMainQueue).start(completed: {
|
||||
self?.dismiss(forceAway: true)
|
||||
})
|
||||
|
@ -226,14 +226,23 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
case let .color(color):
|
||||
displaySize = CGSize(width: 1.0, height: 1.0)
|
||||
contentSize = displaySize
|
||||
signal = solidColor(UIColor(rgb: UInt32(bitPattern: color)))
|
||||
signal = solidColorImage(UIColor(rgb: UInt32(bitPattern: color)))
|
||||
fetchSignal = .complete()
|
||||
statusSignal = .single(.Local)
|
||||
subtitleSignal = .single(nil)
|
||||
actionSignal = .single(defaultAction)
|
||||
colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: self.context.account.postbox.mediaBox)
|
||||
isBlurrable = false
|
||||
case let .gradient(topColor, bottomColor):
|
||||
displaySize = CGSize(width: 1.0, height: 1.0)
|
||||
contentSize = displaySize
|
||||
signal = gradientImage([UIColor(rgb: UInt32(bitPattern: topColor)), UIColor(rgb: UInt32(bitPattern: bottomColor))])
|
||||
fetchSignal = .complete()
|
||||
statusSignal = .single(.Local)
|
||||
subtitleSignal = .single(nil)
|
||||
actionSignal = .single(defaultAction)
|
||||
colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: self.context.account.postbox.mediaBox)
|
||||
isBlurrable = false
|
||||
//self.backgroundColor = UIColor(rgb: UInt32(bitPattern: color))
|
||||
case let .file(file):
|
||||
let dimensions = file.file.dimensions ?? PixelDimensions(width: 100, height: 100)
|
||||
contentSize = dimensions.cgSize
|
||||
@ -715,6 +724,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
blurFrame = leftButtonFrame
|
||||
motionAlpha = 1.0
|
||||
motionFrame = rightButtonFrame
|
||||
case .gradient:
|
||||
blurAlpha = 0.0
|
||||
patternAlpha = 0.0
|
||||
motionAlpha = 0.0
|
||||
case let .file(file):
|
||||
if file.isPattern {
|
||||
motionAlpha = 1.0
|
||||
|
@ -39,6 +39,7 @@ public struct WallpaperSettings: PostboxCoding, Equatable {
|
||||
public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable {
|
||||
case builtin(WallpaperSettings)
|
||||
case color(Int32)
|
||||
case gradient(Int32, Int32)
|
||||
case image([TelegramMediaImageRepresentation], WallpaperSettings)
|
||||
case file(id: Int64, accessHash: Int64, isCreator: Bool, isDefault: Bool, isPattern: Bool, isDark: Bool, slug: String, file: TelegramMediaFile, settings: WallpaperSettings)
|
||||
|
||||
@ -59,7 +60,8 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable {
|
||||
} else {
|
||||
self = .color(0xffffff)
|
||||
}
|
||||
|
||||
case 4:
|
||||
self = .gradient(decoder.decodeInt32ForKey("c1", orElse: 0), decoder.decodeInt32ForKey("c2", orElse: 0))
|
||||
default:
|
||||
assertionFailure()
|
||||
self = .color(0xffffff)
|
||||
@ -83,6 +85,10 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable {
|
||||
case let .color(color):
|
||||
encoder.encodeInt32(1, forKey: "v")
|
||||
encoder.encodeInt32(color, forKey: "c")
|
||||
case let .gradient(topColor, bottomColor):
|
||||
encoder.encodeInt32(4, forKey: "v")
|
||||
encoder.encodeInt32(topColor, forKey: "c1")
|
||||
encoder.encodeInt32(bottomColor, forKey: "c2")
|
||||
case let .image(representations, settings):
|
||||
encoder.encodeInt32(2, forKey: "v")
|
||||
encoder.encodeObjectArray(representations, forKey: "i")
|
||||
@ -115,6 +121,12 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .gradient(topColor, bottomColor):
|
||||
if case .gradient(topColor, bottomColor) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .image(representations, settings):
|
||||
if case .image(representations, settings) = rhs {
|
||||
return true
|
||||
@ -145,6 +157,8 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable {
|
||||
return .builtin(settings)
|
||||
case .color:
|
||||
return self
|
||||
case .gradient:
|
||||
return self
|
||||
case let .image(representations, _):
|
||||
return .image(representations, settings)
|
||||
case let .file(id, accessHash, isCreator, isDefault, isPattern, isDark, slug, file, _):
|
||||
|
@ -41,6 +41,16 @@ public func chatControllerBackgroundImage(theme: PresentationTheme, wallpaper in
|
||||
context.setFillColor(UIColor(rgb: UInt32(bitPattern: color)).cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
})
|
||||
case let .gradient(topColor, bottomColor):
|
||||
backgroundImage = generateImage(CGSize(width: 1.0, height: 1280.0), rotatedContext: { size, context in
|
||||
let gradientColors = [UIColor(rgb: UInt32(bitPattern: topColor)).cgColor, UIColor(rgb: UInt32(bitPattern: bottomColor)).cgColor] as CFArray
|
||||
|
||||
var locations: [CGFloat] = [0.0, 1.0]
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
|
||||
|
||||
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
|
||||
})
|
||||
case let .image(representations, settings):
|
||||
if let largest = largestImageRepresentation(representations) {
|
||||
if settings.blur && composed {
|
||||
|
@ -228,11 +228,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager, s
|
||||
|
||||
let contactSettings: ContactSynchronizationSettings = (transaction.getSharedData(ApplicationSpecificSharedDataKeys.contactSynchronizationSettings) as? ContactSynchronizationSettings) ?? ContactSynchronizationSettings.defaultSettings
|
||||
|
||||
let themeValue: PresentationTheme
|
||||
|
||||
let effectiveTheme: PresentationThemeReference
|
||||
var effectiveChatWallpaper: TelegramWallpaper = themeSettings.chatWallpaper
|
||||
|
||||
let parameters = AutomaticThemeSwitchParameters(settings: themeSettings.automaticThemeSwitchSetting)
|
||||
let autoNightModeTriggered: Bool
|
||||
if automaticThemeShouldSwitchNow(parameters, systemUserInterfaceStyle: systemUserInterfaceStyle) {
|
||||
@ -244,16 +240,8 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager, s
|
||||
}
|
||||
|
||||
let effectiveAccentColor = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.color
|
||||
themeValue = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.baseColor ?? .blue) ?? defaultPresentationTheme
|
||||
|
||||
if effectiveTheme != themeSettings.theme {
|
||||
switch effectiveChatWallpaper {
|
||||
case .builtin, .color:
|
||||
effectiveChatWallpaper = themeValue.chat.defaultWallpaper
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.baseColor ?? .blue) ?? defaultPresentationTheme
|
||||
let effectiveChatWallpaper: TelegramWallpaper = themeSettings.themeSpecificChatWallpapers[effectiveTheme.index] ?? theme.chat.defaultWallpaper
|
||||
|
||||
let dateTimeFormat = currentDateTimeFormat()
|
||||
let stringsValue: PresentationStrings
|
||||
@ -264,7 +252,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager, s
|
||||
}
|
||||
let nameDisplayOrder = contactSettings.nameDisplayOrder
|
||||
let nameSortOrder = currentPersonNameSortOrder()
|
||||
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: themeValue, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji), automaticMediaDownloadSettings: automaticMediaDownloadSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, experimentalUISettings: experimentalUISettings)
|
||||
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: theme, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji), automaticMediaDownloadSettings: automaticMediaDownloadSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, experimentalUISettings: experimentalUISettings)
|
||||
}
|
||||
}
|
||||
|
||||
@ -371,17 +359,21 @@ private func serviceColor(for data: Signal<MediaResourceData, NoError>) -> Signa
|
||||
}
|
||||
}
|
||||
|
||||
public func averageColor(from image: UIImage) -> UIColor {
|
||||
let context = DrawingContext(size: CGSize(width: 1.0, height: 1.0), scale: 1.0, clear: false)
|
||||
context.withFlippedContext({ context in
|
||||
if let cgImage = image.cgImage {
|
||||
context.draw(cgImage, in: CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0))
|
||||
}
|
||||
})
|
||||
return context.colorAt(CGPoint())
|
||||
}
|
||||
|
||||
public func serviceColor(from image: Signal<UIImage?, NoError>) -> Signal<UIColor, NoError> {
|
||||
return image
|
||||
|> mapToSignal { image -> Signal<UIColor, NoError> in
|
||||
if let image = image {
|
||||
let context = DrawingContext(size: CGSize(width: 1.0, height: 1.0), scale: 1.0, clear: false)
|
||||
context.withFlippedContext({ context in
|
||||
if let cgImage = image.cgImage {
|
||||
context.draw(cgImage, in: CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0))
|
||||
}
|
||||
})
|
||||
return .single(serviceColor(with: context.colorAt(CGPoint())))
|
||||
return .single(serviceColor(with: averageColor(from: image)))
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
@ -414,6 +406,9 @@ public func chatServiceBackgroundColor(wallpaper: TelegramWallpaper, mediaBox: M
|
||||
return .single(UIColor(rgb: 0x748391, alpha: 0.45))
|
||||
case let .color(color):
|
||||
return .single(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 .single(serviceColor(with: mixedColor))
|
||||
case let .image(representations, _):
|
||||
if let largest = largestImageRepresentation(representations) {
|
||||
return Signal<UIColor, NoError> { subscriber in
|
||||
@ -476,10 +471,11 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI
|
||||
if let themeSpecificWallpaper = themeSettings.themeSpecificChatWallpapers[themeSettings.theme.index] {
|
||||
currentWallpaper = themeSpecificWallpaper
|
||||
} else {
|
||||
currentWallpaper = themeSettings.chatWallpaper
|
||||
let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: themeSettings.theme, accentColor: nil, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil) ?? defaultPresentationTheme
|
||||
currentWallpaper = theme.chat.defaultWallpaper
|
||||
}
|
||||
|
||||
return (.single(UIColor(rgb: 0x000000, alpha: 0.3))
|
||||
return (.single(defaultServiceBackgroundColor)
|
||||
|> then(chatServiceBackgroundColor(wallpaper: currentWallpaper, mediaBox: accountManager.mediaBox)))
|
||||
|> mapToSignal { serviceBackgroundColor in
|
||||
return applicationInForeground
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Settings/ThemeColorAddIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/ThemeColorAddIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_input_add.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Settings/ThemeColorAddIcon.imageset/ic_input_add.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Settings/ThemeColorAddIcon.imageset/ic_input_add.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Settings/ThemeColorRemoveIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/ThemeColorRemoveIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_input_close.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Settings/ThemeColorRemoveIcon.imageset/ic_input_close.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Settings/ThemeColorRemoveIcon.imageset/ic_input_close.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Settings/ThemeColorSwapIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/ThemeColorSwapIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_input_change.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Settings/ThemeColorSwapIcon.imageset/ic_input_change.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Settings/ThemeColorSwapIcon.imageset/ic_input_change.pdf
vendored
Normal file
Binary file not shown.
@ -1,22 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "color@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "color@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.8 KiB |
@ -580,7 +580,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
}
|
||||
}
|
||||
case let .color(color):
|
||||
return solidColor(color)
|
||||
return solidColorImage(color)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,25 +131,15 @@ final class ThemeUpdateManagerImpl: ThemeUpdateManager {
|
||||
current = PresentationThemeSettings.defaultSettings
|
||||
}
|
||||
|
||||
var chatWallpaper = current.chatWallpaper
|
||||
var theme = current.theme
|
||||
var automaticThemeSwitchSetting = current.automaticThemeSwitchSetting
|
||||
if isAutoNight {
|
||||
automaticThemeSwitchSetting.theme = updatedTheme
|
||||
} else {
|
||||
if let themeSpecificWallpaper = current.themeSpecificChatWallpapers[updatedTheme.index] {
|
||||
chatWallpaper = themeSpecificWallpaper
|
||||
} else if let presentationTheme = presentationTheme {
|
||||
if case let .cloud(info) = updatedTheme, let resolvedWallpaper = info.resolvedWallpaper {
|
||||
chatWallpaper = resolvedWallpaper
|
||||
} else {
|
||||
chatWallpaper = presentationTheme.chat.defaultWallpaper
|
||||
}
|
||||
} else {
|
||||
chatWallpaper = current.chatWallpaper
|
||||
}
|
||||
theme = updatedTheme
|
||||
}
|
||||
|
||||
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: updatedTheme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
return PresentationThemeSettings(theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
})
|
||||
}).start()
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ public func upgradedAccounts(accountManager: AccountManager, rootPath: String, e
|
||||
|
||||
if let value = values[LegacyApplicationSpecificPreferencesKeyValues.presentationThemeSettings.key] as? PresentationThemeSettings {
|
||||
let mediaBox = MediaBox(basePath: path + "/postbox/media")
|
||||
let wallpapers = [value.chatWallpaper] + Array(value.themeSpecificChatWallpapers.values)
|
||||
let wallpapers = Array(value.themeSpecificChatWallpapers.values)
|
||||
for wallpaper in wallpapers {
|
||||
switch wallpaper {
|
||||
case let .file(file):
|
||||
|
@ -119,7 +119,7 @@ final class WallpaperUploadManagerImpl: WallpaperUploadManager {
|
||||
if strongSelf.currentPresentationData?.theme.name == presentationData.theme.name {
|
||||
let _ = (updatePresentationThemeSettingsInteractively(accountManager: sharedContext.accountManager, { current in
|
||||
let updatedWallpaper: TelegramWallpaper
|
||||
if let currentSettings = current.chatWallpaper.settings {
|
||||
if let currentSettings = currentWallpaper.settings {
|
||||
updatedWallpaper = wallpaper.withUpdatedSettings(currentSettings)
|
||||
} else {
|
||||
updatedWallpaper = wallpaper
|
||||
@ -127,7 +127,7 @@ final class WallpaperUploadManagerImpl: WallpaperUploadManager {
|
||||
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
themeSpecificChatWallpapers[current.theme.index] = updatedWallpaper
|
||||
return PresentationThemeSettings(chatWallpaper: updatedWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
})).start()
|
||||
}
|
||||
}
|
||||
|
@ -376,6 +376,38 @@ public enum PresentationThemeBaseColor: Int32, CaseIterable {
|
||||
}
|
||||
}
|
||||
|
||||
public struct PresentationThemeColorPair: PostboxCoding, Equatable {
|
||||
public var color: Int32
|
||||
public var optionalColor: Int32?
|
||||
|
||||
public init(color: Int32, optionalColor: Int32?) {
|
||||
self.color = color
|
||||
self.optionalColor = optionalColor
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.color = decoder.decodeInt32ForKey("t", orElse: 0)
|
||||
self.optionalColor = decoder.decodeOptionalInt32ForKey("b")
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeInt32(self.color, forKey: "t")
|
||||
if let bottomColor = self.optionalColor {
|
||||
encoder.encodeInt32(bottomColor, forKey: "b")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "b")
|
||||
}
|
||||
}
|
||||
|
||||
public var colors: [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))]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct PresentationThemeAccentColor: PostboxCoding, Equatable {
|
||||
public var baseColor: PresentationThemeBaseColor
|
||||
public var value: Int32?
|
||||
@ -409,9 +441,9 @@ public struct PresentationThemeAccentColor: PostboxCoding, Equatable {
|
||||
}
|
||||
|
||||
public struct PresentationThemeSettings: PreferencesEntry {
|
||||
public var chatWallpaper: TelegramWallpaper
|
||||
public var theme: PresentationThemeReference
|
||||
public var themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||
public var themeSpecificBubbleColors: [Int64: PresentationThemeColorPair]
|
||||
public var themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
||||
public var fontSize: PresentationFontSize
|
||||
public var automaticThemeSwitchSetting: AutomaticThemeSwitchSetting
|
||||
@ -419,7 +451,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
||||
public var disableAnimations: Bool
|
||||
|
||||
private func wallpaperResources(_ wallpaper: TelegramWallpaper) -> [MediaResourceId] {
|
||||
switch self.chatWallpaper {
|
||||
switch wallpaper {
|
||||
case let .image(representations, _):
|
||||
return representations.map { $0.resource.id }
|
||||
case let .file(_, _, _, _, _, _, _, file, _):
|
||||
@ -434,7 +466,6 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
||||
|
||||
public var relatedResources: [MediaResourceId] {
|
||||
var resources: [MediaResourceId] = []
|
||||
resources.append(contentsOf: wallpaperResources(self.chatWallpaper))
|
||||
for (_, chatWallpaper) in self.themeSpecificChatWallpapers {
|
||||
resources.append(contentsOf: wallpaperResources(chatWallpaper))
|
||||
}
|
||||
@ -455,13 +486,13 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
||||
}
|
||||
|
||||
public static var defaultSettings: PresentationThemeSettings {
|
||||
return PresentationThemeSettings(chatWallpaper: .builtin(WallpaperSettings()), theme: .builtin(.dayClassic), themeSpecificAccentColors: [:], themeSpecificChatWallpapers: [:], fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .system, theme: .builtin(.night)), largeEmoji: true, disableAnimations: true)
|
||||
return PresentationThemeSettings(theme: .builtin(.dayClassic), themeSpecificAccentColors: [:], themeSpecificBubbleColors: [:], themeSpecificChatWallpapers: [:], fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .system, theme: .builtin(.night)), largeEmoji: true, disableAnimations: true)
|
||||
}
|
||||
|
||||
public init(chatWallpaper: TelegramWallpaper, theme: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, largeEmoji: Bool, disableAnimations: Bool) {
|
||||
self.chatWallpaper = chatWallpaper
|
||||
public init(theme: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificBubbleColors: [Int64: PresentationThemeColorPair], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, largeEmoji: Bool, disableAnimations: Bool) {
|
||||
self.theme = theme
|
||||
self.themeSpecificAccentColors = themeSpecificAccentColors
|
||||
self.themeSpecificBubbleColors = themeSpecificBubbleColors
|
||||
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
|
||||
self.fontSize = fontSize
|
||||
self.automaticThemeSwitchSetting = automaticThemeSwitchSetting
|
||||
@ -470,7 +501,6 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.chatWallpaper = (decoder.decodeObjectForKey("w", decoder: { TelegramWallpaper(decoder: $0) }) as? TelegramWallpaper) ?? .builtin(WallpaperSettings())
|
||||
self.theme = decoder.decodeObjectForKey("t", decoder: { PresentationThemeReference(decoder: $0) }) as? PresentationThemeReference ?? .builtin(.dayClassic)
|
||||
|
||||
self.themeSpecificChatWallpapers = decoder.decodeObjectDictionaryForKey("themeSpecificChatWallpapers", keyDecoder: { decoder in
|
||||
@ -485,6 +515,12 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
||||
return PresentationThemeAccentColor(decoder: decoder)
|
||||
})
|
||||
|
||||
self.themeSpecificBubbleColors = decoder.decodeObjectDictionaryForKey("themeSpecificBubbleColors", keyDecoder: { decoder in
|
||||
return decoder.decodeInt64ForKey("k", orElse: 0)
|
||||
}, 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 {
|
||||
@ -517,11 +553,13 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeObject(self.chatWallpaper, forKey: "w")
|
||||
encoder.encodeObject(self.theme, forKey: "t")
|
||||
encoder.encodeObjectDictionary(self.themeSpecificAccentColors, forKey: "themeSpecificAccentColors", keyEncoder: { key, encoder in
|
||||
encoder.encodeInt64(key, forKey: "k")
|
||||
})
|
||||
encoder.encodeObjectDictionary(self.themeSpecificBubbleColors, forKey: "themeSpecificBubbleColors", keyEncoder: { key, encoder in
|
||||
encoder.encodeInt64(key, forKey: "k")
|
||||
})
|
||||
encoder.encodeObjectDictionary(self.themeSpecificChatWallpapers, forKey: "themeSpecificChatWallpapers", keyEncoder: { key, encoder in
|
||||
encoder.encodeInt64(key, forKey: "k")
|
||||
})
|
||||
@ -540,7 +578,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
||||
}
|
||||
|
||||
public static func ==(lhs: PresentationThemeSettings, rhs: PresentationThemeSettings) -> Bool {
|
||||
return lhs.chatWallpaper == rhs.chatWallpaper && lhs.theme == rhs.theme && lhs.themeSpecificAccentColors == rhs.themeSpecificAccentColors && lhs.themeSpecificChatWallpapers == rhs.themeSpecificChatWallpapers && lhs.fontSize == rhs.fontSize && lhs.automaticThemeSwitchSetting == rhs.automaticThemeSwitchSetting && lhs.largeEmoji == rhs.largeEmoji && lhs.disableAnimations == rhs.disableAnimations
|
||||
return lhs.theme == rhs.theme && lhs.themeSpecificAccentColors == rhs.themeSpecificAccentColors && lhs.themeSpecificBubbleColors == rhs.themeSpecificBubbleColors && lhs.themeSpecificChatWallpapers == rhs.themeSpecificChatWallpapers && lhs.fontSize == rhs.fontSize && lhs.automaticThemeSwitchSetting == rhs.automaticThemeSwitchSetting && lhs.largeEmoji == rhs.largeEmoji && lhs.disableAnimations == rhs.disableAnimations
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -475,7 +475,7 @@ public func patternColor(for color: UIColor, intensity: CGFloat, prominent: Bool
|
||||
return .black
|
||||
}
|
||||
|
||||
public func solidColor(_ color: UIColor) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
public func solidColorImage(_ color: UIColor) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
return .single({ arguments in
|
||||
let context = DrawingContext(size: arguments.drawingSize, clear: true)
|
||||
|
||||
@ -490,6 +490,40 @@ public func solidColor(_ color: UIColor) -> Signal<(TransformImageArguments) ->
|
||||
})
|
||||
}
|
||||
|
||||
public func gradientImage(_ colors: [UIColor]) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
guard !colors.isEmpty else {
|
||||
return .complete()
|
||||
}
|
||||
guard colors.count > 1 else {
|
||||
if let color = colors.first {
|
||||
return solidColorImage(color)
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
return .single({ arguments in
|
||||
let context = DrawingContext(size: arguments.drawingSize, clear: true)
|
||||
|
||||
context.withFlippedContext { c in
|
||||
let gradientColors = colors 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)!
|
||||
|
||||
c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: arguments.drawingSize.height), options: CGGradientDrawingOptions())
|
||||
}
|
||||
|
||||
addCorners(context, arguments: arguments)
|
||||
|
||||
return context
|
||||
})
|
||||
}
|
||||
|
||||
private func builtinWallpaperData() -> Signal<UIImage, NoError> {
|
||||
return Signal { subscriber in
|
||||
if let filePath = getAppBundle().path(forResource: "ChatWallpaperBuiltin0", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath) {
|
||||
@ -961,6 +995,8 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
||||
backgroundColor = UIColor(rgb: 0xd6e2ee)
|
||||
case let .color(color):
|
||||
backgroundColor = UIColor(rgb: UInt32(bitPattern: color))
|
||||
case let .gradient(topColor, bottomColor):
|
||||
backgroundColor = UIColor(rgb: UInt32(bitPattern: topColor))
|
||||
case .image:
|
||||
backgroundColor = .black
|
||||
case let .file(file):
|
||||
|
Loading…
x
Reference in New Issue
Block a user