Wallpaper fixes

This commit is contained in:
Ilya Laktyushin 2019-01-29 13:34:41 +03:00
parent 4e46f2f90e
commit c3b974f6e0
19 changed files with 2718 additions and 2978 deletions

View File

@ -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>";

View File

@ -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)

View File

@ -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())

View File

@ -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

View File

@ -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 })

View File

@ -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

View File

@ -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)

View File

@ -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 {

View File

@ -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()
}

View File

@ -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)
}
}
}

View File

@ -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)

View File

@ -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) {

View File

@ -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.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)

View File

@ -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)) {

View File

@ -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)

View File

@ -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

View File

@ -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 {
maybeFullSize = account.postbox.mediaBox.resourceData(largestRepresentation.resource)
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.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
}
}
}