[WIP] Stickers editor

This commit is contained in:
Ilya Laktyushin 2024-04-11 20:35:25 +04:00
parent 09a0192816
commit d9f010a806
9 changed files with 97 additions and 43 deletions

View File

@ -12035,11 +12035,11 @@ Sorry for the inconvenience.";
"MediaEditor.Undo" = "Undo";
"MediaEditor.Erase" = "Erase";
"MediaEditor.EraseInfo" = "Erase";
"MediaEditor.EraseInfo" = "Erase parts of this sticker";
"MediaEditor.Restore" = "Restore";
"MediaEditor.RestoreInfo" = "Restore";
"MediaEditor.RestoreInfo" = "Restore parts of this sticker";
"MediaEditor.Cutout" = "Cut Out an Object";
"MediaEditor.CutoutInfo" = "Cut Out an Object";
"MediaEditor.CutoutInfo" = "Tap on an object to cut it out";
"MediaEditor.Outline" = "Add Outline";
"MediaEditor.SetStickerEmoji" = "Set emoji that corresponds to your sticker";

View File

@ -1019,7 +1019,7 @@ public protocol SharedAccountContext: AnyObject {
func makeMediaPickerScreen(context: AccountContext, hasSearch: Bool, completion: @escaping (Any) -> Void) -> ViewController
func makeStickerEditorScreen(context: AccountContext, source: Any?, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile, [String], @escaping () -> Void) -> Void) -> ViewController
func makeStickerEditorScreen(context: AccountContext, source: Any?, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile, [String], @escaping () -> Void) -> Void, cancelled: @escaping () -> Void) -> ViewController
func makeStickerMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController
func makeStoryMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void, groupsPresented: @escaping () -> Void) -> ViewController

View File

@ -615,12 +615,16 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
previewView: previewView,
parentView: self.cameraWrapperView,
restore: { [weak self, weak previewView] in
guard let self else{
guard let self else {
return
}
self.modernCameraTapGestureRecognizer?.isEnabled = true
if let previewView {
self.cameraWrapperView.addSubview(previewView)
if let (layout, navigationBarHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
previewView.layer.removeAllAnimations()
}
}
}
)
@ -1562,7 +1566,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
cameraView.center = CGPoint(x: cameraRect.size.width / 2.0, y: cameraRect.size.height / 2.0)
cameraView.transform = CGAffineTransform(scaleX: cameraScale, y: cameraScale)
} else {
} else if cameraView.superview == self.gridNode.scrollView {
transition.updateFrame(view: cameraView, frame: cameraRect)
}
self.cameraActivateAreaNode.frame = cameraRect

View File

@ -41,6 +41,7 @@ swift_library(
"//submodules/StickerPeekUI:StickerPeekUI",
"//submodules/Pasteboard:Pasteboard",
"//submodules/TelegramUI/Components/Stickers/StickerPackEditTitleController",
"//submodules/TelegramUI/Components/CameraScreen",
],
visibility = [
"//visibility:public",

View File

@ -24,6 +24,7 @@ import MultiAnimationRenderer
import Pasteboard
import StickerPackEditTitleController
import EntityKeyboard
import CameraScreen
private let maxStickersCount = 120
@ -1281,14 +1282,21 @@ private final class StickerPackContainer: ASDisplayNode {
packController.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: presentationData.strings.StickerPack_StickerAdded(info.title).string, undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current)
}
})
}
},
cancelled: cancelled
)
navigationController?.pushViewController(editorController)
},
dismissed: {}
)
dismissImpl = { [weak mainController] in
mainController?.dismiss()
if let mainController, let navigationController = mainController.navigationController {
var viewControllers = navigationController.viewControllers
viewControllers = viewControllers.filter { c in
return !(c is CameraScreen) && c !== mainController
}
navigationController.setViewControllers(viewControllers, animated: false)
}
}
navigationController?.pushViewController(mainController)
}
@ -1378,7 +1386,8 @@ private final class StickerPackContainer: ASDisplayNode {
packController.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: presentationData.strings.StickerPack_StickerUpdated, undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current)
}
})
}
},
cancelled: {}
)
navigationController?.pushViewController(controller)
}
@ -2015,7 +2024,11 @@ private final class StickerPackContainer: ASDisplayNode {
let layout = ItemLayout(width: fillingWidth, itemsCount: items.count)
contentHeight = layout.height
} else {
let rowCount = items.count / itemsPerRow + ((items.count % itemsPerRow) == 0 ? 0 : 1)
var itemsCount = items.count
if info.flags.contains(.isCreator) && itemsCount < 120 {
itemsCount += 1
}
let rowCount = itemsCount / itemsPerRow + ((itemsCount % itemsPerRow) == 0 ? 0 : 1)
contentHeight = itemWidth * CGFloat(rowCount)
}
} else {

View File

@ -1417,6 +1417,7 @@ public class CameraScreen: ViewController {
private let mainPreviewContainerView: UIView
fileprivate var mainPreviewView: CameraSimplePreviewView
private let mainPreviewAnimationWrapperView: UIView
private let additionalPreviewContainerView: UIView
fileprivate var additionalPreviewView: CameraSimplePreviewView
@ -1550,6 +1551,10 @@ public class CameraScreen: ViewController {
self.mainPreviewContainerView.clipsToBounds = true
self.mainPreviewView = CameraSimplePreviewView(frame: .zero, main: true)
self.mainPreviewAnimationWrapperView = UIView()
self.mainPreviewAnimationWrapperView.clipsToBounds = true
self.mainPreviewAnimationWrapperView.isUserInteractionEnabled = false
self.additionalPreviewContainerView = UIView()
self.additionalPreviewContainerView.clipsToBounds = true
self.additionalPreviewView = CameraSimplePreviewView(frame: .zero, main: false)
@ -1604,6 +1609,7 @@ public class CameraScreen: ViewController {
self.view.addSubview(self.transitionCornersView)
self.mainPreviewContainerView.addSubview(self.mainPreviewView)
self.mainPreviewContainerView.addSubview(self.mainPreviewAnimationWrapperView)
self.additionalPreviewContainerView.addSubview(self.additionalPreviewView)
self.completion.connect { [weak self] result in
@ -1757,7 +1763,7 @@ public class CameraScreen: ViewController {
if let cameraHolder = controller.holder {
camera = cameraHolder.camera
self.mainPreviewView = cameraHolder.previewView
self.mainPreviewContainerView.addSubview(self.mainPreviewView)
self.mainPreviewAnimationWrapperView.addSubview(self.mainPreviewView)
} else {
camera = Camera(
configuration: Camera.Configuration(
@ -1961,7 +1967,7 @@ public class CameraScreen: ViewController {
let transitionFraction = translation.y / self.frame.height
if abs(transitionFraction) > 0.3 || abs(velocity.y) > 1000.0 {
self.containerView.layer.sublayerTransform = CATransform3DIdentity
self.mainPreviewView.center = self.previewContainerView.center.offsetBy(dx: 0.0, dy: translation.y)
self.mainPreviewAnimationWrapperView.center = self.previewContainerView.center.offsetBy(dx: 0.0, dy: translation.y)
if let view = self.componentHost.view {
view.center = view.center.offsetBy(dx: 0.0, dy: translation.y)
@ -2123,10 +2129,14 @@ public class CameraScreen: ViewController {
duration: 0.3
)
} else {
self.mainPreviewAnimationWrapperView.bounds = self.mainPreviewView.bounds
self.mainPreviewAnimationWrapperView.center = CGPoint(x: self.previewContainerView.frame.width / 2.0, y: self.previewContainerView.frame.height / 2.0)
self.mainPreviewView.layer.position = CGPoint(x: self.previewContainerView.frame.width / 2.0, y: self.previewContainerView.frame.height / 2.0)
let sourceInnerFrame = sourceView.convert(transitionIn.sourceRect, to: self.previewContainerView)
let sourceCenter = sourceInnerFrame.center
self.mainPreviewView.layer.position = CGPoint(x: self.previewContainerView.frame.width / 2.0, y: self.previewContainerView.frame.height / 2.0)
self.mainPreviewView.layer.animatePosition(from: sourceCenter, to: self.mainPreviewView.layer.position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.mainPreviewAnimationWrapperView.layer.animatePosition(from: sourceCenter, to: self.mainPreviewAnimationWrapperView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.requestUpdateLayout(hasAppeared: true, transition: .immediate)
})
@ -2134,12 +2144,12 @@ public class CameraScreen: ViewController {
if let holder = controller.holder {
sourceBounds = CGRect(origin: .zero, size: holder.parentView.frame.size.aspectFitted(sourceBounds.size))
}
self.mainPreviewAnimationWrapperView.layer.animateBounds(from: sourceBounds, to: self.mainPreviewView.bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
self.mainPreviewView.layer.animateBounds(from: sourceBounds, to: CGRect(origin: .zero, size: self.previewContainerView.frame.size), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
let sourceScale = self.mainPreviewView.layer.value(forKeyPath: "transform.scale.x") as? CGFloat ?? 1.0
let sourceScale = sourceInnerFrame.height / self.previewContainerView.frame.height
self.mainPreviewView.transform = CGAffineTransform.identity
self.mainPreviewView.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.mainPreviewAnimationWrapperView.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.mainPreviewContainerView.addSubview(self.mainPreviewView)
Queue.mainQueue().justDispatch {
self.animatedIn = true
}
@ -2184,18 +2194,26 @@ public class CameraScreen: ViewController {
removeOnCompletion: false
)
} else {
self.mainPreviewAnimationWrapperView.addSubview(self.mainPreviewView)
self.animatedIn = false
let destinationInnerFrame = destinationView.convert(transitionOut.destinationRect, to: self.previewContainerView)
let initialCenter = self.mainPreviewView.layer.position
self.mainPreviewView.layer.position = destinationInnerFrame.center
self.mainPreviewView.layer.animatePosition(from: initialCenter, to: self.mainPreviewView.layer.position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.mainPreviewAnimationWrapperView.center = destinationInnerFrame.center
self.mainPreviewAnimationWrapperView.layer.animatePosition(from: initialCenter, to: self.mainPreviewAnimationWrapperView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
completion()
})
self.mainPreviewView.layer.animateBounds(from: self.mainPreviewView.bounds, to: CGRect(origin: .zero, size: self.previewContainerView.frame.size), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
var targetBounds = self.mainPreviewView.bounds
if let holder = controller.holder {
targetBounds = CGRect(origin: .zero, size: holder.parentView.frame.size.aspectFitted(targetBounds.size))
}
self.mainPreviewView.center = self.mainPreviewView.center.offsetBy(dx: (targetBounds.width - self.mainPreviewView.bounds.width) / 2.0, dy: 0.0)
self.mainPreviewAnimationWrapperView.layer.animateBounds(from: self.mainPreviewView.bounds, to: targetBounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
let targetScale = destinationInnerFrame.width / self.previewContainerView.frame.width
self.mainPreviewView.transform = CGAffineTransform(scaleX: targetScale, y: targetScale)
self.mainPreviewView.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
let targetScale = destinationInnerFrame.height / self.previewContainerView.frame.height
self.mainPreviewAnimationWrapperView.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
}
if let view = self.componentHost.view {
@ -2684,7 +2702,11 @@ public class CameraScreen: ViewController {
let additionalPreviewInnerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((circleSide - additionalPreviewInnerSize.width) / 2.0), y: floorToScreenPixels((circleSide - additionalPreviewInnerSize.height) / 2.0)), size: additionalPreviewInnerSize)
if mainPreviewView.superview != self.mainPreviewContainerView {
self.mainPreviewContainerView.insertSubview(mainPreviewView, at: 0)
if case .sticker = controller.mode, !self.animatedIn {
} else {
self.mainPreviewContainerView.insertSubview(mainPreviewView, at: 0)
}
}
if additionalPreviewView.superview != self.additionalPreviewContainerView {
self.additionalPreviewContainerView.insertSubview(additionalPreviewView, at: 0)

View File

@ -365,11 +365,11 @@ private final class MediaCutoutScreenComponent: Component {
let helpText: String
switch controller.mode {
case .cutout:
helpText = "Tap on an object to cut it out"
helpText = environment.strings.MediaEditor_CutoutInfo
case .erase:
helpText = "Erase parts of this sticker"
helpText = environment.strings.MediaEditor_EraseInfo
case .restore:
helpText = "Restore parts of this sticker"
helpText = environment.strings.MediaEditor_RestoreInfo
}
let labelSize = self.label.update(

View File

@ -62,6 +62,7 @@ import MediaEditorScreen
import BusinessIntroSetupScreen
import TelegramNotices
import BotSettingsScreen
import CameraScreen
private final class AccountUserInterfaceInUseContext {
let subscribers = Bag<(Bool) -> Void>()
@ -2366,27 +2367,42 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, isEditing: isEditing, expandIfNeeded: expandIfNeeded, parentNavigationController: parentNavigationController, sendSticker: sendSticker)
}
public func makeStickerEditorScreen(context: AccountContext, source: Any?, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile, [String], @escaping () -> Void) -> Void) -> ViewController {
let subject: MediaEditorScreen.Subject
public func makeStickerEditorScreen(context: AccountContext, source: Any?, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile, [String], @escaping () -> Void) -> Void, cancelled: @escaping () -> Void) -> ViewController {
let subject: Signal<MediaEditorScreen.Subject?, NoError>
let mode: MediaEditorScreen.Mode.StickerEditorMode
var fromCamera = false
if let (file, emoji) = source as? (TelegramMediaFile, [String]) {
subject = .sticker(file, emoji)
subject = .single(.sticker(file, emoji))
mode = .editing
} else if let asset = source as? PHAsset {
subject = .asset(asset)
subject = .single(.asset(asset))
mode = .addingToPack
} else if let image = source as? UIImage {
subject = .image(image, PixelDimensions(image.size), nil, .bottomRight)
subject = .single(.image(image, PixelDimensions(image.size), nil, .bottomRight))
mode = .addingToPack
} else if let source = source as? Signal<CameraScreen.Result, NoError> {
subject = source
|> map { value -> MediaEditorScreen.Subject? in
switch value {
case .pendingImage:
return nil
case let .image(image):
return .image(image.image, PixelDimensions(image.image.size), nil, .topLeft)
default:
return nil
}
}
fromCamera = true
mode = .addingToPack
} else {
subject = .empty(PixelDimensions(width: 1080, height: 1920))
subject = .single(.empty(PixelDimensions(width: 1080, height: 1920)))
mode = .addingToPack
}
let controller = MediaEditorScreen(
let editorController = MediaEditorScreen(
context: context,
mode: .stickerEditor(mode: mode),
subject: .single(subject),
transitionIn: transitionArguments.flatMap { .gallery(
subject: subject,
transitionIn: fromCamera ? .camera : transitionArguments.flatMap { .gallery(
MediaEditorScreen.TransitionIn.GalleryTransitionIn(
sourceView: $0.0,
sourceRect: $0.1,
@ -2410,7 +2426,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}
} as (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void
)
return controller
editorController.cancelled = { _ in
cancelled()
}
return editorController
}
public func makeMediaPickerScreen(context: AccountContext, hasSearch: Bool, completion: @escaping (Any) -> Void) -> ViewController {

View File

@ -226,11 +226,6 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
self.accountSettingsController = accountSettingsController
self.rootTabController = tabBarController
self.pushViewController(tabBarController, animated: false)
// Queue.mainQueue().after(0.3) {
// let controller = self.context.sharedContext.makeStickerEditorScreen(context: self.context, source: nil, transitionArguments: nil, completion: { _ , _ in })
// self.chatListController?.push(controller)
// }
}
public func updateRootControllers(showCallsTab: Bool) {