mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-01 16:06:59 +00:00
Optimize pattern rendering
This commit is contained in:
parent
165c334854
commit
06522652f9
@ -537,11 +537,11 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
|
||||
if address != nil {
|
||||
if foundVenues == nil {
|
||||
if foundVenues == nil && !state.searchingVenuesAround {
|
||||
displayingPlacesButton = true
|
||||
} else if let previousLocation = foundVenuesLocation {
|
||||
let currentLocation = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
|
||||
if currentLocation.distance(from: previousLocation) > 500 {
|
||||
if currentLocation.distance(from: previousLocation) > 300 {
|
||||
displayingPlacesButton = true
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import TelegramUIPreferences
|
||||
import ChatListUI
|
||||
import AccountContext
|
||||
import WallpaperResources
|
||||
import PresentationDataUtils
|
||||
|
||||
private func generateMaskImage(color: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 1.0, height: 80.0), opaque: false, rotatedContext: { size, context in
|
||||
@ -157,6 +158,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
private let mode: ThemeAccentColorControllerMode
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private let queue = Queue()
|
||||
|
||||
private var state: ThemeColorState
|
||||
private let referenceTimestamp: Int32
|
||||
|
||||
@ -398,8 +401,11 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
}
|
||||
|
||||
self.stateDisposable = (self.statePromise.get()
|
||||
|> deliverOn(Queue.concurrentDefaultQueue())
|
||||
|> map { state -> (PresentationTheme, (TelegramWallpaper, UIImage?, Signal<(TransformImageArguments) -> DrawingContext?, NoError>?), UIColor, (UIColor, UIColor?)?, [UIColor], Bool) in
|
||||
|> deliverOn(self.queue)
|
||||
|> mapToThrottled { next -> Signal<ThemeColorState, NoError> in
|
||||
return .single(next) |> then(.complete() |> delay(0.0166667, queue: self.queue))
|
||||
}
|
||||
|> map { [weak self] state -> (PresentationTheme?, (TelegramWallpaper, UIImage?, Signal<(TransformImageArguments) -> DrawingContext?, NoError>?, (() -> Void)?), UIColor, (UIColor, UIColor?)?, PatternWallpaperArguments, Bool) in
|
||||
let accentColor = state.accentColor
|
||||
var backgroundColors = state.backgroundColors
|
||||
let messagesColors = state.messagesColors
|
||||
@ -410,6 +416,11 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
|
||||
var singleBackgroundColor: UIColor?
|
||||
|
||||
var updateOnlyWallpaper = false
|
||||
if state.section == .background && state.preview {
|
||||
updateOnlyWallpaper = true
|
||||
}
|
||||
|
||||
if let backgroundColors = backgroundColors {
|
||||
if let patternWallpaper = state.patternWallpaper, case let .file(file) = patternWallpaper {
|
||||
let color = Int32(bitPattern: backgroundColors.0.rgb)
|
||||
@ -453,40 +464,51 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
}
|
||||
|
||||
let serviceBackgroundColor = serviceColor(for: (wallpaper, wallpaperImage))
|
||||
let updatedTheme: PresentationTheme
|
||||
if let themeReference = mode.themeReference {
|
||||
updatedTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: messagesColors, serviceBackgroundColor: serviceBackgroundColor, preview: true) ?? defaultPresentationTheme
|
||||
} else if case let .edit(theme, _, _, _, _) = mode {
|
||||
updatedTheme = customizePresentationTheme(theme, editing: false, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: messagesColors)
|
||||
let updatedTheme: PresentationTheme?
|
||||
|
||||
if !updateOnlyWallpaper {
|
||||
if let themeReference = mode.themeReference {
|
||||
updatedTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: messagesColors, serviceBackgroundColor: serviceBackgroundColor, preview: true) ?? defaultPresentationTheme
|
||||
} else if case let .edit(theme, _, _, _, _) = mode {
|
||||
updatedTheme = customizePresentationTheme(theme, editing: false, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: messagesColors)
|
||||
} else {
|
||||
updatedTheme = theme
|
||||
}
|
||||
|
||||
let _ = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: updatedTheme!, wallpaper: wallpaper)
|
||||
} else {
|
||||
updatedTheme = theme
|
||||
updatedTheme = nil
|
||||
}
|
||||
|
||||
let _ = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: updatedTheme, wallpaper: wallpaper)
|
||||
|
||||
let patternColors = calcPatternColors(for: state)
|
||||
let patternArguments = PatternWallpaperArguments(colors: calcPatternColors(for: state), rotation: wallpaper.settings?.rotation ?? 0, preview: state.preview)
|
||||
|
||||
return (updatedTheme, (wallpaper, wallpaperImage, wallpaperSignal), serviceBackgroundColor, backgroundColors, patternColors, state.preview)
|
||||
var wallpaperApply: (() -> Void)?
|
||||
if let strongSelf = self, case let .file(file) = wallpaper, file.isPattern, let (layout, _, _) = strongSelf.validLayout {
|
||||
let makeImageLayout = strongSelf.signalBackgroundNode.asyncLayout()
|
||||
wallpaperApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: wallpaper.dimensions ?? layout.size, boundingSize: layout.size, intrinsicInsets: UIEdgeInsets(), custom: patternArguments))
|
||||
}
|
||||
|
||||
return (updatedTheme, (wallpaper, wallpaperImage, wallpaperSignal, wallpaperApply), serviceBackgroundColor, backgroundColors, patternArguments, state.preview)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] theme, wallpaperImageAndSignal, serviceBackgroundColor, backgroundColors, patternColors, preview in
|
||||
|> deliverOnMainQueue).start(next: { [weak self] theme, wallpaperImageAndSignal, serviceBackgroundColor, backgroundColors, patternArguments, preview in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let (wallpaper, wallpaperImage, wallpaperSignal) = wallpaperImageAndSignal
|
||||
let (wallpaper, wallpaperImage, wallpaperSignal, wallpaperApply) = wallpaperImageAndSignal
|
||||
|
||||
if let theme = theme {
|
||||
strongSelf.theme = theme
|
||||
strongSelf.themeUpdated?(theme)
|
||||
strongSelf.themePromise.set(.single(theme))
|
||||
strongSelf.colorPanelNode.updateTheme(theme)
|
||||
strongSelf.toolbarNode.updateThemeAndStrings(theme: theme, strings: strongSelf.presentationData.strings)
|
||||
strongSelf.chatListBackgroundNode.backgroundColor = theme.chatList.backgroundColor
|
||||
strongSelf.maskNode.image = generateMaskImage(color: theme.chatList.backgroundColor)
|
||||
}
|
||||
|
||||
strongSelf.theme = theme
|
||||
strongSelf.themeUpdated?(theme)
|
||||
strongSelf.themePromise.set(.single(theme))
|
||||
strongSelf.serviceBackgroundColor = serviceBackgroundColor
|
||||
strongSelf.serviceBackgroundColorPromise.set(.single(serviceBackgroundColor))
|
||||
|
||||
strongSelf.colorPanelNode.updateTheme(theme)
|
||||
strongSelf.toolbarNode.updateThemeAndStrings(theme: theme, strings: strongSelf.presentationData.strings)
|
||||
|
||||
strongSelf.chatListBackgroundNode.backgroundColor = theme.chatList.backgroundColor
|
||||
strongSelf.maskNode.image = generateMaskImage(color: theme.chatList.backgroundColor)
|
||||
|
||||
let patternArguments = PatternWallpaperArguments(colors: patternColors, rotation: wallpaper.settings?.rotation ?? 0, preview: preview)
|
||||
if case let .color(value) = wallpaper {
|
||||
strongSelf.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
|
||||
strongSelf.immediateBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
|
||||
@ -506,41 +528,10 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
strongSelf.signalBackgroundNode.isHidden = false
|
||||
|
||||
if case let .file(file) = wallpaper, let (layout, _, _) = strongSelf.validLayout {
|
||||
var dispatch = false
|
||||
wallpaperApply?()
|
||||
|
||||
if let previousWallpaper = strongSelf.patternWallpaper, case let .file(previousFile) = previousWallpaper, file.id == previousFile.id {
|
||||
dispatch = true
|
||||
}
|
||||
|
||||
if dispatch {
|
||||
if let _ = strongSelf.patternArgumentsDisposable {
|
||||
} else {
|
||||
strongSelf.signalBackgroundNode.contentAnimations = .subsequentUpdates
|
||||
|
||||
let throttledSignal = strongSelf.patternArgumentsPromise.get()
|
||||
|> mapToThrottled { next -> Signal<TransformImageArguments, NoError> in
|
||||
return .single(next) |> then(.complete() |> delay(0.033333, queue: Queue.concurrentDefaultQueue()))
|
||||
}
|
||||
|
||||
strongSelf.patternArgumentsDisposable = (throttledSignal).start(next: { [weak self] arguments in
|
||||
if let strongSelf = self {
|
||||
let makeImageLayout = strongSelf.signalBackgroundNode.asyncLayout()
|
||||
let imageApply = makeImageLayout(arguments)
|
||||
let _ = imageApply()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
strongSelf.patternArgumentsPromise.set(.single(TransformImageArguments(corners: ImageCorners(), imageSize: layout.size, boundingSize: layout.size, intrinsicInsets: UIEdgeInsets(), custom: patternArguments)))
|
||||
} else {
|
||||
strongSelf.patternArgumentsDisposable?.dispose()
|
||||
strongSelf.patternArgumentsDisposable = nil
|
||||
|
||||
let makeImageLayout = strongSelf.signalBackgroundNode.asyncLayout()
|
||||
let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: layout.size, boundingSize: layout.size, intrinsicInsets: UIEdgeInsets(), custom: patternArguments))
|
||||
let _ = imageApply()
|
||||
}
|
||||
|
||||
if !dispatch {
|
||||
strongSelf.signalBackgroundNode.setSignal(wallpaperSignal)
|
||||
strongSelf.patternWallpaper = wallpaper
|
||||
}
|
||||
@ -557,7 +548,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
strongSelf.patternPanelNode.backgroundColors = backgroundColors
|
||||
}
|
||||
|
||||
if let (layout, navigationBarHeight, messagesBottomInset) = strongSelf.validLayout {
|
||||
if let _ = theme, let (layout, navigationBarHeight, messagesBottomInset) = strongSelf.validLayout {
|
||||
strongSelf.updateChatsLayout(layout: layout, topInset: navigationBarHeight, transition: .immediate)
|
||||
strongSelf.updateMessagesLayout(layout: layout, bottomInset: messagesBottomInset, transition: .immediate)
|
||||
}
|
||||
@ -954,7 +945,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
transition.updateFrame(node: self.backgroundContainerNode, frame: CGRect(origin: CGPoint(), size: backgroundSize))
|
||||
|
||||
let makeImageLayout = self.signalBackgroundNode.asyncLayout()
|
||||
let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: layout.size, boundingSize: layout.size, intrinsicInsets: UIEdgeInsets(), custom: self.patternArguments))
|
||||
let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: self.patternWallpaper?.dimensions ?? layout.size, boundingSize: layout.size, intrinsicInsets: UIEdgeInsets(), custom: self.patternArguments))
|
||||
let _ = imageApply()
|
||||
|
||||
transition.updatePosition(node: self.backgroundWrapperNode, position: CGPoint(x: backgroundSize.width / 2.0, y: backgroundSize.height / 2.0))
|
||||
|
@ -384,23 +384,24 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
||||
}
|
||||
}
|
||||
|
||||
private func themeSettingsControllerEntries(presentationData: PresentationData, presentationThemeSettings: PresentationThemeSettings, theme: PresentationTheme, themeReference: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], availableThemes: [PresentationThemeReference], autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, largeEmoji: Bool, disableAnimations: Bool, availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] {
|
||||
private func themeSettingsControllerEntries(presentationData: PresentationData, presentationThemeSettings: PresentationThemeSettings, themeReference: PresentationThemeReference, availableThemes: [PresentationThemeReference], availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] {
|
||||
var entries: [ThemeSettingsControllerEntry] = []
|
||||
|
||||
let strings = presentationData.strings
|
||||
let title = presentationData.autoNightModeTriggered ? strings.Appearance_ColorThemeNight.uppercased() : strings.Appearance_ColorTheme.uppercased()
|
||||
entries.append(.themeListHeader(presentationData.theme, title))
|
||||
entries.append(.chatPreview(presentationData.theme, theme, wallpaper, presentationData.fontSize, presentationData.strings, dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (presentationData.strings.Appearance_PreviewReplyAuthor, presentationData.strings.Appearance_PreviewReplyText), text: presentationData.strings.Appearance_PreviewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: presentationData.strings.Appearance_PreviewOutgoingText)]))
|
||||
entries.append(.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, themeSpecificAccentColors, themeSpecificAccentColors[themeReference.index]))
|
||||
entries.append(.themeItem(presentationData.theme, presentationData.strings, availableThemes, themeReference, presentationThemeSettings.themeSpecificAccentColors, presentationThemeSettings.themeSpecificAccentColors[themeReference.index]))
|
||||
|
||||
if case let .builtin(theme) = themeReference {
|
||||
entries.append(.accentColor(presentationData.theme, themeReference, themeSpecificAccentColors[themeReference.index]))
|
||||
entries.append(.accentColor(presentationData.theme, themeReference, presentationThemeSettings.themeSpecificAccentColors[themeReference.index]))
|
||||
}
|
||||
|
||||
entries.append(.wallpaper(presentationData.theme, strings.Settings_ChatBackground))
|
||||
|
||||
let autoNightMode: String
|
||||
switch autoNightSettings.trigger {
|
||||
switch presentationThemeSettings.automaticThemeSwitchSetting.trigger {
|
||||
case .system:
|
||||
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
|
||||
autoNightMode = strings.AutoNightTheme_System
|
||||
@ -430,8 +431,8 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
|
||||
}
|
||||
|
||||
entries.append(.otherHeader(presentationData.theme, strings.Appearance_Other.uppercased()))
|
||||
entries.append(.largeEmoji(presentationData.theme, strings.Appearance_LargeEmoji, largeEmoji))
|
||||
entries.append(.animations(presentationData.theme, strings.Appearance_ReduceMotion, disableAnimations))
|
||||
entries.append(.largeEmoji(presentationData.theme, strings.Appearance_LargeEmoji, presentationData.largeEmoji))
|
||||
entries.append(.animations(presentationData.theme, strings.Appearance_ReduceMotion, presentationData.disableAnimations))
|
||||
entries.append(.animationsInfo(presentationData.theme, strings.Appearance_ReduceMotionInfo))
|
||||
|
||||
return entries
|
||||
@ -688,7 +689,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
|> map { presentationData, sharedData, cloudThemes, availableAppIcons, currentAppIconName -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings
|
||||
|
||||
let fontSize = settings.fontSize
|
||||
let fontSize = presentationData.fontSize
|
||||
let dateTimeFormat = presentationData.dateTimeFormat
|
||||
let largeEmoji = presentationData.largeEmoji
|
||||
let disableAnimations = presentationData.disableAnimations
|
||||
@ -700,9 +701,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
themeReference = settings.theme
|
||||
}
|
||||
|
||||
let theme = presentationData.theme
|
||||
let accentColor = settings.themeSpecificAccentColors[themeReference.index]
|
||||
let wallpaper = presentationData.chatWallpaper
|
||||
|
||||
let rightNavigationButton = ItemListNavigationButton(content: .icon(.add), style: .regular, enabled: true, action: {
|
||||
moreImpl?()
|
||||
@ -724,7 +723,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
availableThemes.append(contentsOf: cloudThemes)
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: themeSettingsControllerEntries(presentationData: presentationData, presentationThemeSettings: settings, theme: theme, themeReference: themeReference, themeSpecificAccentColors: settings.themeSpecificAccentColors, availableThemes: availableThemes, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, largeEmoji: largeEmoji, disableAnimations: disableAnimations, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: themeSettingsControllerEntries(presentationData: presentationData, presentationThemeSettings: settings, themeReference: themeReference, availableThemes: availableThemes, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
|
||||
|
||||
let previousThemeIndex = previousThemeReference.swap(themeReference)?.index
|
||||
let previousAccentColor = previousAccentColor.swap(accentColor)
|
||||
|
@ -31,36 +31,22 @@ private func textInputBackgroundImage(fieldColor: UIColor, strokeColor: UIColor,
|
||||
}
|
||||
}
|
||||
|
||||
private func generateSwatchImage(theme: PresentationTheme, color: UIColor) -> UIImage? {
|
||||
private func generateSwatchBorderImage(theme: PresentationTheme) -> UIImage? {
|
||||
return nil
|
||||
return generateImage(CGSize(width: 21.0, height: 21.0), rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.clear(bounds)
|
||||
|
||||
let fillColor = color
|
||||
var strokeColor: UIColor?
|
||||
let inputBackgroundColor = theme.chat.inputPanel.inputBackgroundColor
|
||||
if fillColor.distance(to: inputBackgroundColor) < 200 {
|
||||
strokeColor = theme.chat.inputPanel.inputStrokeColor
|
||||
if strokeColor!.distance(to: inputBackgroundColor) < 200 {
|
||||
strokeColor = theme.chat.inputPanel.inputControlColor
|
||||
}
|
||||
}
|
||||
|
||||
context.setFillColor(fillColor.cgColor)
|
||||
context.setLineWidth(1.0)
|
||||
|
||||
context.fillEllipse(in: bounds)
|
||||
if let strokeColor = strokeColor {
|
||||
context.setStrokeColor(strokeColor.cgColor)
|
||||
context.strokeEllipse(in: bounds.insetBy(dx: 1.0, dy: 1.0))
|
||||
}
|
||||
context.setStrokeColor(theme.chat.inputPanel.inputControlColor.cgColor)
|
||||
context.strokeEllipse(in: bounds.insetBy(dx: 1.0, dy: 1.0))
|
||||
})
|
||||
}
|
||||
|
||||
private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
||||
private var theme: PresentationTheme
|
||||
|
||||
private let swatchNode: ASImageNode
|
||||
private let swatchNode: ASDisplayNode
|
||||
private let borderNode: ASImageNode
|
||||
private let removeButton: HighlightableButtonNode
|
||||
private let textBackgroundNode: ASImageNode
|
||||
private let selectionNode: ASDisplayNode
|
||||
@ -123,9 +109,13 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
||||
self.prefixNode = ASTextNode()
|
||||
self.prefixNode.attributedText = NSAttributedString(string: "#", font: Font.regular(17.0), textColor: self.theme.chat.inputPanel.inputTextColor)
|
||||
|
||||
self.swatchNode = ASImageNode()
|
||||
self.swatchNode.displaysAsynchronously = false
|
||||
self.swatchNode.displayWithoutProcessing = true
|
||||
self.swatchNode = ASDisplayNode()
|
||||
self.swatchNode.cornerRadius = 10.5
|
||||
|
||||
self.borderNode = ASImageNode()
|
||||
self.borderNode.displaysAsynchronously = false
|
||||
self.borderNode.displayWithoutProcessing = true
|
||||
self.borderNode.image = generateSwatchBorderImage(theme: theme)
|
||||
|
||||
self.removeButton = HighlightableButtonNode()
|
||||
self.removeButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeColorRemoveIcon"), color: theme.chat.inputPanel.inputControlColor), for: .normal)
|
||||
@ -137,6 +127,7 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
||||
self.addSubnode(self.textFieldNode)
|
||||
self.addSubnode(self.prefixNode)
|
||||
self.addSubnode(self.swatchNode)
|
||||
self.addSubnode(self.borderNode)
|
||||
self.addSubnode(self.removeButton)
|
||||
|
||||
self.removeButton.addTarget(self, action: #selector(self.removePressed), forControlEvents: .touchUpInside)
|
||||
@ -172,6 +163,8 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
||||
self.textFieldNode.textField.tintColor = self.theme.list.itemAccentColor
|
||||
|
||||
self.selectionNode.backgroundColor = theme.chat.inputPanel.panelControlAccentColor.withAlphaComponent(0.2)
|
||||
self.borderNode.image = generateSwatchBorderImage(theme: theme)
|
||||
self.updateBorderVisibility()
|
||||
}
|
||||
|
||||
func setColor(_ color: UIColor, isDefault: Bool = false, update: Bool = true, ended: Bool = true) {
|
||||
@ -186,7 +179,20 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
||||
if update {
|
||||
self.colorChanged?(color, ended)
|
||||
}
|
||||
self.swatchNode.image = generateSwatchImage(theme: self.theme, color: color)
|
||||
self.swatchNode.backgroundColor = color
|
||||
self.updateBorderVisibility()
|
||||
}
|
||||
|
||||
private func updateBorderVisibility() {
|
||||
guard let color = self.swatchNode.backgroundColor else {
|
||||
return
|
||||
}
|
||||
let inputBackgroundColor = self.theme.chat.inputPanel.inputBackgroundColor
|
||||
if color.distance(to: inputBackgroundColor) < 200 {
|
||||
self.borderNode.alpha = 1.0
|
||||
} else {
|
||||
self.borderNode.alpha = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func removePressed() {
|
||||
@ -294,7 +300,9 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
||||
func updateLayout(size: CGSize, condensed: Bool, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, condensed)
|
||||
|
||||
transition.updateFrame(node: self.swatchNode, frame: CGRect(origin: CGPoint(x: 6.0, y: 6.0), size: CGSize(width: 21.0, height: 21.0)))
|
||||
let swatchFrame = CGRect(origin: CGPoint(x: 6.0, y: 6.0), size: CGSize(width: 21.0, height: 21.0))
|
||||
transition.updateFrame(node: self.swatchNode, frame: swatchFrame)
|
||||
transition.updateFrame(node: self.borderNode, frame: swatchFrame)
|
||||
|
||||
let textPadding: CGFloat = condensed ? 31.0 : 37.0
|
||||
|
||||
|
@ -5,19 +5,22 @@ import SwiftSignalKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
|
||||
private let shadowImage: UIImage = {
|
||||
return generateImage(CGSize(width: 45.0, height: 45.0), opaque: false, scale: nil, rotatedContext: { size, context in
|
||||
context.setBlendMode(.clear)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
private let knobBackgroundImage: UIImage? = {
|
||||
return generateImage(CGSize(width: 45.0, height: 45.0), contextGenerator: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.clear(bounds)
|
||||
|
||||
context.setShadow(offset: CGSize(width: 0.0, height: -1.5), blur: 4.5, color: UIColor(rgb: 0x000000, alpha: 0.4).cgColor)
|
||||
context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.4).cgColor)
|
||||
context.fillEllipse(in: bounds.insetBy(dx: 3.0 + UIScreenPixel, dy: 3.0 + UIScreenPixel))
|
||||
|
||||
context.setBlendMode(.normal)
|
||||
context.setShadow(offset: CGSize(width: 0.0, height: 1.5), blur: 4.5, color: UIColor(rgb: 0x000000, alpha: 0.5).cgColor)
|
||||
context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.5).cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: 3.0 + UIScreenPixel, dy: 3.0 + UIScreenPixel))
|
||||
})!
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
context.fillEllipse(in: bounds.insetBy(dx: 3.0, dy: 3.0))
|
||||
}, opaque: false, scale: nil)
|
||||
}()
|
||||
|
||||
private let pointerImage: UIImage = {
|
||||
private let pointerImage: UIImage? = {
|
||||
return generateImage(CGSize(width: 12.0, height: 55.0), opaque: false, scale: nil, rotatedContext: { size, context in
|
||||
context.setBlendMode(.clear)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
@ -43,7 +46,33 @@ private let pointerImage: UIImage = {
|
||||
context.addLine(to: CGPoint(x: size.width - lineWidth / 2.0, y: size.height - lineWidth / 2.0))
|
||||
context.closePath()
|
||||
context.drawPath(using: .fillStroke)
|
||||
})!
|
||||
})
|
||||
}()
|
||||
|
||||
private let brightnessMaskImage: UIImage? = {
|
||||
return generateImage(CGSize(width: 36.0, height: 36.0), opaque: false, scale: nil, rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
context.fill(bounds)
|
||||
|
||||
context.setBlendMode(.clear)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fillEllipse(in: bounds)
|
||||
})?.stretchableImage(withLeftCapWidth: 18, topCapHeight: 18)
|
||||
}()
|
||||
|
||||
private let brightnessGradientImage: UIImage? = {
|
||||
return generateImage(CGSize(width: 160.0, height: 1.0), opaque: false, scale: nil, rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.clear(bounds)
|
||||
|
||||
let gradientColors = [UIColor.black.withAlphaComponent(0.0), UIColor.black].map { $0.cgColor } as CFArray
|
||||
var locations: [CGFloat] = [0.0, 1.0]
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
|
||||
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
|
||||
})
|
||||
}()
|
||||
|
||||
private final class HSBParameter: NSObject {
|
||||
@ -63,53 +92,46 @@ private final class WallpaperColorKnobNode: ASDisplayNode {
|
||||
var hsb: (CGFloat, CGFloat, CGFloat) = (0.0, 0.0, 1.0) {
|
||||
didSet {
|
||||
if self.hsb != oldValue {
|
||||
self.setNeedsDisplay()
|
||||
let color = UIColor(hue: hsb.0, saturation: hsb.1, brightness: hsb.2, alpha: 1.0)
|
||||
self.colorNode.backgroundColor = color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let backgroundNode: ASImageNode
|
||||
private let colorNode: ASDisplayNode
|
||||
|
||||
override init() {
|
||||
self.backgroundNode = ASImageNode()
|
||||
self.backgroundNode.displaysAsynchronously = false
|
||||
self.backgroundNode.displayWithoutProcessing = true
|
||||
self.backgroundNode.image = knobBackgroundImage
|
||||
|
||||
self.colorNode = ASDisplayNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.isOpaque = false
|
||||
self.displaysAsynchronously = false
|
||||
self.isUserInteractionEnabled = false
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.colorNode)
|
||||
}
|
||||
|
||||
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
|
||||
return HSBParameter(hue: self.hsb.0, saturation: self.hsb.1, value: self.hsb.2)
|
||||
}
|
||||
|
||||
@objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
|
||||
guard let parameters = parameters as? HSBParameter else {
|
||||
return
|
||||
}
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
override func layout() {
|
||||
super.layout()
|
||||
|
||||
if !isRasterizing {
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fill(bounds)
|
||||
}
|
||||
|
||||
context.draw(shadowImage.cgImage!, in: bounds)
|
||||
|
||||
context.setBlendMode(.normal)
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
context.fillEllipse(in: bounds.insetBy(dx: 3.0, dy: 3.0))
|
||||
|
||||
let color = UIColor(hue: parameters.hue, saturation: parameters.saturation, brightness: parameters.value, alpha: 1.0)
|
||||
context.setFillColor(color.cgColor)
|
||||
|
||||
let borderWidth: CGFloat = 7.0
|
||||
context.fillEllipse(in: bounds.insetBy(dx: borderWidth - UIScreenPixel, dy: borderWidth - UIScreenPixel))
|
||||
self.backgroundNode.frame = self.bounds
|
||||
self.colorNode.frame = self.bounds.insetBy(dx: 7.0 - UIScreenPixel, dy: 7.0 - UIScreenPixel)
|
||||
self.colorNode.cornerRadius = self.colorNode.frame.width / 2.0
|
||||
}
|
||||
}
|
||||
|
||||
private final class WallpaperColorHueSaturationNode: ASDisplayNode {
|
||||
var value: CGFloat = 1.0 {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
if self.value != oldValue {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,7 +143,7 @@ private final class WallpaperColorHueSaturationNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
|
||||
return HSBParameter(hue: 1.0, saturation: 1.0, value: self.value)
|
||||
return HSBParameter(hue: 1.0, saturation: 1.0, value: 1.0)
|
||||
}
|
||||
|
||||
@objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
|
||||
@ -147,47 +169,43 @@ private final class WallpaperColorHueSaturationNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
private final class WallpaperColorBrightnessNode: ASDisplayNode {
|
||||
private let gradientNode: ASImageNode
|
||||
private let maskNode: ASImageNode
|
||||
|
||||
var hsb: (CGFloat, CGFloat, CGFloat) = (0.0, 1.0, 1.0) {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
if self.hsb.0 != oldValue.0 || self.hsb.1 != oldValue.1 {
|
||||
let color = UIColor(hue: hsb.0, saturation: hsb.1, brightness: 1.0, alpha: 1.0)
|
||||
self.backgroundColor = color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override init() {
|
||||
self.gradientNode = ASImageNode()
|
||||
self.gradientNode.displaysAsynchronously = false
|
||||
self.gradientNode.displayWithoutProcessing = true
|
||||
self.gradientNode.image = brightnessGradientImage
|
||||
self.gradientNode.contentMode = .scaleToFill
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
self.maskNode.displaysAsynchronously = false
|
||||
self.maskNode.displayWithoutProcessing = true
|
||||
self.maskNode.image = brightnessMaskImage
|
||||
self.maskNode.contentMode = .scaleToFill
|
||||
|
||||
super.init()
|
||||
|
||||
self.isOpaque = true
|
||||
self.displaysAsynchronously = false
|
||||
self.addSubnode(self.gradientNode)
|
||||
self.addSubnode(self.maskNode)
|
||||
}
|
||||
|
||||
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
|
||||
return HSBParameter(hue: self.hsb.0, saturation: self.hsb.1, value: self.hsb.2)
|
||||
}
|
||||
|
||||
@objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
|
||||
guard let parameters = parameters as? HSBParameter else {
|
||||
return
|
||||
}
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
override func layout() {
|
||||
super.layout()
|
||||
|
||||
context.setFillColor(UIColor(white: parameters.value, alpha: 1.0).cgColor)
|
||||
context.fill(bounds)
|
||||
|
||||
let path = UIBezierPath(roundedRect: bounds, cornerRadius: bounds.height / 2.0)
|
||||
context.addPath(path.cgPath)
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
context.fillPath()
|
||||
|
||||
let innerPath = UIBezierPath(roundedRect: bounds.insetBy(dx: 1.0, dy: 1.0), cornerRadius: bounds.height / 2.0)
|
||||
context.addPath(innerPath.cgPath)
|
||||
context.clip()
|
||||
|
||||
let color = UIColor(hue: parameters.hue, saturation: parameters.saturation, brightness: 1.0, alpha: 1.0)
|
||||
let colors = [color.cgColor, UIColor.black.cgColor]
|
||||
var locations: [CGFloat] = [0.0, 1.0]
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
|
||||
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: bounds.width, y: 0.0), options: CGGradientDrawingOptions())
|
||||
self.gradientNode.frame = self.bounds
|
||||
self.maskNode.frame = self.bounds
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,7 +268,7 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
private func update() {
|
||||
self.backgroundColor = UIColor(white: self.colorHsb.2, alpha: 1.0)
|
||||
self.backgroundColor = .white
|
||||
self.colorNode.value = self.colorHsb.2
|
||||
self.brightnessNode.hsb = self.colorHsb
|
||||
self.colorKnobNode.hsb = self.colorHsb
|
||||
|
@ -271,7 +271,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
patternArguments = PatternWallpaperArguments(colors: patternColors, rotation: file.settings.rotation)
|
||||
patternArguments = PatternWallpaperArguments(colors: patternColors, rotation: file.settings.rotation, preview: self.arguments.colorPreview)
|
||||
|
||||
self.backgroundColor = patternColor.withAlphaComponent(1.0)
|
||||
|
||||
@ -295,7 +295,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
|
||||
self.colorPreview = self.arguments.colorPreview
|
||||
|
||||
signal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: self.arguments.colorPreview ? .fastScreen : .screen, autoFetchFullSize: true)
|
||||
signal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true)
|
||||
colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: context.account.postbox.mediaBox)
|
||||
|
||||
isBlurrable = false
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import TelegramCore
|
||||
import SyncCore
|
||||
|
||||
@ -46,4 +47,12 @@ public extension TelegramWallpaper {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var dimensions: CGSize? {
|
||||
if case let .file(file) = self {
|
||||
return file.file.dimensions?.cgSize
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -291,7 +291,6 @@ public func wallpaperImage(account: Account, accountManager: AccountManager, fil
|
||||
|
||||
public enum PatternWallpaperDrawMode {
|
||||
case thumbnail
|
||||
case fastScreen
|
||||
case screen
|
||||
}
|
||||
|
||||
@ -322,8 +321,6 @@ private func patternWallpaperDatas(account: Account, accountManager: AccountMana
|
||||
switch mode {
|
||||
case .thumbnail:
|
||||
size = largestRepresentation.dimensions.cgSize.fitted(CGSize(width: 640.0, height: 640.0))
|
||||
case .fastScreen:
|
||||
size = largestRepresentation.dimensions.cgSize.fitted(CGSize(width: 1280.0, height: 1280.0))
|
||||
default:
|
||||
size = nil
|
||||
}
|
||||
@ -407,18 +404,23 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da
|
||||
}
|
||||
|
||||
var scale: CGFloat = 0.0
|
||||
if case .fastScreen = mode {
|
||||
scale = max(1.0, UIScreenScale - 1.0)
|
||||
}
|
||||
|
||||
return .single((thumbnailData, fullSizeData, fullSizeComplete))
|
||||
|> map { (thumbnailData, fullSizeData, fullSizeComplete) in
|
||||
var fullSizeImage: CGImage?
|
||||
var scaledSizeImage: CGImage?
|
||||
if let fullSizeData = fullSizeData, fullSizeComplete {
|
||||
let options = NSMutableDictionary()
|
||||
options[kCGImageSourceShouldCache as NSString] = false as NSNumber
|
||||
if let imageSource = CGImageSourceCreateWithData(fullSizeData as CFData, nil), let image = CGImageSourceCreateImageAtIndex(imageSource, 0, options as CFDictionary) {
|
||||
fullSizeImage = image
|
||||
|
||||
let options = NSMutableDictionary()
|
||||
options.setValue(960 as NSNumber, forKey: kCGImageSourceThumbnailMaxPixelSize as String)
|
||||
options.setValue(true as NSNumber, forKey: kCGImageSourceCreateThumbnailFromImageAlways as String)
|
||||
if let imageSource = CGImageSourceCreateWithData(fullSizeData as CFData, nil), let image = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options) {
|
||||
scaledSizeImage = image
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -433,6 +435,7 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da
|
||||
if abs(fittedSize.height - arguments.boundingSize.height).isLessThanOrEqualTo(CGFloat(1.0)) {
|
||||
fittedSize.height = arguments.boundingSize.height
|
||||
}
|
||||
fittedSize = fittedSize.aspectFilled(arguments.drawingRect.size)
|
||||
|
||||
let fittedRect = CGRect(origin: CGPoint(x: drawingRect.origin.x + (drawingRect.size.width - fittedSize.width) / 2.0, y: drawingRect.origin.y + (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize)
|
||||
|
||||
@ -446,7 +449,7 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da
|
||||
let color = combinedColor.withAlphaComponent(1.0)
|
||||
let intensity = combinedColor.alpha
|
||||
|
||||
let context = DrawingContext(size: arguments.drawingSize, scale: fullSizeImage == nil ? 1.0 : scale, clear: true)
|
||||
let context = DrawingContext(size: arguments.drawingSize, scale: fullSizeImage == nil ? 1.0 : scale, clear: !arguments.corners.isEmpty)
|
||||
context.withFlippedContext { c in
|
||||
c.setBlendMode(.copy)
|
||||
|
||||
@ -473,10 +476,11 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da
|
||||
c.restoreGState()
|
||||
}
|
||||
|
||||
if let fullSizeImage = fullSizeImage {
|
||||
let image = customArguments.preview ? (scaledSizeImage ?? fullSizeImage) : fullSizeImage
|
||||
if let image = image {
|
||||
c.setBlendMode(.normal)
|
||||
c.interpolationQuality = .medium
|
||||
c.clip(to: fittedRect, mask: fullSizeImage)
|
||||
c.interpolationQuality = customArguments.preview ? .low : .medium
|
||||
c.clip(to: fittedRect, mask: image)
|
||||
|
||||
if colors.count == 1 {
|
||||
c.setFillColor(patternColor(for: color, intensity: intensity, prominent: prominent).cgColor)
|
||||
|
Loading…
x
Reference in New Issue
Block a user