mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-29 03:21:29 +00:00
Theming improvements
This commit is contained in:
parent
fccae60f77
commit
fa3303be90
@ -5186,7 +5186,8 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
|
|
||||||
"AuthSessions.AddDevice.UrlLoginHint" = "This code can be used to allow someone to log in to your Telegram account.\n\nTo confirm Telegram login, please go to Settings > Devices > Scan QR and scan the code.";
|
"AuthSessions.AddDevice.UrlLoginHint" = "This code can be used to allow someone to log in to your Telegram account.\n\nTo confirm Telegram login, please go to Settings > Devices > Scan QR and scan the code.";
|
||||||
|
|
||||||
"Appearance.RemoveThemeColor" = "Remove Color";
|
"Appearance.RemoveThemeColor" = "Remove";
|
||||||
|
"Appearance.RemoveThemeColorConfirmation" = "Remove Color";
|
||||||
|
|
||||||
"WallpaperPreview.PatternTitle" = "Choose Pattern";
|
"WallpaperPreview.PatternTitle" = "Choose Pattern";
|
||||||
"WallpaperPreview.PatternPaternDiscard" = "Discard";
|
"WallpaperPreview.PatternPaternDiscard" = "Discard";
|
||||||
|
|||||||
@ -808,6 +808,39 @@ public extension ContainedViewLayoutTransition {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateTransformRotation(node: ASDisplayNode, angle: CGFloat, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||||
|
let t = node.layer.transform
|
||||||
|
let currentAngle = atan2(t.m12, t.m11)
|
||||||
|
if currentAngle.isEqual(to: angle) {
|
||||||
|
if let completion = completion {
|
||||||
|
completion(true)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch self {
|
||||||
|
case .immediate:
|
||||||
|
node.layer.transform = CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0)
|
||||||
|
if let completion = completion {
|
||||||
|
completion(true)
|
||||||
|
}
|
||||||
|
case let .animated(duration, curve):
|
||||||
|
let previousAngle: CGFloat
|
||||||
|
if beginWithCurrentState, let presentation = node.layer.presentation() {
|
||||||
|
let t = presentation.transform
|
||||||
|
previousAngle = atan2(t.m12, t.m11)
|
||||||
|
} else {
|
||||||
|
previousAngle = currentAngle
|
||||||
|
}
|
||||||
|
node.layer.transform = CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0)
|
||||||
|
node.layer.animateRotation(from: previousAngle, to: angle, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
||||||
|
if let completion = completion {
|
||||||
|
completion(result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
|
|||||||
@ -156,12 +156,15 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE
|
|||||||
|
|
||||||
let _ = (updatePresentationThemeSettingsInteractively(accountManager: accountManager, { current in
|
let _ = (updatePresentationThemeSettingsInteractively(accountManager: accountManager, { current in
|
||||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||||
|
let themeReference: PresentationThemeReference
|
||||||
if autoNightModeTriggered {
|
if autoNightModeTriggered {
|
||||||
themeSpecificChatWallpapers[current.automaticThemeSwitchSetting.theme.index] = wallpaper
|
themeReference = current.automaticThemeSwitchSetting.theme
|
||||||
} else {
|
} else {
|
||||||
themeSpecificChatWallpapers[current.theme.index] = wallpaper
|
themeReference = current.theme
|
||||||
}
|
}
|
||||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
let accentColorIndex = current.themeSpecificAccentColors[themeReference.index]?.index ?? 0
|
||||||
|
themeSpecificChatWallpapers[themeReference.index &+ Int64(accentColorIndex)] = wallpaper
|
||||||
|
return current.withUpdatedThemeSpecificChatWallpapers(themeSpecificChatWallpapers)
|
||||||
})).start()
|
})).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -497,25 +497,18 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
|||||||
|> deliverOnMainQueue).start(next: { next in
|
|> deliverOnMainQueue).start(next: { next in
|
||||||
if case let .result(resultTheme) = next {
|
if case let .result(resultTheme) = next {
|
||||||
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start()
|
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start()
|
||||||
let _ = (context.sharedContext.accountManager.transaction { transaction -> Void in
|
let _ = (updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
|
if let resource = resultTheme.file?.resource, let data = themeData {
|
||||||
let current: PresentationThemeSettings
|
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
|
||||||
if let entry = entry as? PresentationThemeSettings {
|
}
|
||||||
current = entry
|
|
||||||
} else {
|
let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper))
|
||||||
current = PresentationThemeSettings.defaultSettings
|
|
||||||
}
|
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||||
if let resource = resultTheme.file?.resource, let data = themeData {
|
themeSpecificChatWallpapers[themeReference.index] = nil
|
||||||
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
|
|
||||||
}
|
return PresentationThemeSettings(theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificCustomColors: current.themeSpecificCustomColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||||
let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper))
|
}) |> deliverOnMainQueue).start(completed: {
|
||||||
|
|
||||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
|
||||||
themeSpecificChatWallpapers[themeReference.index] = nil
|
|
||||||
|
|
||||||
return PresentationThemeSettings(theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
|
||||||
})
|
|
||||||
} |> deliverOnMainQueue).start(completed: {
|
|
||||||
if !hasCustomFile {
|
if !hasCustomFile {
|
||||||
saveThemeTemplateFile(state.title, themeResource, {
|
saveThemeTemplateFile(state.title, themeResource, {
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
@ -538,25 +531,18 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
|||||||
|> deliverOnMainQueue).start(next: { next in
|
|> deliverOnMainQueue).start(next: { next in
|
||||||
if case let .result(resultTheme) = next {
|
if case let .result(resultTheme) = next {
|
||||||
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start()
|
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start()
|
||||||
let _ = (context.sharedContext.accountManager.transaction { transaction -> Void in
|
let _ = (updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
|
if let resource = resultTheme.file?.resource, let data = themeData {
|
||||||
let current: PresentationThemeSettings
|
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
|
||||||
if let entry = entry as? PresentationThemeSettings {
|
}
|
||||||
current = entry
|
|
||||||
} else {
|
let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper))
|
||||||
current = PresentationThemeSettings.defaultSettings
|
|
||||||
}
|
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||||
if let resource = resultTheme.file?.resource, let data = themeData {
|
themeSpecificChatWallpapers[themeReference.index] = nil
|
||||||
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
|
|
||||||
}
|
return PresentationThemeSettings(theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificCustomColors: current.themeSpecificCustomColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||||
let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper))
|
}) |> deliverOnMainQueue).start(completed: {
|
||||||
|
|
||||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
|
||||||
themeSpecificChatWallpapers[themeReference.index] = nil
|
|
||||||
|
|
||||||
return PresentationThemeSettings(theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
|
||||||
})
|
|
||||||
} |> deliverOnMainQueue).start(completed: {
|
|
||||||
if let themeResource = themeResource, !hasCustomFile {
|
if let themeResource = themeResource, !hasCustomFile {
|
||||||
saveThemeTemplateFile(state.title, themeResource, {
|
saveThemeTemplateFile(state.title, themeResource, {
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
|
|||||||
@ -15,13 +15,13 @@ import MediaResources
|
|||||||
private let randomBackgroundColors: [Int32] = [0x007aff, 0x00c2ed, 0x29b327, 0xeb6ca4, 0xf08200, 0x9472ee, 0xd33213, 0xedb400, 0x6d839e]
|
private let randomBackgroundColors: [Int32] = [0x007aff, 0x00c2ed, 0x29b327, 0xeb6ca4, 0xf08200, 0x9472ee, 0xd33213, 0xedb400, 0x6d839e]
|
||||||
|
|
||||||
enum ThemeAccentColorControllerMode {
|
enum ThemeAccentColorControllerMode {
|
||||||
case colors(themeReference: PresentationThemeReference)
|
case colors(themeReference: PresentationThemeReference, create: Bool)
|
||||||
case background(themeReference: PresentationThemeReference)
|
case background(themeReference: PresentationThemeReference)
|
||||||
case edit(theme: PresentationTheme, wallpaper: TelegramWallpaper?, defaultThemeReference: PresentationThemeReference?, create: Bool, completion: (PresentationTheme) -> Void)
|
case edit(theme: PresentationTheme, wallpaper: TelegramWallpaper?, defaultThemeReference: PresentationThemeReference?, create: Bool, completion: (PresentationTheme) -> Void)
|
||||||
|
|
||||||
var themeReference: PresentationThemeReference? {
|
var themeReference: PresentationThemeReference? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .colors(themeReference), let .background(themeReference):
|
case let .colors(themeReference, _), let .background(themeReference):
|
||||||
return themeReference
|
return themeReference
|
||||||
case let .edit(_, _, defaultThemeReference, _, _):
|
case let .edit(_, _, defaultThemeReference, _, _):
|
||||||
return defaultThemeReference
|
return defaultThemeReference
|
||||||
@ -37,6 +37,7 @@ final class ThemeAccentColorController: ViewController {
|
|||||||
private let section: ThemeColorSection
|
private let section: ThemeColorSection
|
||||||
private let initialBackgroundColor: UIColor?
|
private let initialBackgroundColor: UIColor?
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
|
private var initialAccentColor: PresentationThemeAccentColor?
|
||||||
|
|
||||||
private var controllerNode: ThemeAccentColorControllerNode {
|
private var controllerNode: ThemeAccentColorControllerNode {
|
||||||
return self.displayNode as! ThemeAccentColorControllerNode
|
return self.displayNode as! ThemeAccentColorControllerNode
|
||||||
@ -133,6 +134,7 @@ final class ThemeAccentColorController: ViewController {
|
|||||||
}, apply: { [weak self] state, serviceBackgroundColor in
|
}, apply: { [weak self] state, serviceBackgroundColor in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let context = strongSelf.context
|
let context = strongSelf.context
|
||||||
|
let initialAccentColor = strongSelf.initialAccentColor
|
||||||
|
|
||||||
var coloredWallpaper: TelegramWallpaper?
|
var coloredWallpaper: TelegramWallpaper?
|
||||||
if let backgroundColors = state.backgroundColors {
|
if let backgroundColors = state.backgroundColors {
|
||||||
@ -198,6 +200,8 @@ final class ThemeAccentColorController: ViewController {
|
|||||||
|
|
||||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||||
var themeSpecificAccentColors = current.themeSpecificAccentColors
|
var themeSpecificAccentColors = current.themeSpecificAccentColors
|
||||||
|
var themeSpecificCustomColors = current.themeSpecificCustomColors
|
||||||
|
var customColors = themeSpecificCustomColors[currentTheme.index]?.colors ?? []
|
||||||
|
|
||||||
var bubbleColors: (Int32, Int32?)?
|
var bubbleColors: (Int32, Int32?)?
|
||||||
if let messagesColors = state.messagesColors {
|
if let messagesColors = state.messagesColors {
|
||||||
@ -208,16 +212,37 @@ final class ThemeAccentColorController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let color = PresentationThemeAccentColor(baseColor: .custom, accentColor: Int32(bitPattern: state.accentColor.rgb), bubbleColors: bubbleColors)
|
let index: Int32
|
||||||
|
var exists: Bool
|
||||||
|
if let initialIndex = initialAccentColor?.index, initialIndex != -1 {
|
||||||
|
index = initialIndex
|
||||||
|
exists = true
|
||||||
|
} else {
|
||||||
|
index = Int32(bitPattern: arc4random())
|
||||||
|
exists = false
|
||||||
|
}
|
||||||
|
|
||||||
|
let color = PresentationThemeAccentColor(index: index, baseColor: .custom, accentColor: Int32(bitPattern: state.accentColor.rgb), bubbleColors: bubbleColors)
|
||||||
themeSpecificAccentColors[currentTheme.index] = color
|
themeSpecificAccentColors[currentTheme.index] = color
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
if let index = customColors.firstIndex(where: { $0.index == index }) {
|
||||||
|
customColors[index] = color
|
||||||
|
} else {
|
||||||
|
customColors.append(color)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
customColors.append(color)
|
||||||
|
}
|
||||||
|
|
||||||
var wallpaper = themeSpecificChatWallpapers[currentTheme.index]
|
var wallpaper = themeSpecificChatWallpapers[currentTheme.index]
|
||||||
if let coloredWallpaper = coloredWallpaper {
|
if let coloredWallpaper = coloredWallpaper {
|
||||||
wallpaper = coloredWallpaper
|
wallpaper = coloredWallpaper
|
||||||
}
|
}
|
||||||
themeSpecificChatWallpapers[currentTheme.index] = wallpaper
|
themeSpecificChatWallpapers[currentTheme.index &+ Int64(index)] = wallpaper
|
||||||
|
themeSpecificCustomColors[currentTheme.index] = PresentationThemeCustomColors(colors: customColors)
|
||||||
|
|
||||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificCustomColors: themeSpecificCustomColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||||
})) |> deliverOnMainQueue).start(completed: { [weak self] in
|
})) |> deliverOnMainQueue).start(completed: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.completion?()
|
strongSelf.completion?()
|
||||||
@ -248,7 +273,7 @@ final class ThemeAccentColorController: ViewController {
|
|||||||
var initialWallpaper: TelegramWallpaper?
|
var initialWallpaper: TelegramWallpaper?
|
||||||
var backgroundColors: (UIColor, UIColor?)?
|
var backgroundColors: (UIColor, UIColor?)?
|
||||||
var patternWallpaper: TelegramWallpaper?
|
var patternWallpaper: TelegramWallpaper?
|
||||||
var patternIntensity: Int32 = 40
|
var patternIntensity: Int32 = 50
|
||||||
var motion = false
|
var motion = false
|
||||||
let messageColors: (UIColor, UIColor?)?
|
let messageColors: (UIColor, UIColor?)?
|
||||||
var defaultMessagesColor: UIColor?
|
var defaultMessagesColor: UIColor?
|
||||||
@ -288,9 +313,16 @@ final class ThemeAccentColorController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let themeReference = strongSelf.mode.themeReference {
|
if let themeReference = strongSelf.mode.themeReference {
|
||||||
accentColor = settings.themeSpecificAccentColors[themeReference.index]?.color ?? defaultDayAccentColor
|
let themeSpecificAccentColor = settings.themeSpecificAccentColors[themeReference.index]
|
||||||
|
if case .colors(_, true) = strongSelf.mode {
|
||||||
|
} else if themeSpecificAccentColor?.baseColor == .custom {
|
||||||
|
strongSelf.initialAccentColor = themeSpecificAccentColor
|
||||||
|
}
|
||||||
|
accentColor = themeSpecificAccentColor?.color ?? defaultDayAccentColor
|
||||||
var wallpaper: TelegramWallpaper
|
var wallpaper: TelegramWallpaper
|
||||||
if let customWallpaper = settings.themeSpecificChatWallpapers[themeReference.index] {
|
if let index = themeSpecificAccentColor?.index, let customWallpaper = settings.themeSpecificChatWallpapers[themeReference.index &+ Int64(index)] {
|
||||||
|
wallpaper = customWallpaper
|
||||||
|
} else if let customWallpaper = settings.themeSpecificChatWallpapers[themeReference.index] {
|
||||||
wallpaper = customWallpaper
|
wallpaper = customWallpaper
|
||||||
} else {
|
} else {
|
||||||
let theme = makePresentationTheme(mediaBox: strongSelf.context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: nil) ?? defaultPresentationTheme
|
let theme = makePresentationTheme(mediaBox: strongSelf.context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: nil) ?? defaultPresentationTheme
|
||||||
|
|||||||
@ -64,7 +64,7 @@ struct ThemeColorState {
|
|||||||
self.preview = false
|
self.preview = false
|
||||||
self.previousPatternWallpaper = nil
|
self.previousPatternWallpaper = nil
|
||||||
self.patternWallpaper = nil
|
self.patternWallpaper = nil
|
||||||
self.patternIntensity = 40
|
self.patternIntensity = 50
|
||||||
self.motion = false
|
self.motion = false
|
||||||
self.defaultMessagesColor = nil
|
self.defaultMessagesColor = nil
|
||||||
self.messagesColors = nil
|
self.messagesColors = nil
|
||||||
@ -669,7 +669,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.colorPanelNode.updateState({ _ in
|
self.colorPanelNode.updateState({ _ in
|
||||||
return WallpaperColorPanelNodeState(selection: colorPanelCollapsed ? .none : .first, firstColor: firstColor, defaultColor: defaultColor, secondColor: secondColor, secondColorAvailable: self.state.section != .accent, rotateAvailable: self.state.section == .background, preview: false)
|
return WallpaperColorPanelNodeState(selection: colorPanelCollapsed ? .none : .first, firstColor: firstColor, defaultColor: defaultColor, secondColor: secondColor, secondColorAvailable: self.state.section != .accent, rotateAvailable: self.state.section == .background, rotation: self.state.rotation ?? 0, preview: false)
|
||||||
}, animated: animated)
|
}, animated: animated)
|
||||||
|
|
||||||
needsLayout = true
|
needsLayout = true
|
||||||
@ -986,7 +986,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
|||||||
let centerButtonFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonSize.width) / 2.0), y: layout.size.height - bottomInset - 44.0), size: buttonSize)
|
let centerButtonFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonSize.width) / 2.0), y: layout.size.height - bottomInset - 44.0), size: buttonSize)
|
||||||
let rightButtonFrame = CGRect(origin: CGPoint(x: ceil(layout.size.width / 2.0 + 10.0), y: layout.size.height - bottomInset - 44.0), size: buttonSize)
|
let rightButtonFrame = CGRect(origin: CGPoint(x: ceil(layout.size.width / 2.0 + 10.0), y: layout.size.height - bottomInset - 44.0), size: buttonSize)
|
||||||
|
|
||||||
var hasMotion: Bool = self.state.backgroundColors?.1 != nil || self.state.patternWallpaper != nil || self.state.displayPatternPanel
|
var hasMotion: Bool = self.state.patternWallpaper != nil || self.state.displayPatternPanel
|
||||||
|
|
||||||
var patternAlpha: CGFloat = displayOptionButtons ? 1.0 : 0.0
|
var patternAlpha: CGFloat = displayOptionButtons ? 1.0 : 0.0
|
||||||
var motionAlpha: CGFloat = displayOptionButtons && hasMotion ? 1.0 : 0.0
|
var motionAlpha: CGFloat = displayOptionButtons && hasMotion ? 1.0 : 0.0
|
||||||
|
|||||||
@ -68,7 +68,7 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
case settingInfo(PresentationTheme, String)
|
case settingInfo(PresentationTheme, String)
|
||||||
|
|
||||||
case themeHeader(PresentationTheme, String)
|
case themeHeader(PresentationTheme, String)
|
||||||
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor])
|
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor], [Int64: TelegramWallpaper])
|
||||||
|
|
||||||
var section: ItemListSectionId {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
@ -186,8 +186,8 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .themeItem(lhsTheme, lhsStrings, lhsThemes, lhsCurrentTheme, lhsThemeAccentColors):
|
case let .themeItem(lhsTheme, lhsStrings, lhsThemes, lhsCurrentTheme, lhsThemeAccentColors, lhsThemeChatWallpapers):
|
||||||
if case let .themeItem(rhsTheme, rhsStrings, rhsThemes, rhsCurrentTheme, rhsThemeAccentColors) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsThemes == rhsThemes, lhsCurrentTheme == rhsCurrentTheme, lhsThemeAccentColors == rhsThemeAccentColors {
|
if case let .themeItem(rhsTheme, rhsStrings, rhsThemes, rhsCurrentTheme, rhsThemeAccentColors, rhsThemeChatWallpapers) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsThemes == rhsThemes, lhsCurrentTheme == rhsCurrentTheme, lhsThemeAccentColors == rhsThemeAccentColors, lhsThemeChatWallpapers == rhsThemeChatWallpapers {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -244,8 +244,8 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
case let .themeHeader(theme, title):
|
case let .themeHeader(theme, title):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
||||||
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors):
|
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors, themeSpecificChatWallpapers):
|
||||||
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, displayUnsupported: false, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updatedTheme: { theme in
|
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, displayUnsupported: false, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, currentTheme: currentTheme, updatedTheme: { theme in
|
||||||
arguments.updateTheme(theme)
|
arguments.updateTheme(theme)
|
||||||
}, contextAction: nil)
|
}, contextAction: nil)
|
||||||
}
|
}
|
||||||
@ -313,7 +313,7 @@ private func themeAutoNightSettingsControllerEntries(theme: PresentationTheme, s
|
|||||||
break
|
break
|
||||||
case .system, .timeBased, .brightness:
|
case .system, .timeBased, .brightness:
|
||||||
entries.append(.themeHeader(theme, strings.AutoNightTheme_PreferredTheme))
|
entries.append(.themeHeader(theme, strings.AutoNightTheme_PreferredTheme))
|
||||||
entries.append(.themeItem(theme, strings, availableThemes, switchSetting.theme, settings.themeSpecificAccentColors))
|
entries.append(.themeItem(theme, strings, availableThemes, switchSetting.theme, settings.themeSpecificAccentColors, settings.themeSpecificChatWallpapers))
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
|
|||||||
@ -182,8 +182,16 @@ final class ThemeGridController: ViewController {
|
|||||||
let presentationData = strongSelf.presentationData
|
let presentationData = strongSelf.presentationData
|
||||||
let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in
|
let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in
|
||||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||||
|
let themeReference: PresentationThemeReference
|
||||||
|
if presentationData.autoNightModeTriggered {
|
||||||
|
themeReference = current.automaticThemeSwitchSetting.theme
|
||||||
|
} else {
|
||||||
|
themeReference = current.theme
|
||||||
|
}
|
||||||
|
let accentColorIndex = current.themeSpecificAccentColors[themeReference.index]?.index ?? 0
|
||||||
themeSpecificChatWallpapers[current.theme.index] = nil
|
themeSpecificChatWallpapers[current.theme.index] = nil
|
||||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
themeSpecificChatWallpapers[current.theme.index &+ Int64(accentColorIndex)] = nil
|
||||||
|
return current.withUpdatedThemeSpecificChatWallpapers(themeSpecificChatWallpapers)
|
||||||
})).start()
|
})).start()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -233,18 +241,8 @@ final class ThemeGridController: ViewController {
|
|||||||
|
|
||||||
let _ = resetWallpapers(account: strongSelf.context.account).start(completed: { [weak self, weak controller] in
|
let _ = resetWallpapers(account: strongSelf.context.account).start(completed: { [weak self, weak controller] in
|
||||||
let presentationData = strongSelf.presentationData
|
let presentationData = strongSelf.presentationData
|
||||||
let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> Void in
|
let _ = updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
|
return current.withUpdatedThemeSpecificChatWallpapers([:])
|
||||||
let current: PresentationThemeSettings
|
|
||||||
if let entry = entry as? PresentationThemeSettings {
|
|
||||||
current = entry
|
|
||||||
} else {
|
|
||||||
current = PresentationThemeSettings.defaultSettings
|
|
||||||
}
|
|
||||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
|
||||||
themeSpecificChatWallpapers[current.theme.index] = nil
|
|
||||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: [:], useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
|
||||||
})
|
|
||||||
}).start()
|
}).start()
|
||||||
|
|
||||||
let _ = (telegramWallpapers(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network)
|
let _ = (telegramWallpapers(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network)
|
||||||
|
|||||||
@ -322,25 +322,21 @@ public final class ThemePreviewController: ViewController {
|
|||||||
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: info.theme).start()
|
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()
|
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
|
||||||
let autoNightModeTriggered = context.sharedContext.currentPresentationData.with { $0 }.autoNightModeTriggered
|
return updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current -> PresentationThemeSettings in
|
||||||
|
var updated: PresentationThemeSettings
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
|
if autoNightModeTriggered {
|
||||||
let current = entry as? PresentationThemeSettings ?? PresentationThemeSettings.defaultSettings
|
|
||||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
|
||||||
themeSpecificChatWallpapers[updatedTheme.index] = nil
|
|
||||||
|
|
||||||
var theme = current.theme
|
|
||||||
var automaticThemeSwitchSetting = current.automaticThemeSwitchSetting
|
var automaticThemeSwitchSetting = current.automaticThemeSwitchSetting
|
||||||
if autoNightModeTriggered {
|
automaticThemeSwitchSetting.theme = updatedTheme
|
||||||
automaticThemeSwitchSetting.theme = updatedTheme
|
updated = current.withUpdatedAutomaticThemeSwitchSetting(automaticThemeSwitchSetting)
|
||||||
} else {
|
} else {
|
||||||
theme = updatedTheme
|
updated = current.withUpdatedTheme(updatedTheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
return PresentationThemeSettings(theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||||
})
|
themeSpecificChatWallpapers[updatedTheme.index] = nil
|
||||||
}
|
return updated.withUpdatedThemeSpecificChatWallpapers(themeSpecificChatWallpapers)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var cancelImpl: (() -> Void)?
|
var cancelImpl: (() -> Void)?
|
||||||
|
|||||||
@ -7,74 +7,392 @@ import TelegramCore
|
|||||||
import SyncCore
|
import SyncCore
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import TelegramUIPreferences
|
import TelegramUIPreferences
|
||||||
|
import MergeLists
|
||||||
import ItemListUI
|
import ItemListUI
|
||||||
import ContextUI
|
import ContextUI
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
|
|
||||||
private func generateSwatchImage(theme: PresentationTheme, themeReference: PresentationThemeReference, color: PresentationThemeAccentColor, bubbles: (UIColor, UIColor?)?, selected: Bool, more: Bool) -> UIImage? {
|
private enum ThemeSettingsColorEntryId: Hashable {
|
||||||
|
case color(Int)
|
||||||
|
case picker
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ThemeSettingsColorEntry: Comparable, Identifiable {
|
||||||
|
case color(Int, PresentationThemeReference, PresentationThemeAccentColor?, Bool)
|
||||||
|
case picker
|
||||||
|
|
||||||
|
var stableId: ThemeSettingsColorEntryId {
|
||||||
|
switch self {
|
||||||
|
case let .color(index, _, _, _):
|
||||||
|
return .color(index)
|
||||||
|
case .picker:
|
||||||
|
return .picker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: ThemeSettingsColorEntry, rhs: ThemeSettingsColorEntry) -> Bool {
|
||||||
|
switch lhs {
|
||||||
|
case let .color(lhsIndex, lhsThemeReference, lhsAccentColor, lhsSelected):
|
||||||
|
if case let .color(rhsIndex, rhsThemeReference, rhsAccentColor, rhsSelected) = rhs, lhsIndex == rhsIndex, lhsThemeReference.index == rhsThemeReference.index, lhsAccentColor == rhsAccentColor, lhsSelected == rhsSelected {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case .picker:
|
||||||
|
if case .picker = rhs {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func <(lhs: ThemeSettingsColorEntry, rhs: ThemeSettingsColorEntry) -> Bool {
|
||||||
|
switch lhs {
|
||||||
|
case let .color(lhsIndex, _, _, _):
|
||||||
|
switch rhs {
|
||||||
|
case let .color(rhsIndex, _, _, _):
|
||||||
|
return lhsIndex < rhsIndex
|
||||||
|
case .picker:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case .picker:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func item(action: @escaping (PresentationThemeAccentColor?, Bool) -> Void, contextAction: ((PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?, openColorPicker: @escaping (Bool) -> Void) -> ListViewItem {
|
||||||
|
switch self {
|
||||||
|
case let .color(_, themeReference, accentColor, selected):
|
||||||
|
return ThemeSettingsAccentColorIconItem(themeReference: themeReference, accentColor: accentColor, selected: selected, action: action, contextAction: contextAction)
|
||||||
|
case .picker:
|
||||||
|
return ThemeSettingsAccentColorPickerItem(action: openColorPicker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ThemeSettingsAccentColorIconItem: ListViewItem {
|
||||||
|
let themeReference: PresentationThemeReference
|
||||||
|
let accentColor: PresentationThemeAccentColor?
|
||||||
|
let selected: Bool
|
||||||
|
let action: (PresentationThemeAccentColor?, Bool) -> Void
|
||||||
|
let contextAction: ((PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
|
|
||||||
|
public init(themeReference: PresentationThemeReference, accentColor: PresentationThemeAccentColor?, selected: Bool, action: @escaping (PresentationThemeAccentColor?, Bool) -> Void, contextAction: ((PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?) {
|
||||||
|
self.themeReference = themeReference
|
||||||
|
self.accentColor = accentColor
|
||||||
|
self.selected = selected
|
||||||
|
self.action = action
|
||||||
|
self.contextAction = contextAction
|
||||||
|
}
|
||||||
|
|
||||||
|
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||||
|
async {
|
||||||
|
let node = ThemeSettingsAccentColorIconItemNode()
|
||||||
|
let (nodeLayout, apply) = node.asyncLayout()(self, params)
|
||||||
|
node.insets = nodeLayout.insets
|
||||||
|
node.contentSize = nodeLayout.contentSize
|
||||||
|
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
completion(node, {
|
||||||
|
return (nil, { _ in
|
||||||
|
apply(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
assert(node() is ThemeSettingsAccentColorIconItemNode)
|
||||||
|
if let nodeValue = node() as? ThemeSettingsAccentColorIconItemNode {
|
||||||
|
let layout = nodeValue.asyncLayout()
|
||||||
|
async {
|
||||||
|
let (nodeLayout, apply) = layout(self, params)
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
completion(nodeLayout, { _ in
|
||||||
|
apply(animation.isAnimated)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var selectable = true
|
||||||
|
public func selected(listView: ListView) {
|
||||||
|
self.action(self.accentColor, self.selected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func generateRingImage(color: UIColor) -> UIImage? {
|
||||||
return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in
|
return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in
|
||||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
context.clear(bounds)
|
context.clear(bounds)
|
||||||
|
|
||||||
let fillColor = color.color
|
context.setStrokeColor(color.cgColor)
|
||||||
var strokeColor = color.baseColor.color
|
|
||||||
if strokeColor == .clear {
|
|
||||||
strokeColor = fillColor
|
|
||||||
}
|
|
||||||
if strokeColor.distance(to: theme.list.itemBlocksBackgroundColor) < 200 {
|
|
||||||
if strokeColor.distance(to: UIColor.white) < 200 {
|
|
||||||
strokeColor = UIColor(rgb: 0x999999)
|
|
||||||
} else {
|
|
||||||
strokeColor = theme.list.controlSecondaryColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.setFillColor(fillColor.cgColor)
|
|
||||||
context.setStrokeColor(strokeColor.cgColor)
|
|
||||||
context.setLineWidth(2.0)
|
context.setLineWidth(2.0)
|
||||||
|
context.strokeEllipse(in: bounds.insetBy(dx: 1.0, dy: 1.0))
|
||||||
if selected {
|
})
|
||||||
context.fillEllipse(in: bounds.insetBy(dx: 4.0, dy: 4.0))
|
|
||||||
context.strokeEllipse(in: bounds.insetBy(dx: 1.0, dy: 1.0))
|
|
||||||
|
|
||||||
if more {
|
|
||||||
context.setFillColor(UIColor.white.cgColor)
|
|
||||||
let dotSize = CGSize(width: 4.0, height: 4.0)
|
|
||||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: 11.0, y: 18.0), size: dotSize))
|
|
||||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: 18.0, y: 18.0), size: dotSize))
|
|
||||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: 25.0, y: 18.0), size: dotSize))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
context.fillEllipse(in: bounds)
|
|
||||||
|
|
||||||
context.saveGState()
|
|
||||||
context.addEllipse(in: bounds.insetBy(dx: 10.0, dy: 10.0))
|
|
||||||
context.clip()
|
|
||||||
|
|
||||||
var colors: (UIColor, UIColor)?
|
|
||||||
|
|
||||||
if let customColors = bubbles {
|
|
||||||
colors = (customColors.0, customColors.1 ?? customColors.0)
|
|
||||||
} else if case .builtin(.dayClassic) = themeReference {
|
|
||||||
let hsb = color.color.hsb
|
|
||||||
let bubbleColor = UIColor(hue: hsb.0, saturation: (hsb.1 > 0.0 && hsb.2 > 0.0) ? 0.14 : 0.0, brightness: 0.79 + hsb.2 * 0.21, alpha: 1.0)
|
|
||||||
colors = (bubbleColor, bubbleColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let colors = colors {
|
|
||||||
let gradientColors = [colors.0.cgColor, colors.1.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: 10.0), end: CGPoint(x: 0.0, y: size.height - 10.0), options: CGGradientDrawingOptions())
|
|
||||||
}
|
|
||||||
|
|
||||||
context.restoreGState()
|
|
||||||
}
|
|
||||||
})?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 15)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func generateFillImage(color: UIColor) -> UIImage? {
|
||||||
|
return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in
|
||||||
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
|
context.clear(bounds)
|
||||||
|
|
||||||
|
context.setFillColor(color.cgColor)
|
||||||
|
context.fillEllipse(in: bounds.insetBy(dx: 4.0, dy: 4.0))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private func generateCenterImage(topColor: UIColor, bottomColor: UIColor) -> UIImage? {
|
||||||
|
return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in
|
||||||
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
|
context.clear(bounds)
|
||||||
|
|
||||||
|
context.addEllipse(in: bounds.insetBy(dx: 10.0, dy: 10.0))
|
||||||
|
context.clip()
|
||||||
|
|
||||||
|
let gradientColors = [topColor.cgColor, 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: 10.0), end: CGPoint(x: 0.0, y: size.height - 10.0), options: CGGradientDrawingOptions())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private func generateDotsImage() -> UIImage? {
|
||||||
|
return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in
|
||||||
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
|
context.clear(bounds)
|
||||||
|
|
||||||
|
context.setFillColor(UIColor.white.cgColor)
|
||||||
|
let dotSize = CGSize(width: 4.0, height: 4.0)
|
||||||
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: 11.0, y: 18.0), size: dotSize))
|
||||||
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: 18.0, y: 18.0), size: dotSize))
|
||||||
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: 25.0, y: 18.0), size: dotSize))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class ThemeSettingsAccentColorIconItemNode : ListViewItemNode {
|
||||||
|
private let containerNode: ContextControllerSourceNode
|
||||||
|
private let fillNode: ASImageNode
|
||||||
|
private let ringNode: ASImageNode
|
||||||
|
private let centerNode: ASImageNode
|
||||||
|
private let dotsNode: ASImageNode
|
||||||
|
|
||||||
|
var item: ThemeSettingsAccentColorIconItem?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
self.containerNode = ContextControllerSourceNode()
|
||||||
|
|
||||||
|
self.fillNode = ASImageNode()
|
||||||
|
self.fillNode.displaysAsynchronously = false
|
||||||
|
self.fillNode.displayWithoutProcessing = true
|
||||||
|
|
||||||
|
self.ringNode = ASImageNode()
|
||||||
|
self.ringNode.displaysAsynchronously = false
|
||||||
|
self.ringNode.displayWithoutProcessing = true
|
||||||
|
|
||||||
|
self.centerNode = ASImageNode()
|
||||||
|
self.centerNode.displaysAsynchronously = false
|
||||||
|
self.centerNode.displayWithoutProcessing = true
|
||||||
|
|
||||||
|
self.dotsNode = ASImageNode()
|
||||||
|
self.dotsNode.displaysAsynchronously = false
|
||||||
|
self.dotsNode.displayWithoutProcessing = true
|
||||||
|
self.dotsNode.image = generateDotsImage()
|
||||||
|
|
||||||
|
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||||
|
|
||||||
|
self.addSubnode(self.containerNode)
|
||||||
|
self.containerNode.addSubnode(self.fillNode)
|
||||||
|
self.containerNode.addSubnode(self.ringNode)
|
||||||
|
self.containerNode.addSubnode(self.dotsNode)
|
||||||
|
self.containerNode.addSubnode(self.centerNode)
|
||||||
|
|
||||||
|
self.containerNode.activated = { [weak self] gesture in
|
||||||
|
guard let strongSelf = self, let item = strongSelf.item else {
|
||||||
|
gesture.cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
item.contextAction?(item.accentColor, strongSelf.containerNode, gesture)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didLoad() {
|
||||||
|
super.didLoad()
|
||||||
|
|
||||||
|
self.layer.sublayerTransform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSelected(_ selected: Bool, animated: Bool = false) {
|
||||||
|
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate
|
||||||
|
if selected {
|
||||||
|
transition.updateTransformScale(node: self.fillNode, scale: 1.0)
|
||||||
|
transition.updateTransformScale(node: self.centerNode, scale: 0.16)
|
||||||
|
transition.updateAlpha(node: self.centerNode, alpha: 0.0)
|
||||||
|
transition.updateTransformScale(node: self.dotsNode, scale: 1.0)
|
||||||
|
transition.updateAlpha(node: self.dotsNode, alpha: 1.0)
|
||||||
|
} else {
|
||||||
|
transition.updateTransformScale(node: self.fillNode, scale: 1.2)
|
||||||
|
transition.updateTransformScale(node: self.centerNode, scale: 1.0)
|
||||||
|
transition.updateAlpha(node: self.centerNode, alpha: 1.0)
|
||||||
|
transition.updateTransformScale(node: self.dotsNode, scale: 0.7)
|
||||||
|
transition.updateAlpha(node: self.dotsNode, alpha: 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func asyncLayout() -> (ThemeSettingsAccentColorIconItem, ListViewItemLayoutParams) -> (ListViewItemNodeLayout, (Bool) -> Void) {
|
||||||
|
let currentItem = self.item
|
||||||
|
|
||||||
|
return { [weak self] item, params in
|
||||||
|
var updatedAccentColor = false
|
||||||
|
var updatedSelected = false
|
||||||
|
|
||||||
|
if currentItem == nil || currentItem?.accentColor != item.accentColor {
|
||||||
|
updatedAccentColor = true
|
||||||
|
}
|
||||||
|
if currentItem?.selected != item.selected {
|
||||||
|
updatedSelected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: 60.0, height: 58.0), insets: UIEdgeInsets())
|
||||||
|
return (itemLayout, { animated in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.item = item
|
||||||
|
|
||||||
|
if updatedAccentColor {
|
||||||
|
var fillColor = item.accentColor?.color
|
||||||
|
var strokeColor = item.accentColor?.baseColor.color
|
||||||
|
if strokeColor == .clear {
|
||||||
|
strokeColor = fillColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// if strokeColor.distance(to: theme.list.itemBlocksBackgroundColor) < 200 {
|
||||||
|
// if strokeColor.distance(to: UIColor.white) < 200 {
|
||||||
|
// strokeColor = UIColor(rgb: 0x999999)
|
||||||
|
// } else {
|
||||||
|
// strokeColor = theme.list.controlSecondaryColor
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
var topColor: UIColor?
|
||||||
|
var bottomColor: UIColor?
|
||||||
|
|
||||||
|
if let colors = item.accentColor?.plainBubbleColors {
|
||||||
|
topColor = colors.0
|
||||||
|
bottomColor = colors.1
|
||||||
|
} else if case .builtin(.dayClassic) = item.themeReference {
|
||||||
|
if let accentColor = item.accentColor {
|
||||||
|
let hsb = accentColor.color.hsb
|
||||||
|
let bubbleColor = UIColor(hue: hsb.0, saturation: (hsb.1 > 0.0 && hsb.2 > 0.0) ? 0.14 : 0.0, brightness: 0.79 + hsb.2 * 0.21, alpha: 1.0)
|
||||||
|
topColor = bubbleColor
|
||||||
|
bottomColor = bubbleColor
|
||||||
|
} else {
|
||||||
|
fillColor = UIColor(rgb: 0x007ee5)
|
||||||
|
strokeColor = fillColor
|
||||||
|
topColor = UIColor(rgb: 0xe1ffc7)
|
||||||
|
bottomColor = topColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.fillNode.image = generateFillImage(color: fillColor ?? .clear)
|
||||||
|
strongSelf.ringNode.image = generateRingImage(color: strokeColor ?? .clear)
|
||||||
|
strongSelf.centerNode.image = generateCenterImage(topColor: topColor ?? .clear, bottomColor: bottomColor ?? .clear)
|
||||||
|
}
|
||||||
|
|
||||||
|
let center = CGPoint(x: 30.0, y: 29.0)
|
||||||
|
let bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 40.0, height: 40.0))
|
||||||
|
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: itemLayout.contentSize)
|
||||||
|
|
||||||
|
strongSelf.fillNode.position = center
|
||||||
|
strongSelf.ringNode.position = center
|
||||||
|
strongSelf.centerNode.position = center
|
||||||
|
strongSelf.dotsNode.position = center
|
||||||
|
|
||||||
|
strongSelf.fillNode.bounds = bounds
|
||||||
|
strongSelf.ringNode.bounds = bounds
|
||||||
|
strongSelf.centerNode.bounds = bounds
|
||||||
|
strongSelf.dotsNode.bounds = bounds
|
||||||
|
|
||||||
|
if updatedSelected {
|
||||||
|
strongSelf.setSelected(item.selected, animated: currentItem != nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||||
|
super.animateInsertion(currentTimestamp, duration: duration, short: short)
|
||||||
|
|
||||||
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||||
|
super.animateRemoved(currentTimestamp, duration: duration)
|
||||||
|
|
||||||
|
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func animateAdded(_ currentTimestamp: Double, duration: Double) {
|
||||||
|
super.animateAdded(currentTimestamp, duration: duration)
|
||||||
|
|
||||||
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ThemeSettingsAccentColorPickerItem: ListViewItem {
|
||||||
|
let action: (Bool) -> Void
|
||||||
|
|
||||||
|
public init(action: @escaping (Bool) -> Void) {
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||||
|
async {
|
||||||
|
let node = ThemeSettingsAccentColorPickerItemNode()
|
||||||
|
let (nodeLayout, apply) = node.asyncLayout()(self, params)
|
||||||
|
node.insets = nodeLayout.insets
|
||||||
|
node.contentSize = nodeLayout.contentSize
|
||||||
|
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
completion(node, {
|
||||||
|
return (nil, { _ in
|
||||||
|
apply(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
assert(node() is ThemeSettingsAccentColorPickerItemNode)
|
||||||
|
if let nodeValue = node() as? ThemeSettingsAccentColorPickerItemNode {
|
||||||
|
let layout = nodeValue.asyncLayout()
|
||||||
|
async {
|
||||||
|
let (nodeLayout, apply) = layout(self, params)
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
completion(nodeLayout, { _ in
|
||||||
|
apply(animation.isAnimated)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var selectable = true
|
||||||
|
public func selected(listView: ListView) {
|
||||||
|
self.action(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private func generateCustomSwatchImage() -> UIImage? {
|
private func generateCustomSwatchImage() -> UIImage? {
|
||||||
return generateImage(CGSize(width: 42.0, height: 42.0), rotatedContext: { size, context in
|
return generateImage(CGSize(width: 42.0, height: 42.0), rotatedContext: { size, context in
|
||||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
@ -105,9 +423,78 @@ private func generateCustomSwatchImage() -> UIImage? {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class ThemeSettingsAccentColorPickerItemNode : ListViewItemNode {
|
||||||
|
private let imageNode: ASImageNode
|
||||||
|
|
||||||
|
var item: ThemeSettingsAccentColorPickerItem?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
self.imageNode = ASImageNode()
|
||||||
|
self.imageNode.displaysAsynchronously = false
|
||||||
|
self.imageNode.displayWithoutProcessing = true
|
||||||
|
self.imageNode.image = generateCustomSwatchImage()
|
||||||
|
|
||||||
|
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||||
|
|
||||||
|
self.addSubnode(self.imageNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didLoad() {
|
||||||
|
super.didLoad()
|
||||||
|
|
||||||
|
self.layer.sublayerTransform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func asyncLayout() -> (ThemeSettingsAccentColorPickerItem, ListViewItemLayoutParams) -> (ListViewItemNodeLayout, (Bool) -> Void) {
|
||||||
|
let currentItem = self.item
|
||||||
|
|
||||||
|
return { [weak self] item, params in
|
||||||
|
let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: 60.0, height: 60.0), insets: UIEdgeInsets())
|
||||||
|
return (itemLayout, { animated in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.item = item
|
||||||
|
|
||||||
|
strongSelf.imageNode.frame = CGRect(origin: CGPoint(x: 9.0, y: 9.0), size: CGSize(width: 42.0, height: 42.0))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||||
|
super.animateInsertion(currentTimestamp, duration: duration, short: short)
|
||||||
|
|
||||||
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||||
|
super.animateRemoved(currentTimestamp, duration: duration)
|
||||||
|
|
||||||
|
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func animateAdded(_ currentTimestamp: Double, duration: Double) {
|
||||||
|
super.animateAdded(currentTimestamp, duration: duration)
|
||||||
|
|
||||||
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum ThemeSettingsAccentColor {
|
enum ThemeSettingsAccentColor {
|
||||||
case `default`
|
case `default`
|
||||||
case color(PresentationThemeBaseColor)
|
case color(PresentationThemeBaseColor)
|
||||||
|
case preset(PresentationThemeAccentColor)
|
||||||
|
case custom(PresentationThemeAccentColor)
|
||||||
|
|
||||||
|
var index: Int32? {
|
||||||
|
switch self {
|
||||||
|
case .default:
|
||||||
|
return nil
|
||||||
|
case let .color(color):
|
||||||
|
return 10 + color.rawValue
|
||||||
|
case let .preset(color), let .custom(color):
|
||||||
|
return color.index
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ThemeSettingsAccentColorItem: ListViewItem, ItemListItem {
|
class ThemeSettingsAccentColorItem: ListViewItem, ItemListItem {
|
||||||
@ -119,10 +506,10 @@ class ThemeSettingsAccentColorItem: ListViewItem, ItemListItem {
|
|||||||
let currentColor: PresentationThemeAccentColor?
|
let currentColor: PresentationThemeAccentColor?
|
||||||
let updated: (PresentationThemeAccentColor?) -> Void
|
let updated: (PresentationThemeAccentColor?) -> Void
|
||||||
let contextAction: ((PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?
|
let contextAction: ((PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
let openColorPicker: () -> Void
|
let openColorPicker: (Bool) -> Void
|
||||||
let tag: ItemListItemTag?
|
let tag: ItemListItemTag?
|
||||||
|
|
||||||
init(theme: PresentationTheme, sectionId: ItemListSectionId, themeReference: PresentationThemeReference, colors: [ThemeSettingsAccentColor], currentColor: PresentationThemeAccentColor?, updated: @escaping (PresentationThemeAccentColor?) -> Void, contextAction: ((PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?, openColorPicker: @escaping () -> Void, tag: ItemListItemTag? = nil) {
|
init(theme: PresentationTheme, sectionId: ItemListSectionId, themeReference: PresentationThemeReference, colors: [ThemeSettingsAccentColor], currentColor: PresentationThemeAccentColor?, updated: @escaping (PresentationThemeAccentColor?) -> Void, contextAction: ((PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?, openColorPicker: @escaping (Bool) -> Void, tag: ItemListItemTag? = nil) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.themeReference = themeReference
|
self.themeReference = themeReference
|
||||||
self.colors = colors
|
self.colors = colors
|
||||||
@ -168,66 +555,39 @@ class ThemeSettingsAccentColorItem: ListViewItem, ItemListItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ThemeSettingsAccentColorNode : ASDisplayNode {
|
private struct ThemeSettingsAccentColorItemNodeTransition {
|
||||||
private let containerNode: ContextControllerSourceNode
|
let deletions: [ListViewDeleteItem]
|
||||||
private let iconNode: ASImageNode
|
let insertions: [ListViewInsertItem]
|
||||||
private var action: (() -> Void)?
|
let updates: [ListViewUpdateItem]
|
||||||
private var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
let crossfade: Bool
|
||||||
|
|
||||||
override init() {
|
|
||||||
self.containerNode = ContextControllerSourceNode()
|
|
||||||
|
|
||||||
self.iconNode = ASImageNode()
|
|
||||||
self.iconNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 62.0, height: 62.0))
|
|
||||||
self.iconNode.isLayerBacked = true
|
|
||||||
|
|
||||||
super.init()
|
|
||||||
|
|
||||||
self.addSubnode(self.containerNode)
|
|
||||||
self.containerNode.addSubnode(self.iconNode)
|
|
||||||
|
|
||||||
self.containerNode.activated = { [weak self] gesture in
|
|
||||||
guard let strongSelf = self else {
|
|
||||||
gesture.cancel()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
strongSelf.contextAction?(strongSelf.containerNode, gesture)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setup(theme: PresentationTheme, themeReference: PresentationThemeReference, isDefault: Bool, color: PresentationThemeAccentColor, bubbles: (UIColor, UIColor?)?, selected: Bool, more: Bool, action: @escaping () -> Void, contextAction: ((PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?) {
|
|
||||||
self.iconNode.image = generateSwatchImage(theme: theme, themeReference: themeReference, color: color, bubbles: bubbles, selected: selected, more: more)
|
|
||||||
self.action = {
|
|
||||||
action()
|
|
||||||
}
|
|
||||||
self.contextAction = { node, gesture in
|
|
||||||
contextAction?(themeReference, isDefault ? nil : color, node, gesture)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func didLoad() {
|
|
||||||
super.didLoad()
|
|
||||||
|
|
||||||
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
|
||||||
if case .ended = recognizer.state {
|
|
||||||
self.action?()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func layout() {
|
|
||||||
super.layout()
|
|
||||||
|
|
||||||
self.containerNode.frame = self.bounds
|
|
||||||
self.iconNode.frame = self.containerNode.bounds
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func preparedTransition(action: @escaping (PresentationThemeAccentColor?, Bool) -> Void, contextAction: ((PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?, openColorPicker: @escaping (Bool) -> Void, from fromEntries: [ThemeSettingsColorEntry], to toEntries: [ThemeSettingsColorEntry], crossfade: Bool) -> ThemeSettingsAccentColorItemNodeTransition {
|
||||||
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
|
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||||
|
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(action: action, contextAction: contextAction, openColorPicker: openColorPicker), directionHint: .Down) }
|
||||||
|
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(action: action, contextAction: contextAction, openColorPicker: openColorPicker), directionHint: nil) }
|
||||||
|
|
||||||
|
return ThemeSettingsAccentColorItemNodeTransition(deletions: deletions, insertions: insertions, updates: updates, crossfade: crossfade)
|
||||||
|
}
|
||||||
|
|
||||||
private let textFont = Font.regular(11.0)
|
private func ensureColorVisible(listNode: ListView, accentColor: PresentationThemeAccentColor?, animated: Bool) -> Bool {
|
||||||
private let itemSize = Font.regular(11.0)
|
var resultNode: ThemeSettingsAccentColorIconItemNode?
|
||||||
|
listNode.forEachItemNode { node in
|
||||||
|
if resultNode == nil, let node = node as? ThemeSettingsAccentColorIconItemNode {
|
||||||
|
if node.item?.accentColor?.index == accentColor?.index {
|
||||||
|
resultNode = node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let resultNode = resultNode {
|
||||||
|
listNode.ensureItemNodeVisible(resultNode, animated: animated, overflow: 24.0)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
|
class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
|
||||||
private let containerNode: ASDisplayNode
|
private let containerNode: ASDisplayNode
|
||||||
@ -237,9 +597,10 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
private let maskNode: ASImageNode
|
private let maskNode: ASImageNode
|
||||||
private var snapshotView: UIView?
|
private var snapshotView: UIView?
|
||||||
|
|
||||||
private let scrollNode: ASScrollNode
|
private let listNode: ListView
|
||||||
private var colorNodes: [ThemeSettingsAccentColorNode] = []
|
private var entries: [ThemeSettingsColorEntry]?
|
||||||
private let customNode: HighlightableButtonNode
|
private var enqueuedTransitions: [ThemeSettingsAccentColorItemNodeTransition] = []
|
||||||
|
private var initialized = false
|
||||||
|
|
||||||
private var item: ThemeSettingsAccentColorItem?
|
private var item: ThemeSettingsAccentColorItem?
|
||||||
private var layoutParams: ListViewItemLayoutParams?
|
private var layoutParams: ListViewItemLayoutParams?
|
||||||
@ -262,40 +623,53 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
|
|
||||||
self.maskNode = ASImageNode()
|
self.maskNode = ASImageNode()
|
||||||
|
|
||||||
self.scrollNode = ASScrollNode()
|
self.listNode = ListView()
|
||||||
|
self.listNode.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||||
self.customNode = HighlightableButtonNode()
|
|
||||||
|
|
||||||
super.init(layerBacked: false, dynamicBounce: false)
|
super.init(layerBacked: false, dynamicBounce: false)
|
||||||
|
|
||||||
self.addSubnode(self.containerNode)
|
self.addSubnode(self.containerNode)
|
||||||
|
self.addSubnode(self.listNode)
|
||||||
self.customNode.setImage(generateCustomSwatchImage(), for: .normal)
|
|
||||||
self.customNode.addTarget(self, action: #selector(customPressed), forControlEvents: .touchUpInside)
|
|
||||||
|
|
||||||
self.addSubnode(self.scrollNode)
|
|
||||||
self.scrollNode.addSubnode(self.customNode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
super.didLoad()
|
super.didLoad()
|
||||||
self.scrollNode.view.disablesInteractiveTransitionGestureRecognizer = true
|
self.listNode.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||||
self.scrollNode.view.showsHorizontalScrollIndicator = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func customPressed() {
|
private func enqueueTransition(_ transition: ThemeSettingsAccentColorItemNodeTransition) {
|
||||||
self.item?.openColorPicker()
|
self.enqueuedTransitions.append(transition)
|
||||||
}
|
|
||||||
|
|
||||||
private func scrollToNode(_ node: ThemeSettingsAccentColorNode, animated: Bool) {
|
|
||||||
let bounds = self.scrollNode.view.bounds
|
|
||||||
let frame = node.frame.insetBy(dx: -48.0, dy: 0.0)
|
|
||||||
|
|
||||||
if frame.minX < bounds.minX || frame.maxX > bounds.maxX {
|
if let _ = self.item {
|
||||||
self.scrollNode.view.scrollRectToVisible(frame, animated: animated)
|
while !self.enqueuedTransitions.isEmpty {
|
||||||
|
self.dequeueTransition()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func dequeueTransition() {
|
||||||
|
guard let item = self.item, let transition = self.enqueuedTransitions.first else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.enqueuedTransitions.remove(at: 0)
|
||||||
|
|
||||||
|
var options = ListViewDeleteAndInsertOptions()
|
||||||
|
if self.initialized && transition.crossfade {
|
||||||
|
options.insert(.AnimateCrossfade)
|
||||||
|
}
|
||||||
|
|
||||||
|
var scrollToItem: ListViewScrollToItem?
|
||||||
|
if !self.initialized {
|
||||||
|
if let index = item.colors.firstIndex(where: { $0.index == item.currentColor?.index }) {
|
||||||
|
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(-24.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Down)
|
||||||
|
self.initialized = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: scrollToItem, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { _ in
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func asyncLayout() -> (_ item: ThemeSettingsAccentColorItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
func asyncLayout() -> (_ item: ThemeSettingsAccentColorItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||||
let currentItem = self.item
|
let currentItem = self.item
|
||||||
|
|
||||||
@ -320,7 +694,6 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
strongSelf.layoutParams = params
|
strongSelf.layoutParams = params
|
||||||
|
|
||||||
strongSelf.scrollNode.view.contentInset = UIEdgeInsets(top: 0.0, left: params.leftInset, bottom: 0.0, right: params.rightInset)
|
|
||||||
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||||
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||||
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||||
@ -369,83 +742,66 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||||
|
|
||||||
strongSelf.scrollNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layoutSize.width, height: layoutSize.height))
|
var listInsets = UIEdgeInsets()
|
||||||
|
listInsets.top += params.leftInset + 4.0
|
||||||
|
listInsets.bottom += params.rightInset + 4.0
|
||||||
|
|
||||||
let nodeInset: CGFloat = 15.0
|
strongSelf.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: contentSize.height, height: contentSize.width)
|
||||||
let nodeSize = CGSize(width: 40.0, height: 40.0)
|
strongSelf.listNode.position = CGPoint(x: contentSize.width / 2.0, y: contentSize.height / 2.0)
|
||||||
var nodeOffset = nodeInset
|
strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: contentSize.height, height: contentSize.width), insets: listInsets, duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||||
|
|
||||||
var updated = false
|
var entries: [ThemeSettingsColorEntry] = []
|
||||||
var selectedNode: ThemeSettingsAccentColorNode?
|
var index: Int = 0
|
||||||
|
|
||||||
var i = 0
|
|
||||||
for color in item.colors {
|
for color in item.colors {
|
||||||
let imageNode: ThemeSettingsAccentColorNode
|
|
||||||
if strongSelf.colorNodes.count > i {
|
|
||||||
imageNode = strongSelf.colorNodes[i]
|
|
||||||
} else {
|
|
||||||
imageNode = ThemeSettingsAccentColorNode()
|
|
||||||
strongSelf.colorNodes.append(imageNode)
|
|
||||||
strongSelf.scrollNode.addSubnode(imageNode)
|
|
||||||
updated = true
|
|
||||||
}
|
|
||||||
|
|
||||||
let selected: Bool
|
|
||||||
var accentColor: PresentationThemeAccentColor
|
|
||||||
var itemColor: PresentationThemeAccentColor?
|
|
||||||
var isDefault = false
|
|
||||||
switch color {
|
switch color {
|
||||||
case .default:
|
case .default:
|
||||||
selected = item.currentColor == nil
|
let selected = item.currentColor == nil
|
||||||
accentColor = PresentationThemeAccentColor(baseColor: .blue, accentColor: 0x007ee5, bubbleColors: (0xe1ffc7, nil))
|
entries.append(.color(index, item.themeReference, nil, selected))
|
||||||
isDefault = true
|
|
||||||
case let .color(color):
|
case let .color(color):
|
||||||
selected = item.currentColor?.baseColor == color
|
let selected = item.currentColor?.baseColor == color
|
||||||
|
let accentColor: PresentationThemeAccentColor
|
||||||
if let currentColor = item.currentColor, selected {
|
if let currentColor = item.currentColor, selected {
|
||||||
accentColor = currentColor
|
accentColor = currentColor
|
||||||
} else {
|
} else {
|
||||||
accentColor = PresentationThemeAccentColor(baseColor: color)
|
accentColor = PresentationThemeAccentColor(index: 10 + color.rawValue, baseColor: color)
|
||||||
}
|
}
|
||||||
itemColor = accentColor
|
entries.append(.color(index, item.themeReference, accentColor, selected))
|
||||||
|
case let .preset(color), let .custom(color):
|
||||||
|
let selected = item.currentColor == color
|
||||||
|
entries.append(.color(index, item.themeReference, color, selected))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
entries.append(.picker)
|
||||||
|
|
||||||
if selected {
|
let action: (PresentationThemeAccentColor?, Bool) -> Void = { [weak self] color, selected in
|
||||||
selectedNode = imageNode
|
if let strongSelf = self, let item = strongSelf.item {
|
||||||
}
|
|
||||||
|
|
||||||
imageNode.setup(theme: item.theme, themeReference: item.themeReference, isDefault: isDefault, color: accentColor, bubbles: accentColor.customBubbleColors, selected: selected, more: true, action: { [weak self, weak imageNode] in
|
|
||||||
if selected {
|
if selected {
|
||||||
item.openColorPicker()
|
item.openColorPicker(color?.baseColor != .custom)
|
||||||
} else {
|
} else {
|
||||||
item.updated(itemColor)
|
item.updated(color)
|
||||||
}
|
}
|
||||||
if let imageNode = imageNode {
|
ensureColorVisible(listNode: strongSelf.listNode, accentColor: color, animated: true)
|
||||||
self?.scrollToNode(imageNode, animated: true)
|
}
|
||||||
}
|
}
|
||||||
}, contextAction: item.contextAction)
|
let contextAction: ((PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)? = { [weak item] color, node, gesture in
|
||||||
|
if let strongSelf = self, let item = strongSelf.item {
|
||||||
imageNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 10.0), size: nodeSize)
|
item.contextAction?(item.themeReference, color, node, gesture)
|
||||||
nodeOffset += nodeSize.width + 18.0
|
}
|
||||||
|
}
|
||||||
i += 1
|
let openColorPicker: (Bool) -> Void = { [weak self] create in
|
||||||
|
if let strongSelf = self, let item = strongSelf.item {
|
||||||
|
item.openColorPicker(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.customNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 9.0), size: CGSize(width: 42.0, height: 42.0))
|
let previousEntries = strongSelf.entries ?? []
|
||||||
|
let crossfade = themeUpdated || previousEntries.count != entries.count
|
||||||
|
let transition = preparedTransition(action: action, contextAction: contextAction, openColorPicker: openColorPicker, from: previousEntries, to: entries, crossfade: crossfade)
|
||||||
|
strongSelf.enqueueTransition(transition)
|
||||||
|
|
||||||
for k in (i ..< strongSelf.colorNodes.count).reversed() {
|
strongSelf.entries = entries
|
||||||
let node = strongSelf.colorNodes[k]
|
|
||||||
strongSelf.colorNodes.remove(at: k)
|
|
||||||
node.removeFromSupernode()
|
|
||||||
}
|
|
||||||
|
|
||||||
let contentSize = CGSize(width: strongSelf.customNode.frame.maxX + nodeInset, height: strongSelf.scrollNode.frame.height)
|
|
||||||
if strongSelf.scrollNode.view.contentSize != contentSize {
|
|
||||||
strongSelf.scrollNode.view.contentSize = contentSize
|
|
||||||
}
|
|
||||||
|
|
||||||
if updated, let selectedNode = selectedNode {
|
|
||||||
strongSelf.scrollToNode(selectedNode, animated: false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import ItemListUI
|
|||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import AlertUI
|
import AlertUI
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
|
import MediaResources
|
||||||
import WallpaperResources
|
import WallpaperResources
|
||||||
import ShareController
|
import ShareController
|
||||||
import AccountContext
|
import AccountContext
|
||||||
@ -72,7 +73,7 @@ private final class ThemeSettingsControllerArguments {
|
|||||||
let selectFontSize: (PresentationFontSize) -> Void
|
let selectFontSize: (PresentationFontSize) -> Void
|
||||||
let openWallpaperSettings: () -> Void
|
let openWallpaperSettings: () -> Void
|
||||||
let selectAccentColor: (PresentationThemeAccentColor?) -> Void
|
let selectAccentColor: (PresentationThemeAccentColor?) -> Void
|
||||||
let openAccentColorPicker: (PresentationThemeReference) -> Void
|
let openAccentColorPicker: (PresentationThemeReference, Bool) -> Void
|
||||||
let openAutoNightTheme: () -> Void
|
let openAutoNightTheme: () -> Void
|
||||||
let openTextSize: () -> Void
|
let openTextSize: () -> Void
|
||||||
let toggleLargeEmoji: (Bool) -> Void
|
let toggleLargeEmoji: (Bool) -> Void
|
||||||
@ -82,7 +83,7 @@ private final class ThemeSettingsControllerArguments {
|
|||||||
let themeContextAction: (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void
|
let themeContextAction: (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void
|
||||||
let colorContextAction: (PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void
|
let colorContextAction: (PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void
|
||||||
|
|
||||||
init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor?) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference) -> Void, openAutoNightTheme: @escaping () -> Void, openTextSize: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, themeContextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void, colorContextAction: @escaping (PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void) {
|
init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor?) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, Bool) -> Void, openAutoNightTheme: @escaping () -> Void, openTextSize: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, themeContextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void, colorContextAction: @escaping (PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.selectTheme = selectTheme
|
self.selectTheme = selectTheme
|
||||||
self.selectFontSize = selectFontSize
|
self.selectFontSize = selectFontSize
|
||||||
@ -132,10 +133,10 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
case fontSize(PresentationTheme, PresentationFontSize)
|
case fontSize(PresentationTheme, PresentationFontSize)
|
||||||
case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, [ChatPreviewMessageItem])
|
case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, [ChatPreviewMessageItem])
|
||||||
case wallpaper(PresentationTheme, String)
|
case wallpaper(PresentationTheme, String)
|
||||||
case accentColor(PresentationTheme, PresentationThemeReference, PresentationThemeAccentColor?)
|
case accentColor(PresentationTheme, PresentationThemeReference, PresentationThemeCustomColors?, PresentationThemeAccentColor?)
|
||||||
case autoNightTheme(PresentationTheme, String, String)
|
case autoNightTheme(PresentationTheme, String, String)
|
||||||
case textSize(PresentationTheme, String, String)
|
case textSize(PresentationTheme, String, String)
|
||||||
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor], PresentationThemeAccentColor?)
|
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor], [Int64: TelegramWallpaper], PresentationThemeAccentColor?)
|
||||||
case iconHeader(PresentationTheme, String)
|
case iconHeader(PresentationTheme, String)
|
||||||
case iconItem(PresentationTheme, PresentationStrings, [PresentationAppIcon], String?)
|
case iconItem(PresentationTheme, PresentationStrings, [PresentationAppIcon], String?)
|
||||||
case otherHeader(PresentationTheme, String)
|
case otherHeader(PresentationTheme, String)
|
||||||
@ -207,8 +208,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .accentColor(lhsTheme, lhsCurrentTheme, lhsColor):
|
case let .accentColor(lhsTheme, lhsCurrentTheme, lhsCustomColors, lhsColor):
|
||||||
if case let .accentColor(rhsTheme, rhsCurrentTheme, rhsColor) = rhs, lhsTheme === rhsTheme, lhsCurrentTheme == rhsCurrentTheme, lhsColor == rhsColor {
|
if case let .accentColor(rhsTheme, rhsCurrentTheme, rhsCustomColors, rhsColor) = rhs, lhsTheme === rhsTheme, lhsCurrentTheme == rhsCurrentTheme, lhsCustomColors == rhsCustomColors, lhsColor == rhsColor {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -231,8 +232,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .themeItem(lhsTheme, lhsStrings, lhsThemes, lhsCurrentTheme, lhsThemeAccentColors, lhsCurrentColor):
|
case let .themeItem(lhsTheme, lhsStrings, lhsThemes, lhsCurrentTheme, lhsThemeAccentColors, lhsThemeSpecificChatWallpapers, lhsCurrentColor):
|
||||||
if case let .themeItem(rhsTheme, rhsStrings, rhsThemes, rhsCurrentTheme, rhsThemeAccentColors, rhsCurrentColor) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsThemes == rhsThemes, lhsCurrentTheme == rhsCurrentTheme, lhsThemeAccentColors == rhsThemeAccentColors, lhsCurrentColor == rhsCurrentColor {
|
if case let .themeItem(rhsTheme, rhsStrings, rhsThemes, rhsCurrentTheme, rhsThemeAccentColors, rhsThemeSpecificChatWallpapers, rhsCurrentColor) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsThemes == rhsThemes, lhsCurrentTheme == rhsCurrentTheme, lhsThemeAccentColors == rhsThemeAccentColors, lhsThemeSpecificChatWallpapers == rhsThemeSpecificChatWallpapers, lhsCurrentColor == rhsCurrentColor {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -307,14 +308,25 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openWallpaperSettings()
|
arguments.openWallpaperSettings()
|
||||||
})
|
})
|
||||||
case let .accentColor(theme, currentTheme, color):
|
case let .accentColor(theme, currentTheme, customColors, color):
|
||||||
var colorItems: [ThemeSettingsAccentColor] = []
|
var colorItems: [ThemeSettingsAccentColor] = []
|
||||||
var defaultColor: PresentationThemeAccentColor? = PresentationThemeAccentColor(baseColor: .blue)
|
var defaultColor: PresentationThemeAccentColor? = PresentationThemeAccentColor(baseColor: .blue)
|
||||||
var colors = PresentationThemeBaseColor.allCases
|
var colors = PresentationThemeBaseColor.allCases
|
||||||
|
colors = colors.filter { $0 != .custom && $0 != .preset }
|
||||||
if case let .builtin(name) = currentTheme {
|
if case let .builtin(name) = currentTheme {
|
||||||
if name == .dayClassic {
|
if name == .dayClassic {
|
||||||
colorItems.append(.default)
|
colorItems.append(.default)
|
||||||
defaultColor = nil
|
defaultColor = nil
|
||||||
|
|
||||||
|
let createPaper: (String, Int32, Int32?, Int32?, Int32?) -> TelegramWallpaper = { slug, topColor, bottomColor, intensity, rotation in
|
||||||
|
return TelegramWallpaper.file(id: 0, accessHash: 0, isCreator: false, isDefault: true, isPattern: true, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: false, motion: false, color: topColor, bottomColor: bottomColor, intensity: intensity ?? 50, rotation: rotation))
|
||||||
|
}
|
||||||
|
|
||||||
|
colorItems.append(.preset(PresentationThemeAccentColor(index: 101, baseColor: .preset, accentColor: 0x7e5fe5, bubbleColors: (0xf5e2ff, nil), wallpaper: createPaper("nQcFYJe1mFIBAAAAcI95wtIK0fk", 0xfcccf4, 0xae85f0, 54, nil)))) // amethyst dust
|
||||||
|
colorItems.append(.preset(PresentationThemeAccentColor(index: 102, baseColor: .preset, accentColor: 0xff5fa9, bubbleColors: (0xfff4d7, nil), wallpaper: createPaper("51nnTjx8mFIBAAAAaFGJsMIvWkk", 0xf6b594, 0xebf6cd, 46, 45)))) // bubbly
|
||||||
|
colorItems.append(.preset(PresentationThemeAccentColor(index: 103, baseColor: .preset, accentColor: 0x199972, bubbleColors: (0xfffec7, nil), wallpaper: createPaper("fqv01SQemVIBAAAApND8LDRUhRU", 0xc1e7cb, nil, 50, nil)))) // downtown
|
||||||
|
colorItems.append(.preset(PresentationThemeAccentColor(index: 104, baseColor: .preset, accentColor: 0x5a9e29, bubbleColors: (0xdcf8c6, nil), wallpaper: createPaper("R3j69wKskFIBAAAAoUdXWCKMzCM", 0xede6dd, nil, 50, nil)))) // green
|
||||||
|
colorItems.append(.preset(PresentationThemeAccentColor(index: 105, baseColor: .preset, accentColor: 0x009eee, bubbleColors: (0x94fff9, 0xccffc7), wallpaper: createPaper("p-pXcflrmFIBAAAAvXYQk-mCwZU", 0xffbca6, 0xff63bd, 57, 225)))) // blue lolly
|
||||||
}
|
}
|
||||||
if name != .day {
|
if name != .day {
|
||||||
colors = colors.filter { $0 != .black }
|
colors = colors.filter { $0 != .black }
|
||||||
@ -327,18 +339,22 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let currentColor = color ?? defaultColor
|
let currentColor = color ?? defaultColor
|
||||||
if currentColor?.baseColor != .custom {
|
|
||||||
colors = colors.filter { $0 != .custom }
|
|
||||||
}
|
|
||||||
|
|
||||||
colorItems.append(contentsOf: colors.map { .color($0) })
|
colorItems.append(contentsOf: colors.map { .color($0) })
|
||||||
|
|
||||||
|
if let customColors = customColors {
|
||||||
|
colorItems.append(contentsOf: customColors.colors.map { .custom($0) })
|
||||||
|
} else {
|
||||||
|
if let currentColor = currentColor, currentColor.baseColor == .custom {
|
||||||
|
colorItems.append(.custom(currentColor))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ThemeSettingsAccentColorItem(theme: theme, sectionId: self.section, themeReference: currentTheme, colors: colorItems, currentColor: currentColor, updated: { color in
|
return ThemeSettingsAccentColorItem(theme: theme, sectionId: self.section, themeReference: currentTheme, colors: colorItems, currentColor: currentColor, updated: { color in
|
||||||
arguments.selectAccentColor(color)
|
arguments.selectAccentColor(color)
|
||||||
}, contextAction: { theme, color, node, gesture in
|
}, contextAction: { theme, color, node, gesture in
|
||||||
arguments.colorContextAction(theme, color, node, gesture)
|
arguments.colorContextAction(theme, color, node, gesture)
|
||||||
}, openColorPicker: {
|
}, openColorPicker: { create in
|
||||||
arguments.openAccentColorPicker(currentTheme)
|
arguments.openAccentColorPicker(currentTheme, create)
|
||||||
}, tag: ThemeSettingsEntryTag.accentColor)
|
}, tag: ThemeSettingsEntryTag.accentColor)
|
||||||
case let .autoNightTheme(theme, text, value):
|
case let .autoNightTheme(theme, text, value):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: nil, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: nil, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||||
@ -350,8 +366,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
case let .themeListHeader(theme, text):
|
case let .themeListHeader(theme, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors, _):
|
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors, themeSpecificChatWallpapers, _):
|
||||||
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, displayUnsupported: true, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updatedTheme: { theme in
|
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, displayUnsupported: true, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, currentTheme: currentTheme, updatedTheme: { theme in
|
||||||
if case let .cloud(theme) = theme, theme.theme.file == nil {
|
if case let .cloud(theme) = theme, theme.theme.file == nil {
|
||||||
if theme.theme.isCreator {
|
if theme.theme.isCreator {
|
||||||
arguments.editTheme(theme)
|
arguments.editTheme(theme)
|
||||||
@ -392,10 +408,15 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
|
|||||||
entries.append(.themeListHeader(presentationData.theme, title))
|
entries.append(.themeListHeader(presentationData.theme, title))
|
||||||
entries.append(.chatPreview(presentationData.theme, presentationData.theme, presentationData.chatWallpaper, presentationData.fontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (presentationData.strings.Appearance_PreviewReplyAuthor, presentationData.strings.Appearance_PreviewReplyText), text: presentationData.strings.Appearance_PreviewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: presentationData.strings.Appearance_PreviewOutgoingText)]))
|
entries.append(.chatPreview(presentationData.theme, presentationData.theme, presentationData.chatWallpaper, presentationData.fontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (presentationData.strings.Appearance_PreviewReplyAuthor, presentationData.strings.Appearance_PreviewReplyText), text: presentationData.strings.Appearance_PreviewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: presentationData.strings.Appearance_PreviewOutgoingText)]))
|
||||||
|
|
||||||
entries.append(.themeItem(presentationData.theme, presentationData.strings, availableThemes, themeReference, presentationThemeSettings.themeSpecificAccentColors, presentationThemeSettings.themeSpecificAccentColors[themeReference.index]))
|
var wallpaper: TelegramWallpaper?
|
||||||
|
if let accentColor = presentationThemeSettings.themeSpecificAccentColors[themeReference.index] {
|
||||||
|
wallpaper = presentationThemeSettings.themeSpecificChatWallpapers[themeReference.index &+ Int64(accentColor.index)]
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.append(.themeItem(presentationData.theme, presentationData.strings, availableThemes, themeReference, presentationThemeSettings.themeSpecificAccentColors, presentationThemeSettings.themeSpecificChatWallpapers, presentationThemeSettings.themeSpecificAccentColors[themeReference.index]))
|
||||||
|
|
||||||
if case let .builtin(theme) = themeReference {
|
if case let .builtin(theme) = themeReference {
|
||||||
entries.append(.accentColor(presentationData.theme, themeReference, presentationThemeSettings.themeSpecificAccentColors[themeReference.index]))
|
entries.append(.accentColor(presentationData.theme, themeReference, presentationThemeSettings.themeSpecificCustomColors[themeReference.index], presentationThemeSettings.themeSpecificAccentColors[themeReference.index]))
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.append(.wallpaper(presentationData.theme, strings.Settings_ChatBackground))
|
entries.append(.wallpaper(presentationData.theme, strings.Settings_ChatBackground))
|
||||||
@ -476,38 +497,78 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
|
|
||||||
let arguments = ThemeSettingsControllerArguments(context: context, selectTheme: { theme in
|
let arguments = ThemeSettingsControllerArguments(context: context, selectTheme: { theme in
|
||||||
selectThemeImpl?(theme)
|
selectThemeImpl?(theme)
|
||||||
}, selectFontSize: { size in
|
}, selectFontSize: { fontSize in
|
||||||
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: size, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
return current.withUpdatedFontSize(fontSize)
|
||||||
}).start()
|
}).start()
|
||||||
}, openWallpaperSettings: {
|
}, openWallpaperSettings: {
|
||||||
pushControllerImpl?(ThemeGridController(context: context))
|
pushControllerImpl?(ThemeGridController(context: context))
|
||||||
}, selectAccentColor: { color in
|
}, selectAccentColor: { color in
|
||||||
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
var wallpaperSignal: Signal<TelegramWallpaper?, NoError> = .single(nil)
|
||||||
let autoNightModeTriggered = context.sharedContext.currentPresentationData.with { $0 }.autoNightModeTriggered
|
if let colorWallpaper = color?.wallpaper, case let .file(file) = colorWallpaper {
|
||||||
var currentTheme = current.theme
|
wallpaperSignal = cachedWallpaper(account: context.account, slug: file.slug, settings: colorWallpaper.settings)
|
||||||
if autoNightModeTriggered {
|
|> mapToSignal { cachedWallpaper in
|
||||||
currentTheme = current.automaticThemeSwitchSetting.theme
|
if let wallpaper = cachedWallpaper?.wallpaper, case let .file(file) = wallpaper {
|
||||||
}
|
let resource = file.file.resource
|
||||||
|
let representation = CachedPatternWallpaperRepresentation(color: file.settings.color ?? 0xd6e2ee, bottomColor: file.settings.bottomColor, intensity: file.settings.intensity ?? 50, rotation: file.settings.rotation)
|
||||||
guard let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: color?.color) else {
|
|
||||||
return current
|
var data: Data?
|
||||||
}
|
if let path = context.account.postbox.mediaBox.completedResourcePath(resource), let maybeData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) {
|
||||||
|
data = maybeData
|
||||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
} else if let path = context.sharedContext.accountManager.mediaBox.completedResourcePath(resource), let maybeData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) {
|
||||||
var themeSpecificAccentColors = current.themeSpecificAccentColors
|
data = maybeData
|
||||||
themeSpecificAccentColors[currentTheme.index] = color
|
}
|
||||||
|
|
||||||
if case let .builtin(theme) = currentTheme, theme == .dayClassic || theme == .nightAccent {
|
if let data = data {
|
||||||
if let wallpaper = current.themeSpecificChatWallpapers[currentTheme.index], wallpaper.isColorOrGradient || wallpaper.isPattern || wallpaper.isBuiltin {
|
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
|
||||||
themeSpecificChatWallpapers[currentTheme.index] = nil
|
return (context.sharedContext.accountManager.mediaBox.cachedResourceRepresentation(resource, representation: representation, complete: true, fetch: true)
|
||||||
|
|> filter({ $0.complete })
|
||||||
|
|> take(1)
|
||||||
|
|> mapToSignal { _ -> Signal<TelegramWallpaper?, NoError> in
|
||||||
|
return .single(wallpaper)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .single(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
|
||||||
}).start()
|
let _ = (wallpaperSignal
|
||||||
}, openAccentColorPicker: { themeReference in
|
|> deliverOnMainQueue).start(next: { coloredWallpaper in
|
||||||
let controller = ThemeAccentColorController(context: context, mode: .colors(themeReference: themeReference))
|
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||||
|
let autoNightModeTriggered = context.sharedContext.currentPresentationData.with { $0 }.autoNightModeTriggered
|
||||||
|
var currentTheme = current.theme
|
||||||
|
if autoNightModeTriggered {
|
||||||
|
currentTheme = current.automaticThemeSwitchSetting.theme
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: color?.color) else {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
|
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||||
|
var themeSpecificAccentColors = current.themeSpecificAccentColors
|
||||||
|
themeSpecificAccentColors[currentTheme.index] = color
|
||||||
|
|
||||||
|
if case let .builtin(theme) = currentTheme, theme == .dayClassic || theme == .nightAccent {
|
||||||
|
if let wallpaper = coloredWallpaper, let color = color {
|
||||||
|
themeSpecificChatWallpapers[currentTheme.index &+ Int64(color.index)] = wallpaper
|
||||||
|
} else if let wallpaper = current.themeSpecificChatWallpapers[currentTheme.index], wallpaper.isColorOrGradient || wallpaper.isPattern || wallpaper.isBuiltin {
|
||||||
|
themeSpecificChatWallpapers[currentTheme.index] = nil
|
||||||
|
if let color = color {
|
||||||
|
themeSpecificChatWallpapers[currentTheme.index &+ Int64(color.index)] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificCustomColors: current.themeSpecificCustomColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||||
|
}).start()
|
||||||
|
})
|
||||||
|
}, openAccentColorPicker: { themeReference, create in
|
||||||
|
let controller = ThemeAccentColorController(context: context, mode: .colors(themeReference: themeReference, create: create))
|
||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
}, openAutoNightTheme: {
|
}, openAutoNightTheme: {
|
||||||
pushControllerImpl?(themeAutoNightSettingsController(context: context))
|
pushControllerImpl?(themeAutoNightSettingsController(context: context))
|
||||||
@ -520,11 +581,11 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
})
|
})
|
||||||
}, toggleLargeEmoji: { largeEmoji in
|
}, toggleLargeEmoji: { largeEmoji in
|
||||||
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: largeEmoji, disableAnimations: current.disableAnimations)
|
return current.withUpdatedLargeEmoji(largeEmoji)
|
||||||
}).start()
|
}).start()
|
||||||
}, disableAnimations: { value in
|
}, disableAnimations: { disableAnimations in
|
||||||
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: value)
|
return current.withUpdatedDisableAnimations(disableAnimations)
|
||||||
}).start()
|
}).start()
|
||||||
}, selectAppIcon: { name in
|
}, selectAppIcon: { name in
|
||||||
currentAppIconName.set(name)
|
currentAppIconName.set(name)
|
||||||
@ -540,7 +601,15 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
}, themeContextAction: { isCurrent, reference, node, gesture in
|
}, themeContextAction: { isCurrent, reference, node, gesture in
|
||||||
let _ = (context.sharedContext.accountManager.transaction { transaction -> (PresentationThemeAccentColor?, TelegramWallpaper?) in
|
let _ = (context.sharedContext.accountManager.transaction { transaction -> (PresentationThemeAccentColor?, TelegramWallpaper?) in
|
||||||
let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings) as? PresentationThemeSettings ?? PresentationThemeSettings.defaultSettings
|
let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings) as? PresentationThemeSettings ?? PresentationThemeSettings.defaultSettings
|
||||||
return (settings.themeSpecificAccentColors[reference.index], settings.themeSpecificChatWallpapers[reference.index])
|
let accentColor = settings.themeSpecificAccentColors[reference.index]
|
||||||
|
var wallpaper: TelegramWallpaper?
|
||||||
|
if let accentColor = accentColor {
|
||||||
|
wallpaper = settings.themeSpecificChatWallpapers[reference.index &+ Int64(accentColor.index)]
|
||||||
|
}
|
||||||
|
if wallpaper == nil {
|
||||||
|
settings.themeSpecificChatWallpapers[reference.index]
|
||||||
|
}
|
||||||
|
return (accentColor, wallpaper)
|
||||||
} |> map { accentColor, wallpaper in
|
} |> map { accentColor, wallpaper in
|
||||||
return (makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: reference, accentColor: accentColor?.color, bubbleColors: accentColor?.customBubbleColors), wallpaper)
|
return (makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: reference, accentColor: accentColor?.color, bubbleColors: accentColor?.customBubbleColors), wallpaper)
|
||||||
}
|
}
|
||||||
@ -650,7 +719,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
items.append(.action(ContextMenuActionItem(text: strings.Theme_Context_ChangeColors, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor)
|
items.append(.action(ContextMenuActionItem(text: strings.Theme_Context_ChangeColors, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { c, f in
|
}, action: { c, f in
|
||||||
c.dismiss(completion: {
|
c.dismiss(completion: {
|
||||||
let controller = ThemeAccentColorController(context: context, mode: .colors(themeReference: reference))
|
let controller = ThemeAccentColorController(context: context, mode: .colors(themeReference: reference, create: true))
|
||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
@ -667,14 +736,64 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
let strings = presentationData.strings
|
let strings = presentationData.strings
|
||||||
let themeController = ThemePreviewController(context: context, previewTheme: theme, source: .settings(reference, nil))
|
let themeController = ThemePreviewController(context: context, previewTheme: theme, source: .settings(reference, nil))
|
||||||
var items: [ContextMenuItem] = []
|
var items: [ContextMenuItem] = []
|
||||||
|
|
||||||
let removable = accentColor?.accentColor != nil || accentColor?.bubbleColors != nil
|
if let accentColor = accentColor, accentColor.baseColor == .custom {
|
||||||
if removable {
|
|
||||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_RemoveThemeColor, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_RemoveThemeColor, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
|
||||||
}, action: { c, f in
|
}, action: { c, f in
|
||||||
c.dismiss(completion: {
|
c.dismiss(completion: {
|
||||||
let controller = ThemeAccentColorController(context: context, mode: .colors(themeReference: reference))
|
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||||
pushControllerImpl?(controller)
|
var items: [ActionSheetItem] = []
|
||||||
|
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeColorConfirmation, color: .destructive, action: { [weak actionSheet] in
|
||||||
|
actionSheet?.dismissAnimated()
|
||||||
|
|
||||||
|
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||||
|
let themeReference: PresentationThemeReference
|
||||||
|
if presentationData.autoNightModeTriggered {
|
||||||
|
themeReference = current.automaticThemeSwitchSetting.theme
|
||||||
|
} else {
|
||||||
|
themeReference = current.theme
|
||||||
|
}
|
||||||
|
|
||||||
|
var themeSpecificAccentColors = current.themeSpecificAccentColors
|
||||||
|
var themeSpecificCustomColors = current.themeSpecificCustomColors
|
||||||
|
var customColors = themeSpecificCustomColors[themeReference.index]?.colors ?? []
|
||||||
|
|
||||||
|
var updatedAccentColor: PresentationThemeAccentColor
|
||||||
|
if let index = customColors.firstIndex(where: { $0.index == accentColor.index }) {
|
||||||
|
if index > 0 {
|
||||||
|
updatedAccentColor = customColors[index - 1]
|
||||||
|
} else {
|
||||||
|
if case let .builtin(theme) = themeReference {
|
||||||
|
let updatedBaseColor: PresentationThemeBaseColor
|
||||||
|
switch theme {
|
||||||
|
case .dayClassic, .nightAccent:
|
||||||
|
updatedBaseColor = .gray
|
||||||
|
case .day:
|
||||||
|
updatedBaseColor = .black
|
||||||
|
case .night:
|
||||||
|
updatedBaseColor = .white
|
||||||
|
}
|
||||||
|
updatedAccentColor = PresentationThemeAccentColor(baseColor: updatedBaseColor)
|
||||||
|
} else {
|
||||||
|
updatedAccentColor = PresentationThemeAccentColor(baseColor: .blue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customColors.remove(at: index)
|
||||||
|
} else {
|
||||||
|
updatedAccentColor = PresentationThemeAccentColor(baseColor: .blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
themeSpecificAccentColors[themeReference.index] = updatedAccentColor
|
||||||
|
themeSpecificCustomColors[themeReference.index] = PresentationThemeCustomColors(colors: customColors)
|
||||||
|
return current.withUpdatedThemeSpecificCustomColors(themeSpecificCustomColors).withUpdatedThemeSpecificAccentColors(themeSpecificAccentColors)
|
||||||
|
}).start()
|
||||||
|
}))
|
||||||
|
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||||
|
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||||
|
actionSheet?.dismissAnimated()
|
||||||
|
})
|
||||||
|
])])
|
||||||
|
presentControllerImpl?(actionSheet, nil)
|
||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
@ -754,7 +873,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
return controller?.navigationController as? NavigationController
|
return controller?.navigationController as? NavigationController
|
||||||
}
|
}
|
||||||
presentCrossfadeControllerImpl = { [weak controller] in
|
presentCrossfadeControllerImpl = { [weak controller] in
|
||||||
if let controller = controller, controller.isNodeLoaded {
|
if let controller = controller, controller.isNodeLoaded, let navigationController = controller.navigationController as? NavigationController, navigationController.topViewController === controller {
|
||||||
var topOffset: CGFloat?
|
var topOffset: CGFloat?
|
||||||
var bottomOffset: CGFloat?
|
var bottomOffset: CGFloat?
|
||||||
var themeItemNode: ThemeSettingsThemeItemNode?
|
var themeItemNode: ThemeSettingsThemeItemNode?
|
||||||
@ -830,25 +949,14 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
if case let .cloud(info) = theme {
|
if case let .cloud(info) = theme {
|
||||||
updatedTheme = .cloud(PresentationCloudTheme(theme: info.theme, resolvedWallpaper: resolvedWallpaper))
|
updatedTheme = .cloud(PresentationCloudTheme(theme: info.theme, resolvedWallpaper: resolvedWallpaper))
|
||||||
}
|
}
|
||||||
return (context.sharedContext.accountManager.transaction { transaction -> Void in
|
return updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
|
if autoNightModeTriggered {
|
||||||
let current: PresentationThemeSettings
|
var updatedAutomaticThemeSwitchSetting = current.automaticThemeSwitchSetting
|
||||||
if let entry = entry as? PresentationThemeSettings {
|
updatedAutomaticThemeSwitchSetting.theme = updatedTheme
|
||||||
current = entry
|
return current.withUpdatedAutomaticThemeSwitchSetting(updatedAutomaticThemeSwitchSetting)
|
||||||
} else {
|
} else {
|
||||||
current = PresentationThemeSettings.defaultSettings
|
return current.withUpdatedTheme(updatedTheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
var theme = current.theme
|
|
||||||
var automaticThemeSwitchSetting = current.automaticThemeSwitchSetting
|
|
||||||
if autoNightModeTriggered {
|
|
||||||
automaticThemeSwitchSetting.theme = updatedTheme
|
|
||||||
} else {
|
|
||||||
theme = updatedTheme
|
|
||||||
}
|
|
||||||
|
|
||||||
return PresentationThemeSettings(theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ private struct ThemeSettingsThemeEntry: Comparable, Identifiable {
|
|||||||
let accentColor: PresentationThemeAccentColor?
|
let accentColor: PresentationThemeAccentColor?
|
||||||
var selected: Bool
|
var selected: Bool
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
|
let wallpaper: TelegramWallpaper?
|
||||||
|
|
||||||
var stableId: Int64 {
|
var stableId: Int64 {
|
||||||
return self.themeReference.index
|
return self.themeReference.index
|
||||||
@ -47,6 +48,9 @@ private struct ThemeSettingsThemeEntry: Comparable, Identifiable {
|
|||||||
if lhs.theme !== rhs.theme {
|
if lhs.theme !== rhs.theme {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.wallpaper != rhs.wallpaper {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +59,7 @@ private struct ThemeSettingsThemeEntry: Comparable, Identifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func item(context: AccountContext, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem {
|
func item(context: AccountContext, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem {
|
||||||
return ThemeSettingsThemeIconItem(context: context, themeReference: self.themeReference, accentColor: self.accentColor, selected: self.selected, title: self.title, theme: self.theme, action: action, contextAction: contextAction)
|
return ThemeSettingsThemeIconItem(context: context, themeReference: self.themeReference, accentColor: self.accentColor, selected: self.selected, title: self.title, theme: self.theme, wallpaper: self.wallpaper, action: action, contextAction: contextAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,16 +71,18 @@ private class ThemeSettingsThemeIconItem: ListViewItem {
|
|||||||
let selected: Bool
|
let selected: Bool
|
||||||
let title: String
|
let title: String
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
|
let wallpaper: TelegramWallpaper?
|
||||||
let action: (PresentationThemeReference) -> Void
|
let action: (PresentationThemeReference) -> Void
|
||||||
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
|
|
||||||
public init(context: AccountContext, themeReference: PresentationThemeReference, accentColor: PresentationThemeAccentColor?, selected: Bool, title: String, theme: PresentationTheme, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) {
|
public init(context: AccountContext, themeReference: PresentationThemeReference, accentColor: PresentationThemeAccentColor?, selected: Bool, title: String, theme: PresentationTheme, wallpaper: TelegramWallpaper?, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.themeReference = themeReference
|
self.themeReference = themeReference
|
||||||
self.accentColor = accentColor
|
self.accentColor = accentColor
|
||||||
self.selected = selected
|
self.selected = selected
|
||||||
self.title = title
|
self.title = title
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
self.wallpaper = wallpaper
|
||||||
self.action = action
|
self.action = action
|
||||||
self.contextAction = contextAction
|
self.contextAction = contextAction
|
||||||
}
|
}
|
||||||
@ -248,7 +254,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
|||||||
if currentItem?.themeReference != item.themeReference {
|
if currentItem?.themeReference != item.themeReference {
|
||||||
updatedThemeReference = true
|
updatedThemeReference = true
|
||||||
}
|
}
|
||||||
if currentItem?.accentColor != item.accentColor {
|
if currentItem == nil || currentItem?.accentColor != item.accentColor {
|
||||||
updatedAccentColor = true
|
updatedAccentColor = true
|
||||||
}
|
}
|
||||||
if currentItem?.theme !== item.theme {
|
if currentItem?.theme !== item.theme {
|
||||||
@ -273,7 +279,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
|||||||
strongSelf.containerNode.isGestureEnabled = false
|
strongSelf.containerNode.isGestureEnabled = false
|
||||||
} else {
|
} else {
|
||||||
if updatedThemeReference || updatedAccentColor {
|
if updatedThemeReference || updatedAccentColor {
|
||||||
strongSelf.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: item.themeReference, accentColor: item.accentColor?.color, bubbleColors: item.accentColor?.plainBubbleColors))
|
strongSelf.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: item.themeReference, color: item.accentColor, wallpaper: item.wallpaper))
|
||||||
}
|
}
|
||||||
strongSelf.containerNode.isGestureEnabled = true
|
strongSelf.containerNode.isGestureEnabled = true
|
||||||
}
|
}
|
||||||
@ -325,18 +331,20 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
|
|||||||
let themes: [PresentationThemeReference]
|
let themes: [PresentationThemeReference]
|
||||||
let displayUnsupported: Bool
|
let displayUnsupported: Bool
|
||||||
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||||
|
let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
||||||
let currentTheme: PresentationThemeReference
|
let currentTheme: PresentationThemeReference
|
||||||
let updatedTheme: (PresentationThemeReference) -> Void
|
let updatedTheme: (PresentationThemeReference) -> Void
|
||||||
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
let tag: ItemListItemTag?
|
let tag: ItemListItemTag?
|
||||||
|
|
||||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], displayUnsupported: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
|
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], displayUnsupported: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.themes = themes
|
self.themes = themes
|
||||||
self.displayUnsupported = displayUnsupported
|
self.displayUnsupported = displayUnsupported
|
||||||
self.themeSpecificAccentColors = themeSpecificAccentColors
|
self.themeSpecificAccentColors = themeSpecificAccentColors
|
||||||
|
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
|
||||||
self.currentTheme = currentTheme
|
self.currentTheme = currentTheme
|
||||||
self.updatedTheme = updatedTheme
|
self.updatedTheme = updatedTheme
|
||||||
self.contextAction = contextAction
|
self.contextAction = contextAction
|
||||||
@ -382,16 +390,17 @@ private struct ThemeSettingsThemeItemNodeTransition {
|
|||||||
let deletions: [ListViewDeleteItem]
|
let deletions: [ListViewDeleteItem]
|
||||||
let insertions: [ListViewInsertItem]
|
let insertions: [ListViewInsertItem]
|
||||||
let updates: [ListViewUpdateItem]
|
let updates: [ListViewUpdateItem]
|
||||||
|
let crossfade: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preparedTransition(context: AccountContext, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, from fromEntries: [ThemeSettingsThemeEntry], to toEntries: [ThemeSettingsThemeEntry]) -> ThemeSettingsThemeItemNodeTransition {
|
private func preparedTransition(context: AccountContext, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, from fromEntries: [ThemeSettingsThemeEntry], to toEntries: [ThemeSettingsThemeEntry], crossfade: Bool) -> ThemeSettingsThemeItemNodeTransition {
|
||||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, action: action, contextAction: contextAction), directionHint: .Down) }
|
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, action: action, contextAction: contextAction), directionHint: .Down) }
|
||||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, action: action, contextAction: contextAction), directionHint: nil) }
|
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, action: action, contextAction: contextAction), directionHint: nil) }
|
||||||
|
|
||||||
return ThemeSettingsThemeItemNodeTransition(deletions: deletions, insertions: insertions, updates: updates)
|
return ThemeSettingsThemeItemNodeTransition(deletions: deletions, insertions: insertions, updates: updates, crossfade: crossfade)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func ensureThemeVisible(listNode: ListView, themeReference: PresentationThemeReference, animated: Bool) -> Bool {
|
private func ensureThemeVisible(listNode: ListView, themeReference: PresentationThemeReference, animated: Bool) -> Bool {
|
||||||
@ -476,8 +485,8 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
self.enqueuedTransitions.remove(at: 0)
|
self.enqueuedTransitions.remove(at: 0)
|
||||||
|
|
||||||
var options = ListViewDeleteAndInsertOptions()
|
var options = ListViewDeleteAndInsertOptions()
|
||||||
if self.initialized {
|
if self.initialized && transition.crossfade {
|
||||||
options.insert(.AnimateInsertion)
|
options.insert(.AnimateCrossfade)
|
||||||
}
|
}
|
||||||
|
|
||||||
var scrollToItem: ListViewScrollToItem?
|
var scrollToItem: ListViewScrollToItem?
|
||||||
@ -488,7 +497,8 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: scrollToItem, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { _ in })
|
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: scrollToItem, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { _ in
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func asyncLayout() -> (_ item: ThemeSettingsThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
func asyncLayout() -> (_ item: ThemeSettingsThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||||
@ -576,7 +586,11 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
}
|
}
|
||||||
let title = themeDisplayName(strings: item.strings, reference: theme)
|
let title = themeDisplayName(strings: item.strings, reference: theme)
|
||||||
let accentColor = item.themeSpecificAccentColors[theme.index]
|
let accentColor = item.themeSpecificAccentColors[theme.index]
|
||||||
entries.append(ThemeSettingsThemeEntry(index: index, themeReference: theme, title: title, accentColor: accentColor, selected: item.currentTheme.index == theme.index, theme: item.theme))
|
var wallpaper: TelegramWallpaper?
|
||||||
|
if let accentColor = accentColor {
|
||||||
|
wallpaper = item.themeSpecificChatWallpapers[theme.index &+ Int64(accentColor.index)]
|
||||||
|
}
|
||||||
|
entries.append(ThemeSettingsThemeEntry(index: index, themeReference: theme, title: title, accentColor: accentColor, selected: item.currentTheme.index == theme.index, theme: item.theme, wallpaper: wallpaper))
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -586,7 +600,9 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
ensureThemeVisible(listNode: strongSelf.listNode, themeReference: themeReference, animated: true)
|
ensureThemeVisible(listNode: strongSelf.listNode, themeReference: themeReference, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let transition = preparedTransition(context: item.context, action: action, contextAction: item.contextAction, from: strongSelf.entries ?? [], to: entries)
|
let previousEntries = strongSelf.entries ?? []
|
||||||
|
let crossfade = previousEntries.count != entries.count
|
||||||
|
let transition = preparedTransition(context: item.context, action: action, contextAction: item.contextAction, from: previousEntries, to: entries, crossfade: crossfade)
|
||||||
strongSelf.enqueueTransition(transition)
|
strongSelf.enqueueTransition(transition)
|
||||||
|
|
||||||
strongSelf.entries = entries
|
strongSelf.entries = entries
|
||||||
|
|||||||
@ -334,6 +334,7 @@ struct WallpaperColorPanelNodeState {
|
|||||||
var secondColor: UIColor?
|
var secondColor: UIColor?
|
||||||
var secondColorAvailable: Bool
|
var secondColorAvailable: Bool
|
||||||
var rotateAvailable: Bool
|
var rotateAvailable: Bool
|
||||||
|
var rotation: Int32
|
||||||
var preview: Bool
|
var preview: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,7 +386,7 @@ final class WallpaperColorPanelNode: ASDisplayNode {
|
|||||||
self.firstColorFieldNode = ColorInputFieldNode(theme: theme)
|
self.firstColorFieldNode = ColorInputFieldNode(theme: theme)
|
||||||
self.secondColorFieldNode = ColorInputFieldNode(theme: theme)
|
self.secondColorFieldNode = ColorInputFieldNode(theme: theme)
|
||||||
|
|
||||||
self.state = WallpaperColorPanelNodeState(selection: .first, firstColor: nil, secondColor: nil, secondColorAvailable: false, rotateAvailable: false, preview: false)
|
self.state = WallpaperColorPanelNodeState(selection: .first, firstColor: nil, secondColor: nil, secondColorAvailable: false, rotateAvailable: false, rotation: 0, preview: false)
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -528,6 +529,7 @@ final class WallpaperColorPanelNode: ASDisplayNode {
|
|||||||
let previousFirstColor = self.state.firstColor
|
let previousFirstColor = self.state.firstColor
|
||||||
let previousSecondColor = self.state.secondColor
|
let previousSecondColor = self.state.secondColor
|
||||||
let previousPreview = self.state.preview
|
let previousPreview = self.state.preview
|
||||||
|
let previousRotation = self.state.rotation
|
||||||
self.state = f(self.state)
|
self.state = f(self.state)
|
||||||
|
|
||||||
let firstColor: UIColor
|
let firstColor: UIColor
|
||||||
@ -556,7 +558,7 @@ final class WallpaperColorPanelNode: ASDisplayNode {
|
|||||||
if firstColorWasRemovable != self.firstColorFieldNode.isRemovable {
|
if firstColorWasRemovable != self.firstColorFieldNode.isRemovable {
|
||||||
updateLayout = true
|
updateLayout = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if updateLayout, let size = self.validLayout {
|
if updateLayout, let size = self.validLayout {
|
||||||
switch self.state.selection {
|
switch self.state.selection {
|
||||||
case .first:
|
case .first:
|
||||||
@ -694,6 +696,17 @@ final class WallpaperColorPanelNode: ASDisplayNode {
|
|||||||
transition.updateAlpha(node: self.swapButton, alpha: swapButtonAlpha)
|
transition.updateAlpha(node: self.swapButton, alpha: swapButtonAlpha)
|
||||||
transition.updateAlpha(node: self.addButton, alpha: addButtonAlpha)
|
transition.updateAlpha(node: self.addButton, alpha: addButtonAlpha)
|
||||||
|
|
||||||
|
func degreesToRadians(_ degrees: CGFloat) -> CGFloat
|
||||||
|
{
|
||||||
|
var degrees = degrees
|
||||||
|
if degrees >= 270.0 {
|
||||||
|
degrees = degrees - 360.0
|
||||||
|
}
|
||||||
|
return degrees * CGFloat.pi / 180.0
|
||||||
|
}
|
||||||
|
|
||||||
|
transition.updateTransformRotation(node: self.rotateButton, angle: degreesToRadians(CGFloat(self.state.rotation)), beginWithCurrentState: true, completion: nil)
|
||||||
|
|
||||||
self.firstColorFieldNode.isRemovable = self.state.secondColor != nil || (self.state.defaultColor != nil && self.state.firstColor != nil)
|
self.firstColorFieldNode.isRemovable = self.state.secondColor != nil || (self.state.defaultColor != nil && self.state.firstColor != nil)
|
||||||
self.secondColorFieldNode.isRemovable = true
|
self.secondColorFieldNode.isRemovable = true
|
||||||
|
|
||||||
@ -715,6 +728,15 @@ final class WallpaperColorPanelNode: ASDisplayNode {
|
|||||||
|
|
||||||
@objc private func rotatePressed() {
|
@objc private func rotatePressed() {
|
||||||
self.rotate?()
|
self.rotate?()
|
||||||
|
self.updateState({ current in
|
||||||
|
var updated = current
|
||||||
|
var newRotation = updated.rotation + 45
|
||||||
|
if newRotation >= 360 {
|
||||||
|
newRotation = 0
|
||||||
|
}
|
||||||
|
updated.rotation = newRotation
|
||||||
|
return updated
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func swapPressed() {
|
@objc private func swapPressed() {
|
||||||
|
|||||||
@ -374,12 +374,15 @@ public class WallpaperGalleryController: ViewController {
|
|||||||
let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in
|
let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in
|
||||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||||
var wallpaper = wallpaper.isBasicallyEqual(to: strongSelf.presentationData.theme.chat.defaultWallpaper) ? nil : wallpaper
|
var wallpaper = wallpaper.isBasicallyEqual(to: strongSelf.presentationData.theme.chat.defaultWallpaper) ? nil : wallpaper
|
||||||
|
let themeReference: PresentationThemeReference
|
||||||
if autoNightModeTriggered {
|
if autoNightModeTriggered {
|
||||||
themeSpecificChatWallpapers[current.automaticThemeSwitchSetting.theme.index] = wallpaper
|
themeReference = current.automaticThemeSwitchSetting.theme
|
||||||
} else {
|
} else {
|
||||||
themeSpecificChatWallpapers[current.theme.index] = wallpaper
|
themeReference = current.theme
|
||||||
}
|
}
|
||||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
let accentColorIndex = current.themeSpecificAccentColors[themeReference.index]?.index ?? 0
|
||||||
|
themeSpecificChatWallpapers[themeReference.index &+ Int64(accentColorIndex)] = wallpaper
|
||||||
|
return current.withUpdatedThemeSpecificChatWallpapers(themeSpecificChatWallpapers)
|
||||||
}) |> deliverOnMainQueue).start(completed: {
|
}) |> deliverOnMainQueue).start(completed: {
|
||||||
self?.dismiss(forceAway: true)
|
self?.dismiss(forceAway: true)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -190,6 +190,7 @@ public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, edit
|
|||||||
|
|
||||||
return PresentationTheme(
|
return PresentationTheme(
|
||||||
name: theme.name,
|
name: theme.name,
|
||||||
|
index: theme.index,
|
||||||
referenceTheme: theme.referenceTheme,
|
referenceTheme: theme.referenceTheme,
|
||||||
overallDarkAppearance: theme.overallDarkAppearance,
|
overallDarkAppearance: theme.overallDarkAppearance,
|
||||||
intro: intro,
|
intro: intro,
|
||||||
@ -515,6 +516,7 @@ public func makeDefaultDarkPresentationTheme(preview: Bool) -> PresentationTheme
|
|||||||
|
|
||||||
return PresentationTheme(
|
return PresentationTheme(
|
||||||
name: .builtin(.night),
|
name: .builtin(.night),
|
||||||
|
index: PresentationThemeReference.builtin(.night).index,
|
||||||
referenceTheme: .night,
|
referenceTheme: .night,
|
||||||
overallDarkAppearance: true,
|
overallDarkAppearance: true,
|
||||||
intro: intro,
|
intro: intro,
|
||||||
|
|||||||
@ -421,6 +421,7 @@ public func customizeDefaultDarkTintedPresentationTheme(theme: PresentationTheme
|
|||||||
|
|
||||||
return PresentationTheme(
|
return PresentationTheme(
|
||||||
name: theme.name,
|
name: theme.name,
|
||||||
|
index: theme.index,
|
||||||
referenceTheme: theme.referenceTheme,
|
referenceTheme: theme.referenceTheme,
|
||||||
overallDarkAppearance: theme.overallDarkAppearance,
|
overallDarkAppearance: theme.overallDarkAppearance,
|
||||||
intro: intro,
|
intro: intro,
|
||||||
@ -782,6 +783,7 @@ public func makeDefaultDarkTintedPresentationTheme(preview: Bool) -> Presentatio
|
|||||||
|
|
||||||
return PresentationTheme(
|
return PresentationTheme(
|
||||||
name: .builtin(.nightAccent),
|
name: .builtin(.nightAccent),
|
||||||
|
index: PresentationThemeReference.builtin(.nightAccent).index,
|
||||||
referenceTheme: .nightAccent,
|
referenceTheme: .nightAccent,
|
||||||
overallDarkAppearance: true,
|
overallDarkAppearance: true,
|
||||||
intro: intro,
|
intro: intro,
|
||||||
|
|||||||
@ -263,6 +263,7 @@ public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, ac
|
|||||||
|
|
||||||
return PresentationTheme(
|
return PresentationTheme(
|
||||||
name: theme.name,
|
name: theme.name,
|
||||||
|
index: theme.index,
|
||||||
referenceTheme: theme.referenceTheme,
|
referenceTheme: theme.referenceTheme,
|
||||||
overallDarkAppearance: theme.overallDarkAppearance,
|
overallDarkAppearance: theme.overallDarkAppearance,
|
||||||
intro: intro,
|
intro: intro,
|
||||||
@ -705,6 +706,7 @@ public func makeDefaultDayPresentationTheme(serviceBackgroundColor: UIColor?, da
|
|||||||
|
|
||||||
return PresentationTheme(
|
return PresentationTheme(
|
||||||
name: .builtin(day ? .day : .dayClassic),
|
name: .builtin(day ? .day : .dayClassic),
|
||||||
|
index: PresentationThemeReference.builtin(day ? .day : .dayClassic).index,
|
||||||
referenceTheme: day ? .day : .dayClassic,
|
referenceTheme: day ? .day : .dayClassic,
|
||||||
overallDarkAppearance: false,
|
overallDarkAppearance: false,
|
||||||
intro: intro,
|
intro: intro,
|
||||||
|
|||||||
@ -42,13 +42,13 @@ public func makePresentationTheme(mediaBox: MediaBox, themeReference: Presentati
|
|||||||
let defaultTheme = makeDefaultPresentationTheme(reference: reference, serviceBackgroundColor: serviceBackgroundColor, preview: preview)
|
let defaultTheme = makeDefaultPresentationTheme(reference: reference, serviceBackgroundColor: serviceBackgroundColor, preview: preview)
|
||||||
theme = customizePresentationTheme(defaultTheme, editing: true, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, wallpaper: wallpaper)
|
theme = customizePresentationTheme(defaultTheme, editing: true, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, wallpaper: wallpaper)
|
||||||
case let .local(info):
|
case let .local(info):
|
||||||
if let path = mediaBox.completedResourcePath(info.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data, resolvedWallpaper: info.resolvedWallpaper) {
|
if let path = mediaBox.completedResourcePath(info.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data, themeReference: themeReference, resolvedWallpaper: info.resolvedWallpaper) {
|
||||||
theme = customizePresentationTheme(loadedTheme, editing: false, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, wallpaper: wallpaper)
|
theme = customizePresentationTheme(loadedTheme, editing: false, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, wallpaper: wallpaper)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case let .cloud(info):
|
case let .cloud(info):
|
||||||
if let file = info.theme.file, let path = mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data, resolvedWallpaper: info.resolvedWallpaper) {
|
if let file = info.theme.file, let path = mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data, themeReference: themeReference, resolvedWallpaper: info.resolvedWallpaper) {
|
||||||
theme = customizePresentationTheme(loadedTheme, editing: false, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, wallpaper: wallpaper)
|
theme = customizePresentationTheme(loadedTheme, editing: false, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, wallpaper: wallpaper)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -239,11 +239,10 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager, s
|
|||||||
autoNightModeTriggered = false
|
autoNightModeTriggered = false
|
||||||
}
|
}
|
||||||
|
|
||||||
let effectiveAccentColor = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.color
|
let effectiveColors = themeSettings.themeSpecificAccentColors[effectiveTheme.index]
|
||||||
let effectiveBubbleColors = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.customBubbleColors
|
let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveColors?.color, bubbleColors: effectiveColors?.customBubbleColors) ?? defaultPresentationTheme
|
||||||
|
|
||||||
let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, bubbleColors: effectiveBubbleColors) ?? defaultPresentationTheme
|
let effectiveChatWallpaper: TelegramWallpaper = (themeSettings.themeSpecificChatWallpapers[effectiveTheme.index &+ Int64(effectiveColors?.index ?? 0)] ?? themeSettings.themeSpecificChatWallpapers[effectiveTheme.index]) ?? theme.chat.defaultWallpaper
|
||||||
let effectiveChatWallpaper: TelegramWallpaper = themeSettings.themeSpecificChatWallpapers[effectiveTheme.index] ?? theme.chat.defaultWallpaper
|
|
||||||
|
|
||||||
let dateTimeFormat = currentDateTimeFormat()
|
let dateTimeFormat = currentDateTimeFormat()
|
||||||
let stringsValue: PresentationStrings
|
let stringsValue: PresentationStrings
|
||||||
@ -507,13 +506,14 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI
|
|||||||
|
|
||||||
let contactSettings: ContactSynchronizationSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.contactSynchronizationSettings] as? ContactSynchronizationSettings ?? ContactSynchronizationSettings.defaultSettings
|
let contactSettings: ContactSynchronizationSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.contactSynchronizationSettings] as? ContactSynchronizationSettings ?? ContactSynchronizationSettings.defaultSettings
|
||||||
|
|
||||||
|
let effectiveColors = themeSettings.themeSpecificAccentColors[themeSettings.theme.index]
|
||||||
|
let themeSpecificWallpaper = (themeSettings.themeSpecificChatWallpapers[themeSettings.theme.index &+ Int64(effectiveColors?.index ?? 0)] ?? themeSettings.themeSpecificChatWallpapers[themeSettings.theme.index])
|
||||||
|
|
||||||
let currentWallpaper: TelegramWallpaper
|
let currentWallpaper: TelegramWallpaper
|
||||||
if let themeSpecificWallpaper = themeSettings.themeSpecificChatWallpapers[themeSettings.theme.index] {
|
if let themeSpecificWallpaper = themeSpecificWallpaper {
|
||||||
currentWallpaper = themeSpecificWallpaper
|
currentWallpaper = themeSpecificWallpaper
|
||||||
} else {
|
} else {
|
||||||
let effectiveAccentColor = themeSettings.themeSpecificAccentColors[themeSettings.theme.index]?.color
|
let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: themeSettings.theme, accentColor: effectiveColors?.color, bubbleColors: effectiveColors?.customBubbleColors) ?? defaultPresentationTheme
|
||||||
let effectiveBubbleColors = themeSettings.themeSpecificAccentColors[themeSettings.theme.index]?.customBubbleColors
|
|
||||||
let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: themeSettings.theme, accentColor: effectiveAccentColor, bubbleColors: effectiveBubbleColors) ?? defaultPresentationTheme
|
|
||||||
currentWallpaper = theme.chat.defaultWallpaper
|
currentWallpaper = theme.chat.defaultWallpaper
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,22 +529,25 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI
|
|||||||
var effectiveTheme: PresentationThemeReference
|
var effectiveTheme: PresentationThemeReference
|
||||||
var effectiveChatWallpaper: TelegramWallpaper = currentWallpaper
|
var effectiveChatWallpaper: TelegramWallpaper = currentWallpaper
|
||||||
|
|
||||||
|
var switchedToNightModeWallpaper = false
|
||||||
if autoNightModeTriggered {
|
if autoNightModeTriggered {
|
||||||
let automaticTheme = themeSettings.automaticThemeSwitchSetting.theme
|
let automaticTheme = themeSettings.automaticThemeSwitchSetting.theme
|
||||||
if let themeSpecificWallpaper = themeSettings.themeSpecificChatWallpapers[automaticTheme.index] {
|
let effectiveColors = themeSettings.themeSpecificAccentColors[automaticTheme.index]
|
||||||
|
let themeSpecificWallpaper = (themeSettings.themeSpecificChatWallpapers[automaticTheme.index &+ Int64(effectiveColors?.index ?? 0)] ?? themeSettings.themeSpecificChatWallpapers[automaticTheme.index])
|
||||||
|
|
||||||
|
if let themeSpecificWallpaper = themeSpecificWallpaper {
|
||||||
effectiveChatWallpaper = themeSpecificWallpaper
|
effectiveChatWallpaper = themeSpecificWallpaper
|
||||||
|
switchedToNightModeWallpaper = true
|
||||||
}
|
}
|
||||||
effectiveTheme = automaticTheme
|
effectiveTheme = automaticTheme
|
||||||
} else {
|
} else {
|
||||||
effectiveTheme = themeSettings.theme
|
effectiveTheme = themeSettings.theme
|
||||||
}
|
}
|
||||||
|
|
||||||
let effectiveAccentColor = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.color
|
let effectiveColors = themeSettings.themeSpecificAccentColors[effectiveTheme.index]
|
||||||
let effectiveBubbleColors = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.customBubbleColors
|
let themeValue = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveColors?.color, bubbleColors: effectiveColors?.customBubbleColors, serviceBackgroundColor: serviceBackgroundColor) ?? defaultPresentationTheme
|
||||||
|
|
||||||
let themeValue = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, bubbleColors: effectiveBubbleColors, serviceBackgroundColor: serviceBackgroundColor) ?? defaultPresentationTheme
|
if autoNightModeTriggered && !switchedToNightModeWallpaper {
|
||||||
|
|
||||||
if effectiveTheme != themeSettings.theme && themeSettings.themeSpecificChatWallpapers[effectiveTheme.index] == nil {
|
|
||||||
switch effectiveChatWallpaper {
|
switch effectiveChatWallpaper {
|
||||||
case .builtin, .color, .gradient:
|
case .builtin, .color, .gradient:
|
||||||
effectiveChatWallpaper = themeValue.chat.defaultWallpaper
|
effectiveChatWallpaper = themeValue.chat.defaultWallpaper
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1105,6 +1105,7 @@ public enum PresentationThemeName: Equatable {
|
|||||||
|
|
||||||
public final class PresentationTheme: Equatable {
|
public final class PresentationTheme: Equatable {
|
||||||
public let name: PresentationThemeName
|
public let name: PresentationThemeName
|
||||||
|
public let index: Int64
|
||||||
public let referenceTheme: PresentationBuiltinThemeReference
|
public let referenceTheme: PresentationBuiltinThemeReference
|
||||||
public let overallDarkAppearance: Bool
|
public let overallDarkAppearance: Bool
|
||||||
public let intro: PresentationThemeIntro
|
public let intro: PresentationThemeIntro
|
||||||
@ -1120,8 +1121,9 @@ public final class PresentationTheme: Equatable {
|
|||||||
|
|
||||||
public let resourceCache: PresentationsResourceCache = PresentationsResourceCache()
|
public let resourceCache: PresentationsResourceCache = PresentationsResourceCache()
|
||||||
|
|
||||||
public init(name: PresentationThemeName, referenceTheme: PresentationBuiltinThemeReference, overallDarkAppearance: Bool, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, contextMenu: PresentationThemeContextMenu, inAppNotification: PresentationThemeInAppNotification, preview: Bool = false) {
|
public init(name: PresentationThemeName, index: Int64, referenceTheme: PresentationBuiltinThemeReference, overallDarkAppearance: Bool, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, contextMenu: PresentationThemeContextMenu, inAppNotification: PresentationThemeInAppNotification, preview: Bool = false) {
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.index = index
|
||||||
self.referenceTheme = referenceTheme
|
self.referenceTheme = referenceTheme
|
||||||
self.overallDarkAppearance = overallDarkAppearance
|
self.overallDarkAppearance = overallDarkAppearance
|
||||||
self.intro = intro
|
self.intro = intro
|
||||||
@ -1162,6 +1164,6 @@ public final class PresentationTheme: Equatable {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return PresentationTheme(name: name.flatMap(PresentationThemeName.custom) ?? .custom(self.name.string), referenceTheme: self.referenceTheme, overallDarkAppearance: self.overallDarkAppearance, intro: self.intro, passcode: self.passcode, rootController: self.rootController, list: self.list, chatList: self.chatList, chat: self.chat.withUpdated(defaultWallpaper: defaultWallpaper), actionSheet: self.actionSheet, contextMenu: self.contextMenu, inAppNotification: self.inAppNotification)
|
return PresentationTheme(name: name.flatMap(PresentationThemeName.custom) ?? .custom(self.name.string), index: self.index, referenceTheme: self.referenceTheme, overallDarkAppearance: self.overallDarkAppearance, intro: self.intro, passcode: self.passcode, rootController: self.rootController, list: self.list, chatList: self.chatList, chat: self.chat.withUpdated(defaultWallpaper: defaultWallpaper), actionSheet: self.actionSheet, contextMenu: self.contextMenu, inAppNotification: self.inAppNotification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1723,12 +1723,17 @@ extension PresentationTheme: Codable {
|
|||||||
referenceTheme = .dayClassic
|
referenceTheme = .dayClassic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let index: Int64
|
||||||
if let decoder = decoder as? PresentationThemeDecoding {
|
if let decoder = decoder as? PresentationThemeDecoding {
|
||||||
let serviceBackgroundColor = decoder.serviceBackgroundColor ?? defaultServiceBackgroundColor
|
let serviceBackgroundColor = decoder.serviceBackgroundColor ?? defaultServiceBackgroundColor
|
||||||
decoder.referenceTheme = makeDefaultPresentationTheme(reference: referenceTheme, serviceBackgroundColor: serviceBackgroundColor)
|
decoder.referenceTheme = makeDefaultPresentationTheme(reference: referenceTheme, serviceBackgroundColor: serviceBackgroundColor)
|
||||||
|
index = decoder.reference?.index ?? arc4random64()
|
||||||
|
} else {
|
||||||
|
index = arc4random64()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.init(name: (try? values.decode(PresentationThemeName.self, forKey: .name)) ?? .custom("Untitled"),
|
self.init(name: (try? values.decode(PresentationThemeName.self, forKey: .name)) ?? .custom("Untitled"),
|
||||||
|
index: index,
|
||||||
referenceTheme: referenceTheme,
|
referenceTheme: referenceTheme,
|
||||||
overallDarkAppearance: (try? values.decode(Bool.self, forKey: .dark)) ?? false,
|
overallDarkAppearance: (try? values.decode(Bool.self, forKey: .dark)) ?? false,
|
||||||
intro: try values.decode(PresentationThemeIntro.self, forKey: .intro),
|
intro: try values.decode(PresentationThemeIntro.self, forKey: .intro),
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import SyncCore
|
import SyncCore
|
||||||
|
import TelegramUIPreferences
|
||||||
|
|
||||||
public func encodePresentationTheme(_ theme: PresentationTheme) -> String? {
|
public func encodePresentationTheme(_ theme: PresentationTheme) -> String? {
|
||||||
let encoding = PresentationThemeEncoding()
|
let encoding = PresentationThemeEncoding()
|
||||||
@ -341,7 +342,7 @@ private class PresentationThemeDecodingLevel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makePresentationTheme(data: Data, resolvedWallpaper: TelegramWallpaper? = nil) -> PresentationTheme? {
|
public func makePresentationTheme(data: Data, themeReference: PresentationThemeReference? = nil, resolvedWallpaper: TelegramWallpaper? = nil) -> PresentationTheme? {
|
||||||
guard let string = String(data: data, encoding: .utf8) else {
|
guard let string = String(data: data, encoding: .utf8) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -402,6 +403,7 @@ public func makePresentationTheme(data: Data, resolvedWallpaper: TelegramWallpap
|
|||||||
}
|
}
|
||||||
|
|
||||||
let decoder = PresentationThemeDecoding(referencing: topLevel.data)
|
let decoder = PresentationThemeDecoding(referencing: topLevel.data)
|
||||||
|
decoder.reference = themeReference
|
||||||
decoder.resolvedWallpaper = resolvedWallpaper
|
decoder.resolvedWallpaper = resolvedWallpaper
|
||||||
if let value = try? decoder.unbox(topLevel.data, as: PresentationTheme.self) {
|
if let value = try? decoder.unbox(topLevel.data, as: PresentationTheme.self) {
|
||||||
return value
|
return value
|
||||||
@ -418,6 +420,7 @@ class PresentationThemeDecoding: Decoder {
|
|||||||
return [:]
|
return [:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var reference: PresentationThemeReference?
|
||||||
var referenceTheme: PresentationTheme?
|
var referenceTheme: PresentationTheme?
|
||||||
var serviceBackgroundColor: UIColor?
|
var serviceBackgroundColor: UIColor?
|
||||||
var resolvedWallpaper: TelegramWallpaper?
|
var resolvedWallpaper: TelegramWallpaper?
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 498 KiB After Width: | Height: | Size: 504 KiB |
Binary file not shown.
@ -139,7 +139,7 @@ final class ThemeUpdateManagerImpl: ThemeUpdateManager {
|
|||||||
theme = updatedTheme
|
theme = updatedTheme
|
||||||
}
|
}
|
||||||
|
|
||||||
return PresentationThemeSettings(theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
return PresentationThemeSettings(theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificCustomColors: current.themeSpecificCustomColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -117,6 +117,7 @@ final class WallpaperUploadManagerImpl: WallpaperUploadManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if strongSelf.currentPresentationData?.theme.name == presentationData.theme.name {
|
if strongSelf.currentPresentationData?.theme.name == presentationData.theme.name {
|
||||||
|
let autoNightModeTriggered = strongSelf.currentPresentationData?.autoNightModeTriggered ?? false
|
||||||
let _ = (updatePresentationThemeSettingsInteractively(accountManager: sharedContext.accountManager, { current in
|
let _ = (updatePresentationThemeSettingsInteractively(accountManager: sharedContext.accountManager, { current in
|
||||||
let updatedWallpaper: TelegramWallpaper
|
let updatedWallpaper: TelegramWallpaper
|
||||||
if let currentSettings = currentWallpaper.settings {
|
if let currentSettings = currentWallpaper.settings {
|
||||||
@ -124,10 +125,17 @@ final class WallpaperUploadManagerImpl: WallpaperUploadManager {
|
|||||||
} else {
|
} else {
|
||||||
updatedWallpaper = wallpaper
|
updatedWallpaper = wallpaper
|
||||||
}
|
}
|
||||||
|
let themeReference: PresentationThemeReference
|
||||||
|
if autoNightModeTriggered {
|
||||||
|
themeReference = current.automaticThemeSwitchSetting.theme
|
||||||
|
} else {
|
||||||
|
themeReference = current.theme
|
||||||
|
}
|
||||||
|
let accentColorIndex = current.themeSpecificAccentColors[themeReference.index]?.index ?? 0
|
||||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||||
themeSpecificChatWallpapers[current.theme.index] = updatedWallpaper
|
themeSpecificChatWallpapers[themeReference.index] = updatedWallpaper
|
||||||
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
themeSpecificChatWallpapers[themeReference.index &+ Int64(accentColorIndex)] = updatedWallpaper
|
||||||
|
return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificCustomColors: current.themeSpecificCustomColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||||
})).start()
|
})).start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -314,6 +314,7 @@ public enum PresentationThemeBaseColor: Int32, CaseIterable {
|
|||||||
case black
|
case black
|
||||||
case white
|
case white
|
||||||
case custom
|
case custom
|
||||||
|
case preset
|
||||||
|
|
||||||
public var color: UIColor {
|
public var color: UIColor {
|
||||||
let value: UInt32
|
let value: UInt32
|
||||||
@ -340,29 +341,72 @@ public enum PresentationThemeBaseColor: Int32, CaseIterable {
|
|||||||
value = 0x000000
|
value = 0x000000
|
||||||
case .white:
|
case .white:
|
||||||
value = 0xffffff
|
value = 0xffffff
|
||||||
case .custom:
|
case .custom, .preset:
|
||||||
return .clear
|
return .clear
|
||||||
}
|
}
|
||||||
return UIColor(rgb: value)
|
return UIColor(rgb: value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct PresentationThemeAccentColor: PostboxCoding, Equatable {
|
public struct PresentationThemeCustomColors: PostboxCoding, Equatable {
|
||||||
public static func == (lhs: PresentationThemeAccentColor, rhs: PresentationThemeAccentColor) -> Bool {
|
public static func == (lhs: PresentationThemeCustomColors, rhs: PresentationThemeCustomColors) -> Bool {
|
||||||
return lhs.baseColor == rhs.baseColor && lhs.accentColor == rhs.accentColor && lhs.bubbleColors?.0 == rhs.bubbleColors?.0 && lhs.bubbleColors?.1 == rhs.bubbleColors?.1
|
return lhs.colors == rhs.colors
|
||||||
}
|
}
|
||||||
|
|
||||||
public var baseColor: PresentationThemeBaseColor
|
public let colors: [PresentationThemeAccentColor]
|
||||||
public var accentColor: Int32?
|
|
||||||
public var bubbleColors: (Int32, Int32?)?
|
|
||||||
|
|
||||||
public init(baseColor: PresentationThemeBaseColor, accentColor: Int32? = nil, bubbleColors: (Int32, Int32?)? = nil) {
|
public init(colors: [PresentationThemeAccentColor]) {
|
||||||
self.baseColor = baseColor
|
self.colors = colors
|
||||||
self.accentColor = accentColor
|
|
||||||
self.bubbleColors = bubbleColors
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
|
if let colors = try? decoder.decodeObjectArrayWithCustomDecoderForKey("v", decoder: { decoder in
|
||||||
|
return PresentationThemeAccentColor(decoder: decoder)
|
||||||
|
}) {
|
||||||
|
self.colors = colors
|
||||||
|
} else {
|
||||||
|
self.colors = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
|
encoder.encodeObjectArray(self.colors, forKey: "v")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct PresentationThemeAccentColor: PostboxCoding, Equatable {
|
||||||
|
public static func == (lhs: PresentationThemeAccentColor, rhs: PresentationThemeAccentColor) -> Bool {
|
||||||
|
return lhs.index == rhs.index && lhs.baseColor == rhs.baseColor && lhs.accentColor == rhs.accentColor && lhs.bubbleColors?.0 == rhs.bubbleColors?.0 && lhs.bubbleColors?.1 == rhs.bubbleColors?.1
|
||||||
|
}
|
||||||
|
|
||||||
|
public var index: Int32
|
||||||
|
public var baseColor: PresentationThemeBaseColor
|
||||||
|
public var accentColor: Int32?
|
||||||
|
public var bubbleColors: (Int32, Int32?)?
|
||||||
|
public var wallpaper: TelegramWallpaper?
|
||||||
|
|
||||||
|
public init(baseColor: PresentationThemeBaseColor) {
|
||||||
|
if baseColor != .preset && baseColor != .custom {
|
||||||
|
self.index = baseColor.rawValue + 10
|
||||||
|
} else {
|
||||||
|
self.index = -1
|
||||||
|
}
|
||||||
|
self.baseColor = baseColor
|
||||||
|
self.accentColor = nil
|
||||||
|
self.bubbleColors = nil
|
||||||
|
self.wallpaper = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(index: Int32, baseColor: PresentationThemeBaseColor, accentColor: Int32? = nil, bubbleColors: (Int32, Int32?)? = nil, wallpaper: TelegramWallpaper? = nil) {
|
||||||
|
self.index = index
|
||||||
|
self.baseColor = baseColor
|
||||||
|
self.accentColor = accentColor
|
||||||
|
self.bubbleColors = bubbleColors
|
||||||
|
self.wallpaper = wallpaper
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(decoder: PostboxDecoder) {
|
||||||
|
self.index = decoder.decodeInt32ForKey("i", orElse: -1)
|
||||||
self.baseColor = PresentationThemeBaseColor(rawValue: decoder.decodeInt32ForKey("b", orElse: 0)) ?? .blue
|
self.baseColor = PresentationThemeBaseColor(rawValue: decoder.decodeInt32ForKey("b", orElse: 0)) ?? .blue
|
||||||
self.accentColor = decoder.decodeOptionalInt32ForKey("c")
|
self.accentColor = decoder.decodeOptionalInt32ForKey("c")
|
||||||
if let bubbleTopColor = decoder.decodeOptionalInt32ForKey("bt") {
|
if let bubbleTopColor = decoder.decodeOptionalInt32ForKey("bt") {
|
||||||
@ -377,6 +421,7 @@ public struct PresentationThemeAccentColor: PostboxCoding, Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
|
encoder.encodeInt32(self.index, forKey: "i")
|
||||||
encoder.encodeInt32(self.baseColor.rawValue, forKey: "b")
|
encoder.encodeInt32(self.baseColor.rawValue, forKey: "b")
|
||||||
if let value = self.accentColor {
|
if let value = self.accentColor {
|
||||||
encoder.encodeInt32(value, forKey: "c")
|
encoder.encodeInt32(value, forKey: "c")
|
||||||
@ -432,6 +477,7 @@ public struct PresentationThemeAccentColor: PostboxCoding, Equatable {
|
|||||||
public struct PresentationThemeSettings: PreferencesEntry {
|
public struct PresentationThemeSettings: PreferencesEntry {
|
||||||
public var theme: PresentationThemeReference
|
public var theme: PresentationThemeReference
|
||||||
public var themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
public var themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||||
|
public var themeSpecificCustomColors: [Int64: PresentationThemeCustomColors]
|
||||||
public var themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
public var themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
||||||
public var useSystemFont: Bool
|
public var useSystemFont: Bool
|
||||||
public var fontSize: PresentationFontSize
|
public var fontSize: PresentationFontSize
|
||||||
@ -471,16 +517,24 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
|||||||
resources.append(contentsOf: wallpaperResources(chatWallpaper))
|
resources.append(contentsOf: wallpaperResources(chatWallpaper))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (_, colors) in self.themeSpecificCustomColors {
|
||||||
|
for color in colors.colors {
|
||||||
|
if let wallpaper = color.wallpaper {
|
||||||
|
resources.append(contentsOf: wallpaperResources(wallpaper))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return resources
|
return resources
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var defaultSettings: PresentationThemeSettings {
|
public static var defaultSettings: PresentationThemeSettings {
|
||||||
return PresentationThemeSettings(theme: .builtin(.dayClassic), themeSpecificAccentColors: [:], themeSpecificChatWallpapers: [:], useSystemFont: true, fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .system, theme: .builtin(.night)), largeEmoji: true, disableAnimations: true)
|
return PresentationThemeSettings(theme: .builtin(.dayClassic), themeSpecificAccentColors: [:], themeSpecificCustomColors: [:], themeSpecificChatWallpapers: [:], useSystemFont: true, fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .system, theme: .builtin(.night)), largeEmoji: true, disableAnimations: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(theme: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], useSystemFont: Bool, fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, largeEmoji: Bool, disableAnimations: Bool) {
|
public init(theme: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificCustomColors: [Int64: PresentationThemeCustomColors], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], useSystemFont: Bool, fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, largeEmoji: Bool, disableAnimations: Bool) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.themeSpecificAccentColors = themeSpecificAccentColors
|
self.themeSpecificAccentColors = themeSpecificAccentColors
|
||||||
|
self.themeSpecificCustomColors = themeSpecificCustomColors
|
||||||
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
|
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
|
||||||
self.useSystemFont = useSystemFont
|
self.useSystemFont = useSystemFont
|
||||||
self.fontSize = fontSize
|
self.fontSize = fontSize
|
||||||
@ -498,6 +552,12 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
|||||||
return TelegramWallpaper(decoder: decoder)
|
return TelegramWallpaper(decoder: decoder)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.themeSpecificCustomColors = decoder.decodeObjectDictionaryForKey("themeSpecificCustomColors", keyDecoder: { decoder in
|
||||||
|
return decoder.decodeInt64ForKey("k", orElse: 0)
|
||||||
|
}, valueDecoder: { decoder in
|
||||||
|
return PresentationThemeCustomColors(decoder: decoder)
|
||||||
|
})
|
||||||
|
|
||||||
self.themeSpecificAccentColors = decoder.decodeObjectDictionaryForKey("themeSpecificAccentColors", keyDecoder: { decoder in
|
self.themeSpecificAccentColors = decoder.decodeObjectDictionaryForKey("themeSpecificAccentColors", keyDecoder: { decoder in
|
||||||
return decoder.decodeInt64ForKey("k", orElse: 0)
|
return decoder.decodeInt64ForKey("k", orElse: 0)
|
||||||
}, valueDecoder: { decoder in
|
}, valueDecoder: { decoder in
|
||||||
@ -516,6 +576,9 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
|||||||
encoder.encodeObjectDictionary(self.themeSpecificAccentColors, forKey: "themeSpecificAccentColors", keyEncoder: { key, encoder in
|
encoder.encodeObjectDictionary(self.themeSpecificAccentColors, forKey: "themeSpecificAccentColors", keyEncoder: { key, encoder in
|
||||||
encoder.encodeInt64(key, forKey: "k")
|
encoder.encodeInt64(key, forKey: "k")
|
||||||
})
|
})
|
||||||
|
encoder.encodeObjectDictionary(self.themeSpecificCustomColors, forKey: "themeSpecificCustomColors", keyEncoder: { key, encoder in
|
||||||
|
encoder.encodeInt64(key, forKey: "k")
|
||||||
|
})
|
||||||
encoder.encodeObjectDictionary(self.themeSpecificChatWallpapers, forKey: "themeSpecificChatWallpapers", keyEncoder: { key, encoder in
|
encoder.encodeObjectDictionary(self.themeSpecificChatWallpapers, forKey: "themeSpecificChatWallpapers", keyEncoder: { key, encoder in
|
||||||
encoder.encodeInt64(key, forKey: "k")
|
encoder.encodeInt64(key, forKey: "k")
|
||||||
})
|
})
|
||||||
@ -535,7 +598,43 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: PresentationThemeSettings, rhs: PresentationThemeSettings) -> Bool {
|
public static func ==(lhs: PresentationThemeSettings, rhs: PresentationThemeSettings) -> Bool {
|
||||||
return lhs.theme == rhs.theme && lhs.themeSpecificAccentColors == rhs.themeSpecificAccentColors && lhs.themeSpecificChatWallpapers == rhs.themeSpecificChatWallpapers && lhs.useSystemFont == rhs.useSystemFont && 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.themeSpecificCustomColors == rhs.themeSpecificCustomColors && lhs.themeSpecificChatWallpapers == rhs.themeSpecificChatWallpapers && lhs.useSystemFont == rhs.useSystemFont && lhs.fontSize == rhs.fontSize && lhs.automaticThemeSwitchSetting == rhs.automaticThemeSwitchSetting && lhs.largeEmoji == rhs.largeEmoji && lhs.disableAnimations == rhs.disableAnimations
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedTheme(_ theme: PresentationThemeReference) -> PresentationThemeSettings {
|
||||||
|
return PresentationThemeSettings(theme: theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificCustomColors: self.themeSpecificCustomColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedThemeSpecificAccentColors(_ themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]) -> PresentationThemeSettings {
|
||||||
|
return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificCustomColors: self.themeSpecificCustomColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedThemeSpecificCustomColors(_ themeSpecificCustomColors: [Int64: PresentationThemeCustomColors]) -> PresentationThemeSettings {
|
||||||
|
return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificCustomColors: themeSpecificCustomColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedThemeSpecificChatWallpapers(_ themeSpecificChatWallpapers: [Int64: TelegramWallpaper]) -> PresentationThemeSettings {
|
||||||
|
return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificCustomColors: self.themeSpecificCustomColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedUseSystemFont(_ useSystemFont: Bool) -> PresentationThemeSettings {
|
||||||
|
return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificCustomColors: self.themeSpecificCustomColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: useSystemFont, fontSize: self.fontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedFontSize(_ fontSize: PresentationFontSize) -> PresentationThemeSettings {
|
||||||
|
return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificCustomColors: self.themeSpecificCustomColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: fontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedAutomaticThemeSwitchSetting(_ automaticThemeSwitchSetting: AutomaticThemeSwitchSetting) -> PresentationThemeSettings {
|
||||||
|
return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificCustomColors: self.themeSpecificCustomColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedLargeEmoji(_ largeEmoji: Bool) -> PresentationThemeSettings {
|
||||||
|
return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificCustomColors: self.themeSpecificCustomColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: largeEmoji, disableAnimations: self.disableAnimations)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedDisableAnimations(_ disableAnimations: Bool) -> PresentationThemeSettings {
|
||||||
|
return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificCustomColors: self.themeSpecificCustomColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: disableAnimations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1056,39 +1056,41 @@ public func themeImage(account: Account, accountManager: AccountManager, fileRef
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func themeIconImage(account: Account, accountManager: AccountManager, theme: PresentationThemeReference, accentColor: UIColor?, bubbleColors: (UIColor, UIColor)?) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
public func themeIconImage(account: Account, accountManager: AccountManager, theme: PresentationThemeReference, color: PresentationThemeAccentColor?, wallpaper: TelegramWallpaper? = nil) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||||
let colorsSignal: Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?), NoError>
|
let colorsSignal: Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError>
|
||||||
if case let .builtin(theme) = theme {
|
if case let .builtin(theme) = theme {
|
||||||
let backgroundColor: UIColor
|
|
||||||
let incomingColor: UIColor
|
let incomingColor: UIColor
|
||||||
let outgoingColor: (UIColor, UIColor)
|
let outgoingColor: (UIColor, UIColor)
|
||||||
var accentColor = accentColor
|
var accentColor = color?.color
|
||||||
|
var bubbleColors = color?.plainBubbleColors
|
||||||
|
var topBackgroundColor: UIColor
|
||||||
|
var bottomBackgroundColor: UIColor?
|
||||||
switch theme {
|
switch theme {
|
||||||
case .dayClassic:
|
case .dayClassic:
|
||||||
incomingColor = UIColor(rgb: 0xffffff)
|
incomingColor = UIColor(rgb: 0xffffff)
|
||||||
if let accentColor = accentColor {
|
if let accentColor = accentColor {
|
||||||
if let bubbleColors = bubbleColors {
|
if let bubbleColors = bubbleColors {
|
||||||
backgroundColor = UIColor(rgb: 0xd6e2ee)
|
topBackgroundColor = UIColor(rgb: 0xd6e2ee)
|
||||||
outgoingColor = bubbleColors
|
outgoingColor = bubbleColors
|
||||||
} else {
|
} else {
|
||||||
backgroundColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.867, brightness: 0.965)
|
topBackgroundColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.867, brightness: 0.965)
|
||||||
let hsb = accentColor.hsb
|
let hsb = accentColor.hsb
|
||||||
let bubbleColor = UIColor(hue: hsb.0, saturation: hsb.2 > 0.0 ? 0.14 : 0.0, brightness: 0.79 + hsb.2 * 0.21, alpha: 1.0)
|
let bubbleColor = UIColor(hue: hsb.0, saturation: hsb.2 > 0.0 ? 0.14 : 0.0, brightness: 0.79 + hsb.2 * 0.21, alpha: 1.0)
|
||||||
outgoingColor = (bubbleColor, bubbleColor)
|
outgoingColor = (bubbleColor, bubbleColor)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
backgroundColor = UIColor(rgb: 0xd6e2ee)
|
topBackgroundColor = UIColor(rgb: 0xd6e2ee)
|
||||||
outgoingColor = (UIColor(rgb: 0xe1ffc7), UIColor(rgb: 0xe1ffc7))
|
outgoingColor = (UIColor(rgb: 0xe1ffc7), UIColor(rgb: 0xe1ffc7))
|
||||||
}
|
}
|
||||||
case .day:
|
case .day:
|
||||||
backgroundColor = UIColor(rgb: 0xffffff)
|
topBackgroundColor = UIColor(rgb: 0xffffff)
|
||||||
incomingColor = UIColor(rgb: 0xd5dde6)
|
incomingColor = UIColor(rgb: 0xd5dde6)
|
||||||
if accentColor == nil {
|
if accentColor == nil {
|
||||||
accentColor = UIColor(rgb: 0x007aff)
|
accentColor = UIColor(rgb: 0x007aff)
|
||||||
}
|
}
|
||||||
outgoingColor = bubbleColors ?? (accentColor!, accentColor!)
|
outgoingColor = bubbleColors ?? (accentColor!, accentColor!)
|
||||||
case .night:
|
case .night:
|
||||||
backgroundColor = UIColor(rgb: 0x000000)
|
topBackgroundColor = UIColor(rgb: 0x000000)
|
||||||
incomingColor = UIColor(rgb: 0x1f1f1f)
|
incomingColor = UIColor(rgb: 0x1f1f1f)
|
||||||
if accentColor == nil {
|
if accentColor == nil {
|
||||||
accentColor = UIColor(rgb: 0x313131)
|
accentColor = UIColor(rgb: 0x313131)
|
||||||
@ -1096,12 +1098,33 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
|||||||
outgoingColor = bubbleColors ?? (accentColor!, accentColor!)
|
outgoingColor = bubbleColors ?? (accentColor!, accentColor!)
|
||||||
case .nightAccent:
|
case .nightAccent:
|
||||||
let accentColor = accentColor ?? UIColor(rgb: 0x007aff)
|
let accentColor = accentColor ?? UIColor(rgb: 0x007aff)
|
||||||
backgroundColor = accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18)
|
topBackgroundColor = accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18)
|
||||||
incomingColor = accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25)
|
incomingColor = accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25)
|
||||||
let accentBubbleColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59)
|
let accentBubbleColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59)
|
||||||
outgoingColor = bubbleColors ?? (accentBubbleColor, accentBubbleColor)
|
outgoingColor = bubbleColors ?? (accentBubbleColor, accentBubbleColor)
|
||||||
}
|
}
|
||||||
colorsSignal = .single(((backgroundColor, nil), (incomingColor, incomingColor), outgoingColor, nil))
|
|
||||||
|
var rotation: Int32?
|
||||||
|
if let wallpaper = wallpaper {
|
||||||
|
switch wallpaper {
|
||||||
|
case let .color(color):
|
||||||
|
topBackgroundColor = UIColor(rgb: UInt32(bitPattern: color))
|
||||||
|
case let .gradient(topColor, bottomColor, settings):
|
||||||
|
topBackgroundColor = UIColor(rgb: UInt32(bitPattern: topColor))
|
||||||
|
bottomBackgroundColor = UIColor(rgb: UInt32(bitPattern: bottomColor))
|
||||||
|
rotation = settings.rotation
|
||||||
|
case let .file(file):
|
||||||
|
if let color = file.settings.color {
|
||||||
|
topBackgroundColor = UIColor(rgb: UInt32(bitPattern: color))
|
||||||
|
bottomBackgroundColor = file.settings.bottomColor.flatMap { UIColor(rgb: UInt32(bitPattern: $0)) }
|
||||||
|
}
|
||||||
|
rotation = file.settings.rotation
|
||||||
|
default:
|
||||||
|
topBackgroundColor = UIColor(rgb: 0xd6e2ee)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
colorsSignal = .single(((topBackgroundColor, bottomBackgroundColor), (incomingColor, incomingColor), outgoingColor, nil, rotation))
|
||||||
} else {
|
} else {
|
||||||
var resource: MediaResource?
|
var resource: MediaResource?
|
||||||
if case let .local(theme) = theme {
|
if case let .local(theme) = theme {
|
||||||
@ -1111,9 +1134,10 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
|||||||
}
|
}
|
||||||
if let resource = resource {
|
if let resource = resource {
|
||||||
colorsSignal = telegramThemeData(account: account, accountManager: accountManager, resource: resource, synchronousLoad: false)
|
colorsSignal = telegramThemeData(account: account, accountManager: accountManager, resource: resource, synchronousLoad: false)
|
||||||
|> mapToSignal { data -> Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?), NoError> in
|
|> mapToSignal { data -> Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> in
|
||||||
if let data = data, let theme = makePresentationTheme(data: data) {
|
if let data = data, let theme = makePresentationTheme(data: data) {
|
||||||
var wallpaperSignal: Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?), NoError> = .complete()
|
var wallpaperSignal: Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> = .complete()
|
||||||
|
var rotation: Int32?
|
||||||
let backgroundColor: (UIColor, UIColor?)
|
let backgroundColor: (UIColor, UIColor?)
|
||||||
let incomingColor = (theme.chat.message.incoming.bubble.withoutWallpaper.fill, theme.chat.message.incoming.bubble.withoutWallpaper.gradientFill)
|
let incomingColor = (theme.chat.message.incoming.bubble.withoutWallpaper.fill, theme.chat.message.incoming.bubble.withoutWallpaper.gradientFill)
|
||||||
let outgoingColor = (theme.chat.message.outgoing.bubble.withoutWallpaper.fill, theme.chat.message.outgoing.bubble.withoutWallpaper.gradientFill)
|
let outgoingColor = (theme.chat.message.outgoing.bubble.withoutWallpaper.fill, theme.chat.message.outgoing.bubble.withoutWallpaper.gradientFill)
|
||||||
@ -1122,11 +1146,13 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
|||||||
backgroundColor = (UIColor(rgb: 0xd6e2ee), nil)
|
backgroundColor = (UIColor(rgb: 0xd6e2ee), nil)
|
||||||
case let .color(color):
|
case let .color(color):
|
||||||
backgroundColor = (UIColor(rgb: UInt32(bitPattern: color)), nil)
|
backgroundColor = (UIColor(rgb: UInt32(bitPattern: color)), nil)
|
||||||
case let .gradient(topColor, bottomColor, _):
|
case let .gradient(topColor, bottomColor, settings):
|
||||||
backgroundColor = (UIColor(rgb: UInt32(bitPattern: topColor)), UIColor(rgb: UInt32(bitPattern: bottomColor)))
|
backgroundColor = (UIColor(rgb: UInt32(bitPattern: topColor)), UIColor(rgb: UInt32(bitPattern: bottomColor)))
|
||||||
|
rotation = settings.rotation
|
||||||
case .image:
|
case .image:
|
||||||
backgroundColor = (.black, nil)
|
backgroundColor = (.black, nil)
|
||||||
case let .file(file):
|
case let .file(file):
|
||||||
|
rotation = file.settings.rotation
|
||||||
if file.isPattern, let color = file.settings.color {
|
if file.isPattern, let color = file.settings.color {
|
||||||
backgroundColor = (UIColor(rgb: UInt32(bitPattern: color)), file.settings.bottomColor.flatMap { UIColor(rgb: UInt32(bitPattern: $0)) })
|
backgroundColor = (UIColor(rgb: UInt32(bitPattern: color)), file.settings.bottomColor.flatMap { UIColor(rgb: UInt32(bitPattern: $0)) })
|
||||||
} else {
|
} else {
|
||||||
@ -1138,7 +1164,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
|||||||
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
||||||
convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource), reference: .media(media: .standalone(media: file.file), resource: file.file.resource)))
|
convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource), reference: .media(media: .standalone(media: file.file), resource: file.file.resource)))
|
||||||
return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
|
return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
|
||||||
|> mapToSignal { _, fullSizeData, complete -> Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?), NoError> in
|
|> mapToSignal { _, fullSizeData, complete -> Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> in
|
||||||
guard complete, let fullSizeData = fullSizeData else {
|
guard complete, let fullSizeData = fullSizeData else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
@ -1154,13 +1180,13 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
|||||||
return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true)
|
return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true)
|
||||||
|> mapToSignal { _ in
|
|> mapToSignal { _ in
|
||||||
if let image = UIImage(data: fullSizeData) {
|
if let image = UIImage(data: fullSizeData) {
|
||||||
return .single((backgroundColor, incomingColor, outgoingColor, image))
|
return .single((backgroundColor, incomingColor, outgoingColor, image, rotation))
|
||||||
} else {
|
} else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let image = UIImage(data: fullSizeData) {
|
} else if let image = UIImage(data: fullSizeData) {
|
||||||
return .single((backgroundColor, incomingColor, outgoingColor, image))
|
return .single((backgroundColor, incomingColor, outgoingColor, image, rotation))
|
||||||
} else {
|
} else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
@ -1170,7 +1196,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .single((backgroundColor, incomingColor, outgoingColor, nil))
|
return .single((backgroundColor, incomingColor, outgoingColor, nil, rotation))
|
||||||
|> then(wallpaperSignal)
|
|> then(wallpaperSignal)
|
||||||
} else {
|
} else {
|
||||||
return .complete()
|
return .complete()
|
||||||
@ -1192,7 +1218,14 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
|||||||
var locations: [CGFloat] = [0.0, 1.0]
|
var locations: [CGFloat] = [0.0, 1.0]
|
||||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
|
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
|
||||||
c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: drawingRect.height), options: CGGradientDrawingOptions())
|
c.saveGState()
|
||||||
|
if let rotation = colors.4 {
|
||||||
|
c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
|
||||||
|
c.rotate(by: CGFloat(rotation) * CGFloat.pi / 180.0)
|
||||||
|
c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
|
||||||
|
}
|
||||||
|
c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: drawingRect.height), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
|
||||||
|
c.restoreGState()
|
||||||
} else {
|
} else {
|
||||||
c.setFillColor(colors.0.0.cgColor)
|
c.setFillColor(colors.0.0.cgColor)
|
||||||
c.fill(drawingRect)
|
c.fill(drawingRect)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user