mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-05 05:51:42 +00:00
Wallpaper fixes
This commit is contained in:
parent
4e46f2f90e
commit
c3b974f6e0
@ -3157,11 +3157,11 @@
|
||||
09F664CF21EBCFB900AB7E26 /* WallpaperCropNode.swift */,
|
||||
09DD5D5121ED175300D7007A /* WallpaperColorPickerNode.swift */,
|
||||
0900678C21ED5EA800530762 /* WallpaperColorPanelNode.swift */,
|
||||
0910B0F021FB3DE100F8F87D /* WallpaperPatternPanelNode.swift */,
|
||||
091417F121EF4E5D00C8325A /* WallpaperGalleryController.swift */,
|
||||
09749BCC21F23139008FDDE9 /* WallpaperGalleryDecorationNode.swift */,
|
||||
091417F321EF4F5F00C8325A /* WallpaperGalleryItem.swift */,
|
||||
D05174AA1EAA5B4700A1BF36 /* WallpaperGalleryToolbarNode.swift */,
|
||||
0910B0F021FB3DE100F8F87D /* WallpaperPatternPanelNode.swift */,
|
||||
);
|
||||
name = Themes;
|
||||
sourceTree = "<group>";
|
||||
|
||||
@ -164,7 +164,7 @@ func serviceColor(with color: UIColor) -> UIColor {
|
||||
var brightness: CGFloat = 0.0
|
||||
var alpha: CGFloat = 0.0
|
||||
if color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
|
||||
if saturation > 0.0 || brightness < 1.0 {
|
||||
if saturation > 0.0 {
|
||||
saturation = min(1.0, saturation + 0.05 + 0.1 * (1.0 - saturation))
|
||||
}
|
||||
brightness = max(0.0, brightness * 0.65)
|
||||
|
||||
@ -416,7 +416,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
if case let .file(file, patternColor) = wallpaper.content {
|
||||
if case let .file(file, _) = wallpaper.content {
|
||||
updatedFetchControls = FetchControls(fetch: { manual in
|
||||
if let strongSelf = self {
|
||||
strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(account: account, message: message, file: file, userInitiated: manual).start())
|
||||
|
||||
@ -2740,76 +2740,6 @@ func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?, albumA
|
||||
}
|
||||
}
|
||||
|
||||
func photoWallpaper(postbox: Postbox, photoLibraryResource: PhotoLibraryMediaResource) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
let thumbnail = fetchPhotoLibraryImage(localIdentifier: photoLibraryResource.localIdentifier, thumbnail: true)
|
||||
let fullSize = fetchPhotoLibraryImage(localIdentifier: photoLibraryResource.localIdentifier, thumbnail: false)
|
||||
|
||||
return (thumbnail |> then(fullSize))
|
||||
|> map { result in
|
||||
var sourceImage = result?.0
|
||||
let isThumbnail = result?.1 ?? false
|
||||
|
||||
return { arguments in
|
||||
let context = DrawingContext(size: arguments.drawingSize, scale: 1.0, clear: true)
|
||||
|
||||
let dimensions = sourceImage?.size
|
||||
|
||||
if let thumbnailImage = sourceImage?.cgImage, isThumbnail {
|
||||
var fittedSize = arguments.imageSize
|
||||
if abs(fittedSize.width - arguments.boundingSize.width).isLessThanOrEqualTo(CGFloat(1.0)) {
|
||||
fittedSize.width = arguments.boundingSize.width
|
||||
}
|
||||
if abs(fittedSize.height - arguments.boundingSize.height).isLessThanOrEqualTo(CGFloat(1.0)) {
|
||||
fittedSize.height = arguments.boundingSize.height
|
||||
}
|
||||
|
||||
let thumbnailSize = CGSize(width: thumbnailImage.width, height: thumbnailImage.height)
|
||||
|
||||
let initialThumbnailContextFittingSize = fittedSize.fitted(CGSize(width: 100.0, height: 100.0))
|
||||
|
||||
let thumbnailContextSize = thumbnailSize.aspectFitted(initialThumbnailContextFittingSize)
|
||||
let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0)
|
||||
thumbnailContext.withFlippedContext { c in
|
||||
c.interpolationQuality = .none
|
||||
c.draw(thumbnailImage, in: CGRect(origin: CGPoint(), size: thumbnailContextSize))
|
||||
}
|
||||
telegramFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes)
|
||||
|
||||
var thumbnailContextFittingSize = CGSize(width: floor(arguments.drawingSize.width * 0.5), height: floor(arguments.drawingSize.width * 0.5))
|
||||
if thumbnailContextFittingSize.width < 150.0 || thumbnailContextFittingSize.height < 150.0 {
|
||||
thumbnailContextFittingSize = thumbnailContextFittingSize.aspectFilled(CGSize(width: 150.0, height: 150.0))
|
||||
}
|
||||
|
||||
if thumbnailContextFittingSize.width > thumbnailContextSize.width {
|
||||
let additionalContextSize = thumbnailContextFittingSize
|
||||
let additionalBlurContext = DrawingContext(size: additionalContextSize, scale: 1.0)
|
||||
additionalBlurContext.withFlippedContext { c in
|
||||
c.interpolationQuality = .default
|
||||
if let image = thumbnailContext.generateImage()?.cgImage {
|
||||
c.draw(image, in: CGRect(origin: CGPoint(), size: additionalContextSize))
|
||||
}
|
||||
}
|
||||
telegramFastBlur(Int32(additionalContextSize.width), Int32(additionalContextSize.height), Int32(additionalBlurContext.bytesPerRow), additionalBlurContext.bytes)
|
||||
sourceImage = additionalBlurContext.generateImage()
|
||||
} else {
|
||||
sourceImage = thumbnailContext.generateImage()
|
||||
}
|
||||
}
|
||||
|
||||
context.withFlippedContext { c in
|
||||
c.setBlendMode(.copy)
|
||||
if let sourceImage = sourceImage, let cgImage = sourceImage.cgImage, let dimensions = dimensions {
|
||||
let imageSize = dimensions.aspectFilled(arguments.drawingRect.size)
|
||||
let fittedRect = CGRect(origin: CGPoint(x: floor((arguments.drawingRect.size.width - imageSize.width) / 2.0), y: floor((arguments.drawingRect.size.height - imageSize.height) / 2.0)), size: imageSize)
|
||||
c.draw(cgImage, in: fittedRect)
|
||||
}
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func securePhoto(account: Account, resource: TelegramMediaResource, accessContext: SecureIdAccessContext) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
return securePhotoInternal(account: account, resource: resource, accessContext: accessContext) |> map { $0.1 }
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -180,7 +180,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
||||
switch self.chatWallpaper {
|
||||
case let .image(representations, _):
|
||||
return representations.map { $0.resource.id }
|
||||
case let .file(_, _, _, _, _, _, file, _):
|
||||
case let .file(_, _, _, _, _, _, _, file, _):
|
||||
var resources: [MediaResourceId] = []
|
||||
resources.append(file.resource.id)
|
||||
resources.append(contentsOf: file.previewRepresentations.map { $0.resource.id })
|
||||
|
||||
Binary file not shown.
@ -45,6 +45,15 @@ final class SearchDisplayController {
|
||||
self?.searchBar.prefixString = prefix
|
||||
self?.searchBar.text = query
|
||||
}
|
||||
self.contentNode.setPlaceholder = { [weak self] string in
|
||||
guard string != self?.searchBar.placeholderString?.string else {
|
||||
return
|
||||
}
|
||||
if let mutableAttributedString = self?.searchBar.placeholderString?.mutableCopy() as? NSMutableAttributedString {
|
||||
mutableAttributedString.mutableString.setString(string)
|
||||
self?.searchBar.placeholderString = mutableAttributedString
|
||||
}
|
||||
}
|
||||
|
||||
self.isSearchingDisposable = (contentNode.isSearching
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
|
||||
@ -7,6 +7,7 @@ class SearchDisplayControllerContentNode: ASDisplayNode {
|
||||
final var dismissInput: (() -> Void)?
|
||||
final var cancel: (() -> Void)?
|
||||
final var setQuery: ((NSAttributedString?, String) -> Void)?
|
||||
final var setPlaceholder: ((String) -> Void)?
|
||||
|
||||
var isSearching: Signal<Bool, NoError> {
|
||||
return .single(false)
|
||||
|
||||
@ -246,6 +246,40 @@ final class ThemeGridController: ViewController {
|
||||
if let strongSelf = self {
|
||||
strongSelf.shareWallpapers(wallpapers)
|
||||
}
|
||||
}, resetWallpapers: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme)
|
||||
let items: [ActionSheetItem] = [
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Wallpaper_ResetWallpapersConfirmation, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
if let strongSelf = self {
|
||||
strongSelf.scrollToTop?()
|
||||
|
||||
let controller = OverlayStatusController(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, type: .loading(cancelled: nil))
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
|
||||
let _ = resetWallpapers(account: strongSelf.account).start(completed: { [weak self, weak controller] in
|
||||
let _ = (telegramWallpapers(postbox: strongSelf.account.postbox, network: strongSelf.account.network)
|
||||
|> deliverOnMainQueue).start(completed: { [weak self, weak controller] in
|
||||
controller?.dismiss()
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerNode.updateWallpapers()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
]
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items),
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])
|
||||
])
|
||||
strongSelf.present(actionSheet, in: .window(.root))
|
||||
}
|
||||
}, popViewController: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let _ = (strongSelf.navigationController as? NavigationController)?.popViewController(animated: true)
|
||||
@ -346,6 +380,17 @@ final class ThemeGridController: ViewController {
|
||||
|
||||
let account = self.account
|
||||
let updateWallpaper: (TelegramWallpaper) -> Void = { [weak self] wallpaper in
|
||||
var resource: MediaResource?
|
||||
if case let .image(representations, _) = wallpaper, let representation = largestImageRepresentation(representations) {
|
||||
resource = representation.resource
|
||||
} else if case let .file(file) = wallpaper {
|
||||
resource = file.file.resource
|
||||
}
|
||||
|
||||
if let resource = resource {
|
||||
let _ = account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start(completed: {})
|
||||
}
|
||||
|
||||
let _ = (updatePresentationThemeSettingsInteractively(postbox: account.postbox, { current in
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
themeSpecificChatWallpapers[current.theme.index] = wallpaper
|
||||
@ -366,7 +411,7 @@ final class ThemeGridController: ViewController {
|
||||
|
||||
let _ = uploadWallpaper(account: account, resource: resource).start(next: { status in
|
||||
if case let .complete(wallpaper) = status {
|
||||
if mode.contains(.blur), case let .file(_, _, _, _, _, _, file, _) = wallpaper {
|
||||
if mode.contains(.blur), case let .file(_, _, _, _, _, _, _, file, _) = wallpaper {
|
||||
let _ = account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: {
|
||||
updateWallpaper(wallpaper)
|
||||
})
|
||||
@ -377,6 +422,7 @@ final class ThemeGridController: ViewController {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
if mode.contains(.blur) {
|
||||
let _ = account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: {
|
||||
apply()
|
||||
@ -395,7 +441,7 @@ final class ThemeGridController: ViewController {
|
||||
for wallpaper in wallpapers {
|
||||
var item: String?
|
||||
switch wallpaper {
|
||||
case let .file(_, _, _, _, isPattern, slug, _, settings):
|
||||
case let .file(_, _, _, _, isPattern, _, slug, _, settings):
|
||||
var options: [String] = []
|
||||
if isPattern {
|
||||
if let color = settings.color {
|
||||
|
||||
@ -25,8 +25,8 @@ private func areWallpapersEqual(_ lhs: TelegramWallpaper, _ rhs: TelegramWallpap
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .file(_, _, _, _, _, lhsSlug, _, lhsSettings):
|
||||
if case let .file(_, _, _, _, _, rhsSlug, _, rhsSettings) = rhs, lhsSlug == rhsSlug, lhsSettings.color == rhsSettings.color && lhsSettings.intensity == rhsSettings.intensity {
|
||||
case let .file(_, _, _, _, _, _, lhsSlug, _, lhsSettings):
|
||||
if case let .file(_, _, _, _, _, _, rhsSlug, _, rhsSettings) = rhs, lhsSlug == rhsSlug, lhsSettings.color == rhsSettings.color && lhsSettings.intensity == rhsSettings.intensity {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -90,15 +90,15 @@ private struct ThemeGridControllerEntry: Comparable, Identifiable {
|
||||
case .builtin:
|
||||
return 0
|
||||
case let .color(color):
|
||||
return (Int64(0) << 32) | Int64(bitPattern: UInt64(UInt32(bitPattern: color)))
|
||||
case let .file(id, _, _, _, _, _, _, settings):
|
||||
return (Int64(1) << 32) | Int64(bitPattern: UInt64(UInt32(bitPattern: color)))
|
||||
case let .file(id, _, _, _, _, _, _, _, settings):
|
||||
var hash: Int = id.hashValue
|
||||
hash = hash &* 31 &+ (settings.color?.hashValue ?? 0)
|
||||
hash = hash &* 31 &+ (settings.intensity?.hashValue ?? 0)
|
||||
return (Int64(1) << 32) | Int64(hash)
|
||||
return (Int64(2) << 32) | Int64(hash)
|
||||
case let .image(representations, _):
|
||||
if let largest = largestImageRepresentation(representations) {
|
||||
return (Int64(2) << 32) | Int64(largest.resource.id.hashValue)
|
||||
return (Int64(3) << 32) | Int64(largest.resource.id.hashValue)
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
@ -157,43 +157,6 @@ private func selectedWallpapers(entries: [ThemeGridControllerEntry]?, state: The
|
||||
return wallpapers
|
||||
}
|
||||
|
||||
private func isDarkWallpaper(_ wallpaper: TelegramWallpaper) -> Bool {
|
||||
if case let .file(file) = wallpaper {
|
||||
if let data = file.file.immediateThumbnailData {
|
||||
let options = NSMutableDictionary()
|
||||
options.setValue(1 as NSNumber, forKey: kCGImageSourceThumbnailMaxPixelSize as String)
|
||||
options.setValue(true as NSNumber, forKey: kCGImageSourceCreateThumbnailFromImageAlways as String)
|
||||
|
||||
let thumbnailData = decodeTinyThumbnail(data: data)
|
||||
if let thumbnailData = thumbnailData, let imageSource = CGImageSourceCreateWithData(thumbnailData as CFData, nil), let image = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) {
|
||||
let pixelData = image.dataProvider!.data
|
||||
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
|
||||
|
||||
let point = CGPoint()
|
||||
let pixelInfo: Int = ((Int(image.width) * Int(point.y)) + Int(point.x)) * 4
|
||||
let r = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
|
||||
let g = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
|
||||
let b = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)
|
||||
|
||||
let color = UIColor(red: r, green: g, blue: b, alpha: 1.0)
|
||||
|
||||
var hue: CGFloat = 0.0
|
||||
var saturation: CGFloat = 0.0
|
||||
var brightness: CGFloat = 0.0
|
||||
var alpha: CGFloat = 0.0
|
||||
if color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
|
||||
if brightness > 0.5 {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
final class ThemeGridControllerNode: ASDisplayNode {
|
||||
private let account: Account
|
||||
private var presentationData: PresentationData
|
||||
@ -203,6 +166,8 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
private let presentGallery: () -> Void
|
||||
private let presentColors: () -> Void
|
||||
private let emptyStateUpdated: (Bool) -> Void
|
||||
private let resetWallpapers: () -> Void
|
||||
|
||||
var requestDeactivateSearch: (() -> Void)?
|
||||
|
||||
let ready = ValuePromise<Bool>()
|
||||
@ -210,15 +175,19 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
|
||||
private var backgroundNode: ASDisplayNode
|
||||
private var separatorNode: ASDisplayNode
|
||||
private var bottomBackgroundNode: ASDisplayNode
|
||||
private var bottomSeparatorNode: ASDisplayNode
|
||||
|
||||
private let colorItemNode: ItemListActionItemNode
|
||||
private var colorItem: ItemListActionItem
|
||||
|
||||
private let galleryItemNode: ItemListActionItemNode
|
||||
private var galleryItem: ItemListActionItem
|
||||
|
||||
private let descriptionItemNode: ItemListTextItemNode
|
||||
private var descriptionItem: ItemListTextItem
|
||||
private let resetItemNode: ItemListActionItemNode
|
||||
private var resetItem: ItemListActionItem
|
||||
private let resetDescriptionItemNode: ItemListTextItemNode
|
||||
private var resetDescriptionItem: ItemListTextItem
|
||||
|
||||
private var selectionPanel: ThemeGridSelectionPanelNode?
|
||||
private var selectionPanelSeparatorNode: ASDisplayNode?
|
||||
@ -240,13 +209,14 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
|
||||
private var disposable: Disposable?
|
||||
|
||||
init(account: Account, presentationData: PresentationData, presentPreviewController: @escaping (WallpaperListSource) -> Void, presentGallery: @escaping () -> Void, presentColors: @escaping () -> Void, emptyStateUpdated: @escaping (Bool) -> Void, deleteWallpapers: @escaping ([TelegramWallpaper], @escaping () -> Void) -> Void, shareWallpapers: @escaping ([TelegramWallpaper]) -> Void, popViewController: @escaping () -> Void) {
|
||||
init(account: Account, presentationData: PresentationData, presentPreviewController: @escaping (WallpaperListSource) -> Void, presentGallery: @escaping () -> Void, presentColors: @escaping () -> Void, emptyStateUpdated: @escaping (Bool) -> Void, deleteWallpapers: @escaping ([TelegramWallpaper], @escaping () -> Void) -> Void, shareWallpapers: @escaping ([TelegramWallpaper]) -> Void, resetWallpapers: @escaping () -> Void, popViewController: @escaping () -> Void) {
|
||||
self.account = account
|
||||
self.presentationData = presentationData
|
||||
self.presentPreviewController = presentPreviewController
|
||||
self.presentGallery = presentGallery
|
||||
self.presentColors = presentColors
|
||||
self.emptyStateUpdated = emptyStateUpdated
|
||||
self.resetWallpapers = resetWallpapers
|
||||
|
||||
self.gridNode = GridNode()
|
||||
self.gridNode.showVerticalScrollIndicator = true
|
||||
@ -257,6 +227,12 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
self.bottomBackgroundNode = ASDisplayNode()
|
||||
self.bottomBackgroundNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
|
||||
self.bottomSeparatorNode = ASDisplayNode()
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
self.colorItemNode = ItemListActionItemNode()
|
||||
self.colorItem = ItemListActionItem(theme: presentationData.theme, title: presentationData.strings.Wallpaper_SetColor, kind: .generic, alignment: .natural, sectionId: 0, style: .blocks, action: {
|
||||
presentColors()
|
||||
@ -267,6 +243,12 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
})
|
||||
self.descriptionItemNode = ItemListTextItemNode()
|
||||
self.descriptionItem = ItemListTextItem(theme: presentationData.theme, text: .plain(presentationData.strings.Wallpaper_SetCustomBackgroundInfo), sectionId: 0)
|
||||
self.resetItemNode = ItemListActionItemNode()
|
||||
self.resetItem = ItemListActionItem(theme: presentationData.theme, title: presentationData.strings.Wallpaper_ResetWallpapers, kind: .generic, alignment: .natural, sectionId: 0, style: .blocks, action: {
|
||||
resetWallpapers()
|
||||
})
|
||||
self.resetDescriptionItemNode = ItemListTextItemNode()
|
||||
self.resetDescriptionItem = ItemListTextItem(theme: presentationData.theme, text: .plain(presentationData.strings.Wallpaper_ResetWallpapersInfo), sectionId: 0)
|
||||
|
||||
self.currentState = ThemeGridControllerNodeState(editing: false, selectedIndices: Set())
|
||||
self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true)
|
||||
@ -288,9 +270,13 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
|
||||
self.gridNode.addSubnode(self.backgroundNode)
|
||||
self.gridNode.addSubnode(self.separatorNode)
|
||||
self.gridNode.addSubnode(self.bottomBackgroundNode)
|
||||
self.gridNode.addSubnode(self.bottomSeparatorNode)
|
||||
self.gridNode.addSubnode(self.colorItemNode)
|
||||
self.gridNode.addSubnode(self.galleryItemNode)
|
||||
self.gridNode.addSubnode(self.descriptionItemNode)
|
||||
self.gridNode.addSubnode(self.resetItemNode)
|
||||
self.gridNode.addSubnode(self.resetDescriptionItemNode)
|
||||
self.addSubnode(self.gridNode)
|
||||
|
||||
let previousEntries = Atomic<[ThemeGridControllerEntry]?>(value: nil)
|
||||
@ -357,7 +343,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
if presentationData.theme.overallDarkAppearance {
|
||||
var darkWallpapers: [TelegramWallpaper] = []
|
||||
for wallpaper in wallpapers {
|
||||
if isDarkWallpaper(wallpaper) {
|
||||
if case let .file(file) = wallpaper, file.isDark {
|
||||
darkWallpapers.append(wallpaper)
|
||||
} else {
|
||||
sortedWallpapers.append(wallpaper)
|
||||
@ -409,6 +395,8 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
highlightedNode = strongSelf.colorItemNode
|
||||
} else if strongSelf.galleryItemNode.frame.contains(point) {
|
||||
highlightedNode = strongSelf.galleryItemNode
|
||||
} else if strongSelf.resetItemNode.frame.contains(point) {
|
||||
highlightedNode = strongSelf.resetItemNode
|
||||
}
|
||||
}
|
||||
|
||||
@ -417,10 +405,31 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
} else {
|
||||
strongSelf.colorItemNode.setHighlighted(false, at: CGPoint(), animated: true)
|
||||
strongSelf.galleryItemNode.setHighlighted(false, at: CGPoint(), animated: true)
|
||||
strongSelf.resetItemNode.setHighlighted(false, at: CGPoint(), animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.gridNode.view.addGestureRecognizer(tapRecognizer)
|
||||
|
||||
self.gridNode.presentationLayoutUpdated = { [weak self] gridLayout, transition in
|
||||
if let strongSelf = self, let (layout, _) = strongSelf.validLayout {
|
||||
transition.updateFrame(node: strongSelf.bottomBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: gridLayout.contentSize.height), size: CGSize(width: layout.size.width, height: 500.0)))
|
||||
transition.updateFrame(node: strongSelf.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: gridLayout.contentSize.height), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
|
||||
|
||||
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
|
||||
|
||||
let makeResetLayout = strongSelf.resetItemNode.asyncLayout()
|
||||
let makeResetDescriptionLayout = strongSelf.resetDescriptionItemNode.asyncLayout()
|
||||
let (resetLayout, resetApply) = makeResetLayout(strongSelf.resetItem, params, ItemListNeighbors(top: .none, bottom: .sameSection(alwaysPlain: true)))
|
||||
let (resetDescriptionLayout, resetDescriptionApply) = makeResetDescriptionLayout(strongSelf.resetDescriptionItem, params, ItemListNeighbors(top: .none, bottom: .none))
|
||||
|
||||
resetApply()
|
||||
resetDescriptionApply()
|
||||
|
||||
transition.updateFrame(node: strongSelf.resetItemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: gridLayout.contentSize.height + 35.0), size: resetLayout.contentSize))
|
||||
transition.updateFrame(node: strongSelf.resetDescriptionItemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: gridLayout.contentSize.height + 35.0 + resetLayout.contentSize.height), size: resetDescriptionLayout.contentSize))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func tapAction(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
@ -433,6 +442,8 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
self.colorItem.action()
|
||||
} else if self.galleryItemNode.frame.contains(location) {
|
||||
self.galleryItem.action()
|
||||
} else if self.resetItemNode.frame.contains(location) {
|
||||
self.resetItem.action()
|
||||
}
|
||||
default:
|
||||
break
|
||||
@ -460,6 +471,10 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
self?.presentGallery()
|
||||
})
|
||||
self.descriptionItem = ItemListTextItem(theme: presentationData.theme, text: .plain(presentationData.strings.Wallpaper_SetCustomBackgroundInfo), sectionId: 0)
|
||||
self.resetItem = ItemListActionItem(theme: presentationData.theme, title: presentationData.strings.Wallpaper_ResetWallpapers, kind: .generic, alignment: .natural, sectionId: 0, style: .blocks, action: { [weak self] in
|
||||
self?.resetWallpapers()
|
||||
})
|
||||
self.resetDescriptionItem = ItemListTextItem(theme: presentationData.theme, text: .plain(presentationData.strings.Wallpaper_ResetWallpapersInfo), sectionId: 0)
|
||||
|
||||
if let (layout, navigationBarHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||
@ -519,7 +534,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
insets.top += navigationBarHeight
|
||||
insets.left = layout.safeInsets.left
|
||||
insets.right = layout.safeInsets.right
|
||||
let scrollIndicatorInsets = insets
|
||||
var scrollIndicatorInsets = insets
|
||||
|
||||
let minSpacing: CGFloat = 8.0
|
||||
let referenceImageSize: CGSize
|
||||
@ -563,9 +578,10 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
insets.top += spacing + buttonInset
|
||||
|
||||
if self.currentState.editing {
|
||||
let panelHeight: CGFloat
|
||||
if let selectionPanel = self.selectionPanel {
|
||||
selectionPanel.selectedIndices = self.currentState.selectedIndices
|
||||
let panelHeight = selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: 0.0, transition: transition, metrics: layout.metrics)
|
||||
panelHeight = selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: 0.0, transition: transition, metrics: layout.metrics)
|
||||
transition.updateFrame(node: selectionPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: panelHeight)))
|
||||
if let selectionPanelSeparatorNode = self.selectionPanelSeparatorNode {
|
||||
transition.updateFrame(node: selectionPanelSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
|
||||
@ -584,7 +600,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
selectionPanel.backgroundColor = self.presentationData.theme.chat.inputPanel.panelBackgroundColor
|
||||
selectionPanel.controllerInteraction = self.controllerInteraction
|
||||
selectionPanel.selectedIndices = self.currentState.selectedIndices
|
||||
let panelHeight = selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: 0.0, transition: .immediate, metrics: layout.metrics)
|
||||
panelHeight = selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: 0.0, transition: .immediate, metrics: layout.metrics)
|
||||
self.selectionPanel = selectionPanel
|
||||
self.addSubnode(selectionPanel)
|
||||
|
||||
@ -601,6 +617,9 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
transition.updateFrame(node: selectionPanelBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: insets.bottom + panelHeight)))
|
||||
transition.updateFrame(node: selectionPanelSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
|
||||
}
|
||||
|
||||
insets.bottom += panelHeight
|
||||
scrollIndicatorInsets.bottom += panelHeight
|
||||
} else if let selectionPanel = self.selectionPanel {
|
||||
self.selectionPanel = nil
|
||||
transition.updateFrame(node: selectionPanel, frame: selectionPanel.frame.offsetBy(dx: 0.0, dy: selectionPanel.bounds.size.height + insets.bottom), completion: { [weak selectionPanel] _ in
|
||||
@ -618,10 +637,13 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
let makeResetDescriptionLayout = self.resetDescriptionItemNode.asyncLayout()
|
||||
let (resetDescriptionLayout, _) = makeResetDescriptionLayout(self.resetDescriptionItem, params, ItemListNeighbors(top: .none, bottom: .none))
|
||||
insets.bottom += buttonHeight + 35.0 + resetDescriptionLayout.contentSize.height + 32.0
|
||||
|
||||
self.gridNode.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: layout.size, insets: insets, scrollIndicatorInsets: scrollIndicatorInsets, preloadSize: 300.0, type: .fixed(itemSize: imageSize, fillWidth: nil, lineSpacing: spacing, itemSpacing: nil)), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
||||
|
||||
|
||||
if !hadValidLayout {
|
||||
self.dequeueTransitions()
|
||||
}
|
||||
|
||||
@ -345,7 +345,6 @@ final class ThemeGridSearchContentNode: SearchDisplayControllerContentNode {
|
||||
self.recentListNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
|
||||
self.gridNode = GridNode()
|
||||
|
||||
|
||||
self.emptyResultsTitleNode = ImmediateTextNode()
|
||||
self.emptyResultsTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.SharedMedia_SearchNoResults, font: Font.semibold(17.0), textColor: self.presentationData.theme.list.freeTextColor)
|
||||
self.emptyResultsTitleNode.textAlignment = .center
|
||||
@ -589,18 +588,22 @@ final class ThemeGridSearchContentNode: SearchDisplayControllerContentNode {
|
||||
if updateInterface {
|
||||
let prefix: NSAttributedString?
|
||||
let text: String
|
||||
let placeholder: String
|
||||
switch query {
|
||||
case let .generic(query):
|
||||
prefix = nil
|
||||
text = query
|
||||
placeholder = self.presentationData.strings.Wallpaper_Search
|
||||
case let .color(color, query):
|
||||
let prefixString = NSMutableAttributedString()
|
||||
prefixString.append(NSAttributedString(string: self.presentationData.strings.WallpaperSearch_ColorPrefix, font: Font.regular(17.0), textColor: self.presentationData.theme.rootController.activeNavigationSearchBar.inputTextColor))
|
||||
prefixString.append(NSAttributedString(string: "\(color.localizedString(strings: self.presentationData.strings)) ", font: Font.regular(17.0), textColor: self.presentationData.theme.rootController.activeNavigationSearchBar.accentColor))
|
||||
prefix = prefixString
|
||||
text = query
|
||||
placeholder = self.presentationData.strings.Wallpaper_SearchShort
|
||||
}
|
||||
self.setQuery?(prefix, text)
|
||||
self.setPlaceholder?(placeholder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,12 +24,12 @@ final class ThrottledValue<T: Equatable> {
|
||||
guard self.value != value else {
|
||||
return
|
||||
}
|
||||
self.timer?.invalidate()
|
||||
let timestamp = CACurrentMediaTime()
|
||||
if timestamp > self.previousSetTimestamp + self.interval {
|
||||
self.previousSetTimestamp = timestamp
|
||||
self.valuePromise.set(value)
|
||||
} else {
|
||||
self.timer?.invalidate()
|
||||
let timer = SwiftSignalKit.Timer(timeout: self.interval, repeat: false, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.valuePromise.set(strongSelf.value)
|
||||
|
||||
@ -51,21 +51,8 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate {
|
||||
self.setColor(newValue)
|
||||
}
|
||||
}
|
||||
var intensity: Int32 {
|
||||
get {
|
||||
return self.colorPickerNode.intensity
|
||||
}
|
||||
set {
|
||||
self.colorPickerNode.intensity = newValue
|
||||
}
|
||||
}
|
||||
var colorChanged: ((UIColor, Int32?, Bool) -> Void)?
|
||||
|
||||
var adjustingPattern: Bool = false {
|
||||
didSet {
|
||||
self.colorPickerNode.adjustingPattern = self.adjustingPattern
|
||||
}
|
||||
}
|
||||
var colorChanged: ((UIColor, Bool) -> Void)?
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings) {
|
||||
self.theme = theme
|
||||
@ -110,16 +97,6 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate {
|
||||
self.colorPickerNode.colorChangeEnded = { [weak self] color in
|
||||
self?.setColor(color, updatePicker: false, ended: true)
|
||||
}
|
||||
self.colorPickerNode.intensityChanged = { [weak self] value in
|
||||
if let strongSelf = self {
|
||||
strongSelf.colorChanged?(strongSelf.color, value, false)
|
||||
}
|
||||
}
|
||||
self.colorPickerNode.intensityChangeEnded = { [weak self] value in
|
||||
if let strongSelf = self {
|
||||
strongSelf.colorChanged?(strongSelf.color, value, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@ -142,7 +119,7 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate {
|
||||
if updatePicker {
|
||||
self.colorPickerNode.color = color
|
||||
}
|
||||
self.colorChanged?(color, self.intensity, ended)
|
||||
self.colorChanged?(color, ended)
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, keyboardHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
|
||||
@ -15,18 +15,6 @@ private let shadowImage: UIImage = {
|
||||
})!
|
||||
}()
|
||||
|
||||
private let smallShadowImage: UIImage = {
|
||||
return generateImage(CGSize(width: 24.0, height: 24.0), opaque: false, scale: nil, rotatedContext: { size, context in
|
||||
context.setBlendMode(.clear)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
context.setBlendMode(.normal)
|
||||
context.setShadow(offset: CGSize(width: 0.0, height: 1.5), blur: 4.5, color: UIColor(rgb: 0x000000, alpha: 0.65).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))
|
||||
})!
|
||||
}()
|
||||
|
||||
private let pointerImage: UIImage = {
|
||||
return generateImage(CGSize(width: 12.0, height: 42.0), opaque: false, scale: nil, rotatedContext: { size, context in
|
||||
context.setBlendMode(.clear)
|
||||
@ -68,19 +56,6 @@ private final class HSVParameter: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
private final class IntensitySliderParameter: NSObject {
|
||||
let bordered: Bool
|
||||
let min: HSVParameter
|
||||
let max: HSVParameter
|
||||
|
||||
init(bordered: Bool, min: HSVParameter, max: HSVParameter) {
|
||||
self.bordered = bordered
|
||||
self.min = min
|
||||
self.max = max
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
|
||||
private final class WallpaperColorKnobNode: ASDisplayNode {
|
||||
var hsv: (CGFloat, CGFloat, CGFloat) = (0.0, 0.0, 1.0) {
|
||||
didSet {
|
||||
@ -114,8 +89,7 @@ private final class WallpaperColorKnobNode: ASDisplayNode {
|
||||
context.fill(bounds)
|
||||
}
|
||||
|
||||
let image = bounds.width > 30.0 ? shadowImage : smallShadowImage
|
||||
context.draw(image.cgImage!, in: bounds)
|
||||
context.draw(shadowImage.cgImage!, in: bounds)
|
||||
|
||||
context.setBlendMode(.normal)
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
@ -214,176 +188,9 @@ private final class WallpaperColorBrightnessNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
final class WallpaperIntensitySliderNode: ASDisplayNode {
|
||||
private let bordered: Bool
|
||||
var extrema: ((CGFloat, CGFloat, CGFloat), (CGFloat, CGFloat, CGFloat)) = ((0.0, 1.0, 0.0), (0.0, 1.0, 1.0)) {
|
||||
didSet {
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
init(bordered: Bool) {
|
||||
self.bordered = bordered
|
||||
|
||||
super.init()
|
||||
|
||||
self.isOpaque = bordered
|
||||
self.displaysAsynchronously = false
|
||||
}
|
||||
|
||||
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
|
||||
return IntensitySliderParameter(bordered: self.bordered, min: HSVParameter(hue: self.extrema.0.0, saturation: self.extrema.0.1, value: self.extrema.0.2), max: HSVParameter(hue: self.extrema.1.0, saturation: self.extrema.1.1, value: self.extrema.1.2))
|
||||
}
|
||||
|
||||
@objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
|
||||
guard let parameters = parameters as? IntensitySliderParameter else {
|
||||
return
|
||||
}
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
|
||||
if parameters.bordered {
|
||||
context.setFillColor(UIColor(white: parameters.min.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()
|
||||
} else if !isRasterizing {
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fill(bounds)
|
||||
context.setBlendMode(.normal)
|
||||
}
|
||||
|
||||
let innerPath = UIBezierPath(roundedRect: bounds.insetBy(dx: 1.0, dy: 1.0), cornerRadius: bounds.height / 2.0)
|
||||
context.addPath(innerPath.cgPath)
|
||||
context.clip()
|
||||
|
||||
let minColor = UIColor(hue: parameters.min.hue, saturation: parameters.min.saturation, brightness: parameters.min.value, alpha: 1.0)
|
||||
let maxColor = UIColor(hue: parameters.max.hue, saturation: parameters.max.saturation, brightness: parameters.max.value, alpha: 1.0)
|
||||
let colors = [minColor.cgColor, maxColor.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())
|
||||
}
|
||||
}
|
||||
|
||||
final class WallpaperIntensityPickerNode: ASDisplayNode {
|
||||
private let labelNode: ASTextNode
|
||||
private let sliderNode: WallpaperIntensitySliderNode
|
||||
private let knobNode: WallpaperColorKnobNode
|
||||
|
||||
var valueChanged: ((CGFloat) -> Void)?
|
||||
var valueChangeEnded: ((CGFloat) -> Void)?
|
||||
|
||||
var value: CGFloat = 0.0 {
|
||||
didSet {
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
}
|
||||
|
||||
var intensity: Int32 {
|
||||
return Int32(self.value * 100.0)
|
||||
}
|
||||
|
||||
private var bordered: Bool
|
||||
|
||||
init(theme: PresentationTheme?, title: String, bordered: Bool) {
|
||||
self.bordered = bordered
|
||||
self.labelNode = ASTextNode()
|
||||
var color: UIColor = .black
|
||||
if let theme = theme {
|
||||
color = theme.rootController.navigationBar.primaryTextColor
|
||||
}
|
||||
self.labelNode.attributedText = NSAttributedString(string: title, font: Font.regular(14.0), textColor: color)
|
||||
self.sliderNode = WallpaperIntensitySliderNode(bordered: bordered)
|
||||
self.sliderNode.hitTestSlop = UIEdgeInsetsMake(-16.0, -16.0, -16.0, -16.0)
|
||||
self.knobNode = WallpaperColorKnobNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.labelNode)
|
||||
self.addSubnode(self.sliderNode)
|
||||
self.addSubnode(self.knobNode)
|
||||
}
|
||||
|
||||
func updateExtrema(min: (CGFloat, CGFloat, CGFloat), max: (CGFloat, CGFloat, CGFloat)) {
|
||||
self.sliderNode.extrema = (min, max)
|
||||
self.update()
|
||||
}
|
||||
|
||||
private func update() {
|
||||
let extrema = self.sliderNode.extrema
|
||||
let hsv = (extrema.0.0 + (extrema.1.0 - extrema.0.0) * self.value, extrema.0.1 + (extrema.1.1 - extrema.0.1) * self.value, extrema.0.2 + (extrema.1.2 - extrema.0.2) * self.value)
|
||||
self.knobNode.hsv = hsv
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(WallpaperIntensityPickerNode.pan))
|
||||
self.sliderNode.view.addGestureRecognizer(panRecognizer)
|
||||
}
|
||||
|
||||
@objc private func pan(_ recognizer: UIPanGestureRecognizer) {
|
||||
let size = self.bounds.size
|
||||
|
||||
let previousValue = self.value
|
||||
|
||||
let transition = recognizer.translation(in: recognizer.view)
|
||||
let width: CGFloat = size.width - 5.0 * 2.0
|
||||
let newValue = max(0.0, min(1.0, self.value + transition.x / width))
|
||||
self.value = newValue
|
||||
|
||||
var ended = false
|
||||
switch recognizer.state {
|
||||
case .changed:
|
||||
self.updateKnobLayout(size: size)
|
||||
recognizer.setTranslation(CGPoint(), in: recognizer.view)
|
||||
case .ended:
|
||||
self.updateKnobLayout(size: size)
|
||||
ended = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if self.value != previousValue || ended {
|
||||
if ended {
|
||||
self.valueChangeEnded?(self.value)
|
||||
} else {
|
||||
self.valueChanged?(self.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateKnobLayout(size: CGSize) {
|
||||
let knobSize = CGSize(width: 24.0, height: 24.0)
|
||||
|
||||
let inset: CGFloat = 5.0
|
||||
let knobFrame = CGRect(x: inset - knobSize.width / 2.0 + (size.width - inset * 2.0) * self.value, y: 17.0, width: knobSize.width, height: knobSize.height)
|
||||
self.knobNode.frame = knobFrame
|
||||
self.update()
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
|
||||
let labelSize = self.labelNode.measure(self.bounds.size)
|
||||
self.labelNode.frame = CGRect(origin: CGPoint(), size: labelSize)
|
||||
|
||||
self.sliderNode.frame = CGRect(x: 0.0, y: 27.0, width: self.bounds.width, height: 4.0)
|
||||
self.updateKnobLayout(size: self.bounds.size)
|
||||
}
|
||||
}
|
||||
|
||||
final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
private let brightnessNode: WallpaperColorBrightnessNode
|
||||
private let brightnessKnobNode: ASImageNode
|
||||
|
||||
private let intensityNode: WallpaperIntensityPickerNode
|
||||
|
||||
private let colorNode: WallpaperColorHueSaturationNode
|
||||
private let colorKnobNode: WallpaperColorKnobNode
|
||||
|
||||
@ -415,27 +222,6 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
var colorChanged: ((UIColor) -> Void)?
|
||||
var colorChangeEnded: ((UIColor) -> Void)?
|
||||
|
||||
var intensity: Int32 {
|
||||
get {
|
||||
return self.intensityNode.intensity
|
||||
}
|
||||
set {
|
||||
self.intensityNode.value = CGFloat(newValue) / 100.0
|
||||
}
|
||||
}
|
||||
var intensityChanged: ((Int32) -> Void)?
|
||||
var intensityChangeEnded: ((Int32) -> Void)?
|
||||
|
||||
var adjustingPattern: Bool = false {
|
||||
didSet {
|
||||
let value = self.adjustingPattern
|
||||
self.brightnessNode.isHidden = value
|
||||
self.brightnessKnobNode.isHidden = value
|
||||
self.intensityNode.isHidden = !value
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
}
|
||||
|
||||
init(strings: PresentationStrings) {
|
||||
self.brightnessNode = WallpaperColorBrightnessNode()
|
||||
self.brightnessNode.hitTestSlop = UIEdgeInsetsMake(-16.0, -16.0, -16.0, -16.0)
|
||||
@ -445,10 +231,6 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
self.colorNode.hitTestSlop = UIEdgeInsetsMake(-16.0, -16.0, -16.0, -16.0)
|
||||
self.colorKnobNode = WallpaperColorKnobNode()
|
||||
|
||||
|
||||
self.intensityNode = WallpaperIntensityPickerNode(theme: nil, title: strings.WallpaperPreview_PatternIntensity, bordered: true)
|
||||
self.intensityNode.isHidden = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = .white
|
||||
@ -457,35 +239,6 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
self.addSubnode(self.brightnessKnobNode)
|
||||
self.addSubnode(self.colorNode)
|
||||
self.addSubnode(self.colorKnobNode)
|
||||
self.addSubnode(self.intensityNode)
|
||||
|
||||
let valueChanged: (CGFloat, Bool) -> Void = { [weak self] value, ended in
|
||||
if let strongSelf = self {
|
||||
let previousColor = strongSelf.color
|
||||
strongSelf.colorHSV.2 = 1.0 - value
|
||||
|
||||
if strongSelf.color != previousColor || ended {
|
||||
strongSelf.update()
|
||||
if ended {
|
||||
strongSelf.colorChangeEnded?(strongSelf.color)
|
||||
} else {
|
||||
strongSelf.colorChanged?(strongSelf.color)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.intensityNode.valueChanged = { [weak self] value in
|
||||
if let strongSelf = self {
|
||||
strongSelf.intensityChanged?(Int32(value * 100.0))
|
||||
}
|
||||
}
|
||||
|
||||
self.intensityNode.valueChangeEnded = { [weak self] value in
|
||||
if let strongSelf = self {
|
||||
strongSelf.intensityChangeEnded?(Int32(value * 100.0))
|
||||
}
|
||||
}
|
||||
|
||||
self.update()
|
||||
}
|
||||
@ -504,24 +257,16 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
private func update() {
|
||||
if self.adjustingPattern {
|
||||
self.backgroundColor = .white
|
||||
} else {
|
||||
self.backgroundColor = UIColor(white: self.colorHSV.2, alpha: 1.0)
|
||||
}
|
||||
self.backgroundColor = UIColor(white: self.colorHSV.2, alpha: 1.0)
|
||||
self.colorNode.value = self.colorHSV.2
|
||||
self.brightnessNode.hsv = self.colorHSV
|
||||
self.colorKnobNode.hsv = self.colorHSV
|
||||
|
||||
let min = self.colorHSV
|
||||
let max = patternColor(for: self.color, intensity: 1.0).hsv
|
||||
self.intensityNode.updateExtrema(min: min, max: max)
|
||||
}
|
||||
|
||||
func updateKnobLayout(size: CGSize, panningColor: Bool, transition: ContainedViewLayoutTransition) {
|
||||
let knobSize = CGSize(width: 45.0, height: 45.0)
|
||||
|
||||
let colorHeight = self.adjustingPattern ? size.height - 116.0 : size.height - 66.0
|
||||
let colorHeight = size.height - 66.0
|
||||
var colorKnobFrame = CGRect(x: -knobSize.width / 2.0 + size.width * self.colorHSV.0, y: -knobSize.height / 2.0 + (colorHeight * (1.0 - self.colorHSV.1)), width: knobSize.width, height: knobSize.height)
|
||||
var origin = colorKnobFrame.origin
|
||||
if !panningColor {
|
||||
@ -541,15 +286,12 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = size
|
||||
|
||||
let colorHeight = self.adjustingPattern ? size.height - 116.0 : size.height - 66.0
|
||||
let colorHeight = size.height - 66.0
|
||||
transition.updateFrame(node: self.colorNode, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: colorHeight))
|
||||
|
||||
let inset: CGFloat = 42.0
|
||||
transition.updateFrame(node: self.brightnessNode, frame: CGRect(x: inset, y: size.height - 55.0, width: size.width - inset * 2.0, height: 29.0))
|
||||
|
||||
let slidersInset: CGFloat = 24.0
|
||||
transition.updateFrame(node: self.intensityNode, frame: CGRect(x: slidersInset, y: size.height - 54.0, width: size.width - slidersInset * 2.0, height: 50.0))
|
||||
|
||||
self.updateKnobLayout(size: size, panningColor: false, transition: .immediate)
|
||||
}
|
||||
|
||||
@ -558,7 +300,7 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
|
||||
let colorHeight = self.adjustingPattern ? size.height - 116.0 : size.height - 66.0
|
||||
let colorHeight = size.height - 66.0
|
||||
|
||||
let location = recognizer.location(in: recognizer.view)
|
||||
let newHue = max(0.0, min(1.0, location.x / size.width))
|
||||
@ -579,7 +321,7 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
|
||||
let previousColor = self.color
|
||||
|
||||
let colorHeight = self.adjustingPattern ? size.height - 116.0 : size.height - 66.0
|
||||
let colorHeight = size.height - 66.0
|
||||
|
||||
let location = recognizer.location(in: recognizer.view)
|
||||
let transition = recognizer.translation(in: recognizer.view)
|
||||
|
||||
@ -91,7 +91,7 @@ private func updatedFileWallpaper(id: Int64? = nil, accessHash: Int64? = nil, sl
|
||||
intensityValue = 50
|
||||
}
|
||||
|
||||
return .file(id: id ?? 0, accessHash: accessHash ?? 0, isCreator: false, isDefault: false, isPattern: isPattern, slug: slug, file: file, settings: WallpaperSettings(blur: false, motion: false, color: colorValue, intensity: intensityValue))
|
||||
return .file(id: id ?? 0, accessHash: accessHash ?? 0, isCreator: false, isDefault: false, isPattern: isPattern, isDark: false, slug: slug, file: file, settings: WallpaperSettings(blur: false, motion: false, color: colorValue, intensity: intensityValue))
|
||||
}
|
||||
|
||||
private struct WallpaperPatternTransition: Equatable {
|
||||
@ -318,9 +318,9 @@ class WallpaperGalleryController: ViewController {
|
||||
self.galleryNode.addSubnode(overlayNode)
|
||||
|
||||
let colorPanelNode = WallpaperColorPanelNode(theme: presentationData.theme, strings: presentationData.strings)
|
||||
colorPanelNode.colorChanged = { [weak self] color, intensity, ended in
|
||||
colorPanelNode.colorChanged = { [weak self] color, ended in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateEntries(color: color, intensity: intensity, preview: !ended)
|
||||
strongSelf.updateEntries(color: color, preview: !ended)
|
||||
}
|
||||
}
|
||||
if case let .customColor(colorValue) = self.source, let color = colorValue {
|
||||
@ -344,6 +344,18 @@ class WallpaperGalleryController: ViewController {
|
||||
let entry = strongSelf.entries[centralItemNode.index]
|
||||
switch entry {
|
||||
case let .wallpaper(wallpaper):
|
||||
var resource: MediaResource?
|
||||
switch wallpaper {
|
||||
case let .file(file):
|
||||
resource = file.file.resource
|
||||
case let .image(representations, _):
|
||||
if let largestSize = largestImageRepresentation(representations) {
|
||||
resource = largestSize.resource
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
let completion: () -> Void = {
|
||||
let _ = (updatePresentationThemeSettingsInteractively(postbox: strongSelf.account.postbox, { current in
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
@ -360,18 +372,6 @@ class WallpaperGalleryController: ViewController {
|
||||
}
|
||||
|
||||
if options.contains(.blur) {
|
||||
var resource: MediaResource?
|
||||
switch wallpaper {
|
||||
case let .file(file):
|
||||
resource = file.file.resource
|
||||
case let .image(representations, _):
|
||||
if let largestSize = largestImageRepresentation(representations) {
|
||||
resource = largestSize.resource
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if let resource = resource {
|
||||
let _ = strongSelf.account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: {
|
||||
completion()
|
||||
@ -439,17 +439,6 @@ class WallpaperGalleryController: ViewController {
|
||||
strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
}
|
||||
node.requestColorPanel = { [weak self] color in
|
||||
if let strongSelf = self, let (layout, _) = strongSelf.validLayout {
|
||||
strongSelf.colorPanelEnabled = true
|
||||
strongSelf.colorPanelNode?.adjustingPattern = true
|
||||
strongSelf.colorPanelNode?.intensity = 40
|
||||
strongSelf.colorPanelNode?.color = color
|
||||
strongSelf.galleryNode.scrollView.isScrollEnabled = false
|
||||
strongSelf.galleryNode.pager.isScrollEnabled = false
|
||||
strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
if let (layout, bottomInset) = self.validLayout {
|
||||
self.updateMessagesLayout(layout: layout, bottomInset: bottomInset, transition: .immediate)
|
||||
@ -457,7 +446,7 @@ class WallpaperGalleryController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
private func updateEntries(color: UIColor, intensity: Int32? = nil, preview: Bool = false) {
|
||||
private func updateEntries(color: UIColor, preview: Bool = false) {
|
||||
guard self.validLayout != nil, let centralEntryIndex = self.galleryNode.pager.centralItemNode()?.index else {
|
||||
return
|
||||
}
|
||||
@ -469,12 +458,6 @@ class WallpaperGalleryController: ViewController {
|
||||
switch wallpaper {
|
||||
case .color:
|
||||
currentEntry = .wallpaper(.color(Int32(color.rgb)))
|
||||
case let .file(file):
|
||||
if file.isPattern {
|
||||
let newSettings = WallpaperSettings(blur: file.settings.blur, motion: file.settings.motion, color: Int32(bitPattern: color.rgb), intensity: intensity)
|
||||
let newWallpaper = TelegramWallpaper.file(id: file.id, accessHash: file.accessHash, isCreator: file.isCreator, isDefault: file.isDefault, isPattern: file.isPattern, slug: file.slug, file: file.file, settings: newSettings)
|
||||
currentEntry = .wallpaper(newWallpaper)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -502,7 +485,7 @@ class WallpaperGalleryController: ViewController {
|
||||
if let entryColor = entryColor {
|
||||
if case let .file(file) = pattern {
|
||||
let newSettings = WallpaperSettings(blur: file.settings.blur, motion: file.settings.motion, color: entryColor, intensity: intensity)
|
||||
let newWallpaper = TelegramWallpaper.file(id: file.id, accessHash: file.accessHash, isCreator: file.isCreator, isDefault: file.isDefault, isPattern: file.isPattern, slug: file.slug, file: file.file, settings: newSettings)
|
||||
let newWallpaper = TelegramWallpaper.file(id: file.id, accessHash: file.accessHash, isCreator: file.isCreator, isDefault: file.isDefault, isPattern: file.isPattern, isDark: file.isDark, slug: file.slug, file: file.file, settings: newSettings)
|
||||
updatedEntries.append(.wallpaper(newWallpaper))
|
||||
} else {
|
||||
let newWallpaper = TelegramWallpaper.color(entryColor)
|
||||
@ -690,7 +673,7 @@ class WallpaperGalleryController: ViewController {
|
||||
|
||||
var controller: ShareController?
|
||||
switch wallpaper {
|
||||
case let .file(_, _, _, _, isPattern, slug, _, settings):
|
||||
case let .file(_, _, _, _, isPattern, _, slug, _, settings):
|
||||
var options: [String] = []
|
||||
if (itemNode.options.contains(.blur) && !isPattern) {
|
||||
if (itemNode.options.contains(.motion)) {
|
||||
|
||||
@ -6,34 +6,6 @@ import Postbox
|
||||
import TelegramCore
|
||||
import LegacyComponents
|
||||
|
||||
private class WallpaperMotionEffect: UIInterpolatingMotionEffect {
|
||||
var previousValue: CGFloat?
|
||||
|
||||
override func keyPathsAndRelativeValues(forViewerOffset viewerOffset: UIOffset) -> [String : Any]? {
|
||||
var motionAmplitude: CGFloat = 0.0
|
||||
switch self.type {
|
||||
case .tiltAlongHorizontalAxis:
|
||||
motionAmplitude = viewerOffset.horizontal
|
||||
case .tiltAlongVerticalAxis:
|
||||
motionAmplitude = viewerOffset.vertical
|
||||
}
|
||||
|
||||
if (motionAmplitude > 0) {
|
||||
guard let max = (self.maximumRelativeValue as? CGFloat) else {
|
||||
return nil
|
||||
}
|
||||
let value = max * motionAmplitude
|
||||
return [self.keyPath: value]
|
||||
} else {
|
||||
guard let min = (self.minimumRelativeValue as? CGFloat) else {
|
||||
return nil
|
||||
}
|
||||
let value = -(min) * motionAmplitude
|
||||
return [self.keyPath: value]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct WallpaperGalleryItemArguments {
|
||||
let colorPreview: Bool
|
||||
let isColorsList: Bool
|
||||
@ -87,14 +59,12 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
let wrapperNode: ASDisplayNode
|
||||
let imageNode: TransformImageNode
|
||||
private let statusNode: RadialStatusNode
|
||||
private let progressNode: ASTextNode
|
||||
private let blurredNode: BlurredImageNode
|
||||
let cropNode: WallpaperCropNode
|
||||
|
||||
private var blurButtonNode: WallpaperOptionButtonNode
|
||||
private var motionButtonNode: WallpaperOptionButtonNode
|
||||
private var patternButtonNode: WallpaperOptionButtonNode
|
||||
private var colorButtonNode: WallpaperOptionButtonNode
|
||||
|
||||
fileprivate let _ready = Promise<Void>()
|
||||
private let fetchDisposable = MetaDisposable()
|
||||
@ -106,7 +76,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
let actionButton = Promise<UIBarButtonItem?>(nil)
|
||||
var action: (() -> Void)?
|
||||
var requestPatternPanel: ((Bool) -> Void)?
|
||||
var requestColorPanel: ((UIColor) -> Void)?
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
private var validOffset: CGFloat?
|
||||
@ -121,16 +90,13 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6))
|
||||
self.statusNode.frame = CGRect(x: 0.0, y: 0.0, width: progressDiameter, height: progressDiameter)
|
||||
self.statusNode.isUserInteractionEnabled = false
|
||||
self.progressNode = ASTextNode()
|
||||
|
||||
self.blurredNode = BlurredImageNode()
|
||||
|
||||
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
self.blurButtonNode = WallpaperOptionButtonNode(title: presentationData.strings.WallpaperPreview_Blurred, value: .check(false))
|
||||
self.motionButtonNode = WallpaperOptionButtonNode(title: presentationData.strings.WallpaperPreview_Motion, value: .check(false))
|
||||
|
||||
self.patternButtonNode = WallpaperOptionButtonNode(title: presentationData.strings.WallpaperPreview_Pattern, value: .check(false))
|
||||
self.colorButtonNode = WallpaperOptionButtonNode(title: presentationData.strings.WallpaperPreview_Color, value: .color(false, .white))
|
||||
|
||||
super.init()
|
||||
|
||||
@ -146,17 +112,14 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
|
||||
self.addSubnode(self.wrapperNode)
|
||||
self.addSubnode(self.statusNode)
|
||||
self.addSubnode(self.progressNode)
|
||||
|
||||
self.addSubnode(self.blurButtonNode)
|
||||
self.addSubnode(self.motionButtonNode)
|
||||
self.addSubnode(self.patternButtonNode)
|
||||
self.addSubnode(self.colorButtonNode)
|
||||
|
||||
self.blurButtonNode.addTarget(self, action: #selector(self.toggleBlur), forControlEvents: .touchUpInside)
|
||||
self.motionButtonNode.addTarget(self, action: #selector(self.toggleMotion), forControlEvents: .touchUpInside)
|
||||
self.patternButtonNode.addTarget(self, action: #selector(self.togglePattern), forControlEvents: .touchUpInside)
|
||||
self.colorButtonNode.addTarget(self, action: #selector(self.toggleColor), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -265,7 +228,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
patternColor = UIColor(rgb: UInt32(bitPattern: color), alpha: patternIntensity)
|
||||
}
|
||||
|
||||
self.colorButtonNode.color = patternColor.withAlphaComponent(1.0)
|
||||
self.backgroundColor = patternColor.withAlphaComponent(1.0)
|
||||
|
||||
if let previousEntry = previousEntry, case let .wallpaper(wallpaper) = previousEntry, case let .file(previousFile) = wallpaper, file.id == previousFile.id && (file.settings.color != previousFile.settings.color || file.settings.intensity != previousFile.settings.intensity) && self.colorPreview == self.arguments.colorPreview {
|
||||
@ -418,21 +380,17 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
case let .Fetching(_, progress):
|
||||
let adjustedProgress = max(progress, 0.027)
|
||||
state = .progress(color: statusForegroundColor, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: false)
|
||||
strongSelf.progressNode.attributedText = NSAttributedString(string: "\(Int(progress * 100))%", font: Font.medium(13), textColor: .white, paragraphAlignment: .center)
|
||||
case .Local:
|
||||
state = .none
|
||||
strongSelf.progressNode.attributedText = nil
|
||||
local = true
|
||||
case .Remote:
|
||||
state = .progress(color: statusForegroundColor, lineWidth: nil, value: 0.027, cancelEnabled: false)
|
||||
strongSelf.progressNode.attributedText = nil
|
||||
}
|
||||
strongSelf.statusNode.transitionToState(state, completion: {})
|
||||
|
||||
strongSelf.blurButtonNode.setEnabled(local)
|
||||
strongSelf.motionButtonNode.setEnabled(local)
|
||||
strongSelf.patternButtonNode.setEnabled(local)
|
||||
strongSelf.colorButtonNode.setEnabled(local)
|
||||
}
|
||||
}))
|
||||
|
||||
@ -444,7 +402,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
|> deliverOnMainQueue).start(next: { [weak self] color in
|
||||
self?.statusNode.backgroundNodeColor = color
|
||||
self?.patternButtonNode.buttonColor = color
|
||||
self?.colorButtonNode.buttonColor = color
|
||||
self?.blurButtonNode.buttonColor = color
|
||||
self?.motionButtonNode.buttonColor = color
|
||||
}))
|
||||
@ -568,21 +525,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
self.requestPatternPanel?(value)
|
||||
}
|
||||
|
||||
var isColorEnabled: Bool {
|
||||
return self.colorButtonNode.isSelected
|
||||
}
|
||||
|
||||
@objc func toggleColor() {
|
||||
let value = !self.colorButtonNode.isSelected
|
||||
self.colorButtonNode.setSelected(value, animated: true)
|
||||
|
||||
if let color = self.colorButtonNode.color {
|
||||
self.requestColorPanel?(color)
|
||||
|
||||
self.preparePatternEditing()
|
||||
}
|
||||
}
|
||||
|
||||
private func preparePatternEditing() {
|
||||
if let entry = self.entry, case let .wallpaper(wallpaper) = entry, case let .file(file) = wallpaper {
|
||||
if let size = file.file.dimensions?.fitted(CGSize(width: 1280.0, height: 1280.0)) {
|
||||
@ -593,11 +535,11 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
|
||||
func setMotionEnabled(_ enabled: Bool, animated: Bool) {
|
||||
if enabled {
|
||||
let horizontal = WallpaperMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
|
||||
let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
|
||||
horizontal.minimumRelativeValue = motionAmount
|
||||
horizontal.maximumRelativeValue = -motionAmount
|
||||
|
||||
let vertical = WallpaperMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis)
|
||||
let vertical = UIInterpolatingMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis)
|
||||
vertical.minimumRelativeValue = motionAmount
|
||||
vertical.maximumRelativeValue = -motionAmount
|
||||
|
||||
@ -640,11 +582,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
|
||||
func updateButtonsLayout(layout: ContainerViewLayout, offset: CGPoint, transition: ContainedViewLayoutTransition) {
|
||||
let patternButtonSize = self.patternButtonNode.measure(layout.size)
|
||||
let colorButtonSize = self.colorButtonNode.measure(layout.size)
|
||||
let blurButtonSize = self.blurButtonNode.measure(layout.size)
|
||||
let motionButtonSize = self.motionButtonNode.measure(layout.size)
|
||||
|
||||
let maxButtonWidth = max(patternButtonSize.width, max(colorButtonSize.width, max(blurButtonSize.width, motionButtonSize.width)))
|
||||
let maxButtonWidth = max(patternButtonSize.width, max(blurButtonSize.width, motionButtonSize.width))
|
||||
let buttonSize = CGSize(width: maxButtonWidth, height: 30.0)
|
||||
let alpha = 1.0 - min(1.0, max(0.0, abs(offset.y) / 50.0))
|
||||
|
||||
@ -654,17 +595,12 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
}
|
||||
|
||||
let leftButtonFrame = CGRect(origin: CGPoint(x: floor(layout.size.width / 2.0 - buttonSize.width - 10.0) + offset.x, y: layout.size.height - 49.0 - layout.intrinsicInsets.bottom - 54.0 + offset.y + additionalYOffset), size: buttonSize)
|
||||
|
||||
let centerButtonFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonSize.width) / 2.0) + offset.x, y: layout.size.height - 49.0 - layout.intrinsicInsets.bottom - 54.0 + offset.y + additionalYOffset), size: buttonSize)
|
||||
|
||||
let rightButtonFrame = CGRect(origin: CGPoint(x: ceil(layout.size.width / 2.0 + 10.0) + offset.x, y: layout.size.height - 49.0 - layout.intrinsicInsets.bottom - 54.0 + offset.y + additionalYOffset), size: buttonSize)
|
||||
|
||||
var patternAlpha: CGFloat = 0.0
|
||||
var patternFrame = centerButtonFrame
|
||||
|
||||
var colorAlpha: CGFloat = 0.0
|
||||
var colorFrame = centerButtonFrame
|
||||
|
||||
var blurAlpha: CGFloat = 0.0
|
||||
var blurFrame = centerButtonFrame
|
||||
|
||||
@ -673,7 +609,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
|
||||
if let entry = self.entry {
|
||||
switch entry {
|
||||
case let .asset(_):
|
||||
case .asset:
|
||||
blurAlpha = 1.0
|
||||
blurFrame = leftButtonFrame
|
||||
motionAlpha = 1.0
|
||||
@ -709,10 +645,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
}
|
||||
motionFrame = rightButtonFrame
|
||||
}
|
||||
// if !self.arguments.isColorsList || self.patternButtonNode.isSelected {
|
||||
// motionAlpha = 1.0
|
||||
// motionFrame = rightButtonFrame
|
||||
// }
|
||||
} else {
|
||||
blurAlpha = 1.0
|
||||
blurFrame = leftButtonFrame
|
||||
@ -726,9 +658,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
transition.updateFrame(node: self.patternButtonNode, frame: patternFrame)
|
||||
transition.updateAlpha(node: self.patternButtonNode, alpha: patternAlpha * alpha)
|
||||
|
||||
transition.updateFrame(node: self.colorButtonNode, frame: colorFrame)
|
||||
transition.updateAlpha(node: self.colorButtonNode, alpha: colorAlpha * alpha)
|
||||
|
||||
transition.updateFrame(node: self.blurButtonNode, frame: blurFrame)
|
||||
transition.updateAlpha(node: self.blurButtonNode, alpha: blurAlpha * alpha)
|
||||
|
||||
@ -768,8 +697,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
}
|
||||
|
||||
self.statusNode.frame = CGRect(x: layout.safeInsets.left + floorToScreenPixels((layout.size.width - layout.safeInsets.left - layout.safeInsets.right - progressDiameter) / 2.0), y: floorToScreenPixels((layout.size.height + additionalYOffset - progressDiameter) / 2.0), width: progressDiameter, height: progressDiameter)
|
||||
self.progressNode.frame = CGRect(x: layout.safeInsets.left + floorToScreenPixels((layout.size.width - layout.safeInsets.left - layout.safeInsets.right - progressDiameter) / 2.0), y: floorToScreenPixels((layout.size.height + additionalYOffset - 15.0) / 2.0), width: progressDiameter, height: progressDiameter)
|
||||
|
||||
|
||||
self.updateButtonsLayout(layout: layout, offset: CGPoint(x: offset, y: 0.0), transition: transition)
|
||||
|
||||
|
||||
@ -75,7 +75,7 @@ final class WallpaperPatternPanelNode: ASDisplayNode {
|
||||
var updatedWallpaper = wallpaper
|
||||
if case let .file(file) = updatedWallpaper {
|
||||
let settings = WallpaperSettings(blur: false, motion: false, color: 0xd6e2ee, intensity: 100)
|
||||
updatedWallpaper = .file(id: file.id, accessHash: file.accessHash, isCreator: file.isCreator, isDefault: file.isDefault, isPattern: file.isPattern, slug: file.slug, file: file.file, settings: settings)
|
||||
updatedWallpaper = .file(id: file.id, accessHash: file.accessHash, isCreator: file.isCreator, isDefault: file.isDefault, isPattern: file.isPattern, isDark: file.isDark, slug: file.slug, file: file.file, settings: settings)
|
||||
}
|
||||
|
||||
node.setWallpaper(account: account, wallpaper: updatedWallpaper, selected: selected, size: itemSize)
|
||||
@ -116,7 +116,6 @@ final class WallpaperPatternPanelNode: ASDisplayNode {
|
||||
self.scrollNode.view.alwaysBounceHorizontal = true
|
||||
|
||||
let sliderView = TGPhotoEditorSliderView()
|
||||
//sliderView.enablePanHandling = true
|
||||
sliderView.trackCornerRadius = 1.0
|
||||
sliderView.lineSize = 2.0
|
||||
sliderView.minimumValue = 0.0
|
||||
|
||||
@ -11,8 +11,29 @@ private func wallpaperDatas(account: Account, fileReference: FileMediaReference?
|
||||
let maybeFullSize: Signal<MediaResourceData, NoError>
|
||||
if thumbnail, let file = fileReference?.media {
|
||||
maybeFullSize = account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: false, fetch: false)
|
||||
|> mapToSignal { maybeData -> Signal<MediaResourceData, NoError> in
|
||||
if maybeData.complete {
|
||||
return .single(maybeData)
|
||||
} else {
|
||||
return account.postbox.mediaBox.resourceData(file.resource)
|
||||
|> mapToSignal { maybeData -> Signal<MediaResourceData, NoError> in
|
||||
if maybeData.complete {
|
||||
return account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: false, fetch: true)
|
||||
} else {
|
||||
return .single(maybeData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
maybeFullSize = account.postbox.mediaBox.resourceData(largestRepresentation.resource)
|
||||
maybeFullSize = account.postbox.mediaBox.cachedResourceRepresentation(largestRepresentation.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: false, fetch: false)
|
||||
|> mapToSignal { maybeData -> Signal<MediaResourceData, NoError> in
|
||||
if maybeData.complete {
|
||||
return .single(maybeData)
|
||||
} else {
|
||||
return account.postbox.mediaBox.resourceData(largestRepresentation.resource)
|
||||
}
|
||||
}
|
||||
}
|
||||
let decodedThumbnailData = fileReference?.media.immediateThumbnailData.flatMap(decodeTinyThumbnail)
|
||||
|
||||
@ -300,8 +321,14 @@ private func patternWallpaperDatas(account: Account, representations: [ImageRepr
|
||||
}
|
||||
|
||||
func patternWallpaperImage(account: Account, representations: [ImageRepresentationWithReference], mode: PatternWallpaperDrawMode, autoFetchFullSize: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
let signal = patternWallpaperDatas(account: account, representations: representations, mode: mode, autoFetchFullSize: autoFetchFullSize)
|
||||
|
||||
return patternWallpaperDatas(account: account, representations: representations, mode: mode, autoFetchFullSize: autoFetchFullSize)
|
||||
|> mapToSignal { (thumbnailData, fullSizeData, fullSizeComplete) in
|
||||
return patternWallpaperImageInternal(thumbnailData: thumbnailData, fullSizeData: fullSizeData, fullSizeComplete: fullSizeComplete, mode: mode)
|
||||
}
|
||||
}
|
||||
|
||||
func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Data?, fullSizeComplete: Bool, mode: PatternWallpaperDrawMode) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
var prominent = false
|
||||
if case .thumbnail = mode {
|
||||
prominent = true
|
||||
@ -309,10 +336,10 @@ func patternWallpaperImage(account: Account, representations: [ImageRepresentati
|
||||
|
||||
var scale: CGFloat = 0.0
|
||||
if case .fastScreen = mode {
|
||||
scale = 1.0 //max(1.0, UIScreenScale - 1.0)
|
||||
scale = 1.0
|
||||
}
|
||||
|
||||
return signal
|
||||
return .single((thumbnailData, fullSizeData, fullSizeComplete))
|
||||
|> map { (thumbnailData, fullSizeData, fullSizeComplete) in
|
||||
return { arguments in
|
||||
let drawingRect = arguments.drawingRect
|
||||
@ -382,7 +409,7 @@ func patternColor(for color: UIColor, intensity: CGFloat, prominent: Bool = fals
|
||||
var saturation: CGFloat = 0.0
|
||||
var brightness: CGFloat = 0.0
|
||||
if color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: nil) {
|
||||
if saturation > 0.0 || (brightness < 1.0 && brightness > 0.0) {
|
||||
if saturation > 0.0 {
|
||||
saturation = min(1.0, saturation + 0.05 + 0.1 * (1.0 - saturation))
|
||||
}
|
||||
if brightness > 0.5 {
|
||||
@ -452,3 +479,73 @@ func settingsBuiltinWallpaperImage(account: Account) -> Signal<(TransformImageAr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func photoWallpaper(postbox: Postbox, photoLibraryResource: PhotoLibraryMediaResource) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
let thumbnail = fetchPhotoLibraryImage(localIdentifier: photoLibraryResource.localIdentifier, thumbnail: true)
|
||||
let fullSize = fetchPhotoLibraryImage(localIdentifier: photoLibraryResource.localIdentifier, thumbnail: false)
|
||||
|
||||
return (thumbnail |> then(fullSize))
|
||||
|> map { result in
|
||||
var sourceImage = result?.0
|
||||
let isThumbnail = result?.1 ?? false
|
||||
|
||||
return { arguments in
|
||||
let context = DrawingContext(size: arguments.drawingSize, scale: 1.0, clear: true)
|
||||
|
||||
let dimensions = sourceImage?.size
|
||||
|
||||
if let thumbnailImage = sourceImage?.cgImage, isThumbnail {
|
||||
var fittedSize = arguments.imageSize
|
||||
if abs(fittedSize.width - arguments.boundingSize.width).isLessThanOrEqualTo(CGFloat(1.0)) {
|
||||
fittedSize.width = arguments.boundingSize.width
|
||||
}
|
||||
if abs(fittedSize.height - arguments.boundingSize.height).isLessThanOrEqualTo(CGFloat(1.0)) {
|
||||
fittedSize.height = arguments.boundingSize.height
|
||||
}
|
||||
|
||||
let thumbnailSize = CGSize(width: thumbnailImage.width, height: thumbnailImage.height)
|
||||
|
||||
let initialThumbnailContextFittingSize = fittedSize.fitted(CGSize(width: 100.0, height: 100.0))
|
||||
|
||||
let thumbnailContextSize = thumbnailSize.aspectFitted(initialThumbnailContextFittingSize)
|
||||
let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0)
|
||||
thumbnailContext.withFlippedContext { c in
|
||||
c.interpolationQuality = .none
|
||||
c.draw(thumbnailImage, in: CGRect(origin: CGPoint(), size: thumbnailContextSize))
|
||||
}
|
||||
telegramFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes)
|
||||
|
||||
var thumbnailContextFittingSize = CGSize(width: floor(arguments.drawingSize.width * 0.5), height: floor(arguments.drawingSize.width * 0.5))
|
||||
if thumbnailContextFittingSize.width < 150.0 || thumbnailContextFittingSize.height < 150.0 {
|
||||
thumbnailContextFittingSize = thumbnailContextFittingSize.aspectFilled(CGSize(width: 150.0, height: 150.0))
|
||||
}
|
||||
|
||||
if thumbnailContextFittingSize.width > thumbnailContextSize.width {
|
||||
let additionalContextSize = thumbnailContextFittingSize
|
||||
let additionalBlurContext = DrawingContext(size: additionalContextSize, scale: 1.0)
|
||||
additionalBlurContext.withFlippedContext { c in
|
||||
c.interpolationQuality = .default
|
||||
if let image = thumbnailContext.generateImage()?.cgImage {
|
||||
c.draw(image, in: CGRect(origin: CGPoint(), size: additionalContextSize))
|
||||
}
|
||||
}
|
||||
telegramFastBlur(Int32(additionalContextSize.width), Int32(additionalContextSize.height), Int32(additionalBlurContext.bytesPerRow), additionalBlurContext.bytes)
|
||||
sourceImage = additionalBlurContext.generateImage()
|
||||
} else {
|
||||
sourceImage = thumbnailContext.generateImage()
|
||||
}
|
||||
}
|
||||
|
||||
context.withFlippedContext { c in
|
||||
c.setBlendMode(.copy)
|
||||
if let sourceImage = sourceImage, let cgImage = sourceImage.cgImage, let dimensions = dimensions {
|
||||
let imageSize = dimensions.aspectFilled(arguments.drawingRect.size)
|
||||
let fittedRect = CGRect(origin: CGPoint(x: floor((arguments.drawingRect.size.width - imageSize.width) / 2.0), y: floor((arguments.drawingRect.size.height - imageSize.height) / 2.0)), size: imageSize)
|
||||
c.draw(cgImage, in: fittedRect)
|
||||
}
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user