mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Merge commit 'd4c13120f8d9df314e0447065ea88b897f25143d'
This commit is contained in:
commit
4ec8f3449e
@ -1028,306 +1028,6 @@ final class MediaEditorScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
let mediaEditor = controller.node.mediaEditor
|
||||
var isOutlineActive = false
|
||||
if let value = mediaEditor?.values.toolValues[.stickerOutline] as? Float, value > 0.0 {
|
||||
isOutlineActive = true
|
||||
}
|
||||
|
||||
if case .stickerEditor = controller.mode {
|
||||
var stickerButtonsHidden = buttonsAreHidden
|
||||
if let displayingTool = component.isDisplayingTool, [.cutoutErase, .cutoutRestore].contains(displayingTool) {
|
||||
stickerButtonsHidden = false
|
||||
}
|
||||
let stickerButtonsAlpha = stickerButtonsHidden ? 0.0 : bottomButtonsAlpha
|
||||
|
||||
let stickerFrameWidth = floorToScreenPixels(previewSize.width * 0.97)
|
||||
let stickerFrameRect = CGRect(origin: CGPoint(x: previewFrame.minX + floorToScreenPixels((previewSize.width - stickerFrameWidth) / 2.0), y: previewFrame.minY + floorToScreenPixels((previewSize.height - stickerFrameWidth) / 2.0)), size: CGSize(width: stickerFrameWidth, height: stickerFrameWidth))
|
||||
|
||||
var hasCutoutButton = false
|
||||
var hasUndoButton = false
|
||||
var hasEraseButton = false
|
||||
var hasRestoreButton = false
|
||||
var hasOutlineButton = false
|
||||
|
||||
if let canCutout = controller.node.canCutout {
|
||||
if controller.node.isCutout || controller.node.stickerMaskDrawingView?.internalState.canUndo == true {
|
||||
hasUndoButton = true
|
||||
}
|
||||
if canCutout && !controller.node.isCutout {
|
||||
hasCutoutButton = true
|
||||
} else {
|
||||
hasEraseButton = true
|
||||
if hasUndoButton {
|
||||
hasRestoreButton = true
|
||||
}
|
||||
}
|
||||
if hasUndoButton || controller.node.hasTransparency {
|
||||
hasOutlineButton = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasUndoButton {
|
||||
let undoButtonSize = self.undoButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(CutoutButtonContentComponent(
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||
icon: state.image(.undo),
|
||||
title: "Undo"
|
||||
)),
|
||||
effectAlignment: .center,
|
||||
action: {
|
||||
cutoutUndo()
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: 44.0)
|
||||
)
|
||||
let undoButtonFrame = CGRect(
|
||||
origin: CGPoint(x: floorToScreenPixels((availableSize.width - undoButtonSize.width) / 2.0), y: stickerFrameRect.minY - 35.0 - undoButtonSize.height),
|
||||
size: undoButtonSize
|
||||
)
|
||||
if let undoButtonView = self.undoButton.view {
|
||||
var positionTransition = transition
|
||||
if undoButtonView.superview == nil {
|
||||
self.addSubview(undoButtonView)
|
||||
|
||||
undoButtonView.alpha = stickerButtonsAlpha
|
||||
undoButtonView.layer.animateAlpha(from: 0.0, to: stickerButtonsAlpha, duration: 0.2, delay: 0.0)
|
||||
undoButtonView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: 0.0)
|
||||
positionTransition = .immediate
|
||||
}
|
||||
positionTransition.setPosition(view: undoButtonView, position: undoButtonFrame.center)
|
||||
undoButtonView.bounds = CGRect(origin: .zero, size: undoButtonFrame.size)
|
||||
transition.setAlpha(view: undoButtonView, alpha: stickerButtonsAlpha)
|
||||
}
|
||||
} else {
|
||||
if let undoButtonView = self.undoButton.view, undoButtonView.superview != nil {
|
||||
undoButtonView.alpha = 0.0
|
||||
undoButtonView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.0, completion: { _ in
|
||||
undoButtonView.removeFromSuperview()
|
||||
})
|
||||
undoButtonView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, delay: 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
if hasCutoutButton {
|
||||
let cutoutButtonSize = self.cutoutButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(CutoutButtonContentComponent(
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||
icon: state.image(.cutout),
|
||||
title: "Cut Out an Object"
|
||||
)),
|
||||
effectAlignment: .center,
|
||||
action: {
|
||||
openDrawing(.cutout)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: 44.0)
|
||||
)
|
||||
let cutoutButtonFrame = CGRect(
|
||||
origin: CGPoint(x: floorToScreenPixels((availableSize.width - cutoutButtonSize.width) / 2.0), y: stickerFrameRect.maxY + 35.0),
|
||||
size: cutoutButtonSize
|
||||
)
|
||||
if let cutoutButtonView = self.cutoutButton.view {
|
||||
var positionTransition = transition
|
||||
if cutoutButtonView.superview == nil {
|
||||
self.addSubview(cutoutButtonView)
|
||||
|
||||
cutoutButtonView.layer.animatePosition(from: CGPoint(x: 0.0, y: 64.0), to: .zero, duration: 0.3, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
cutoutButtonView.layer.animateAlpha(from: 0.0, to: stickerButtonsAlpha, duration: 0.2, delay: 0.0)
|
||||
cutoutButtonView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: 0.0)
|
||||
positionTransition = .immediate
|
||||
}
|
||||
positionTransition.setPosition(view: cutoutButtonView, position: cutoutButtonFrame.center)
|
||||
cutoutButtonView.bounds = CGRect(origin: .zero, size: cutoutButtonFrame.size)
|
||||
transition.setAlpha(view: cutoutButtonView, alpha: stickerButtonsAlpha)
|
||||
}
|
||||
} else {
|
||||
if let cutoutButtonView = self.cutoutButton.view, cutoutButtonView.superview != nil {
|
||||
cutoutButtonView.alpha = 0.0
|
||||
if transition.animation.isImmediate {
|
||||
cutoutButtonView.removeFromSuperview()
|
||||
} else {
|
||||
cutoutButtonView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.0, completion: { _ in
|
||||
cutoutButtonView.removeFromSuperview()
|
||||
})
|
||||
cutoutButtonView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, delay: 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hasEraseButton {
|
||||
let buttonSpacing: CGFloat = hasRestoreButton ? 10.0 : 0.0
|
||||
var totalButtonsWidth = buttonSpacing
|
||||
|
||||
let eraseButtonSize = self.eraseButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(CutoutButtonContentComponent(
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||
icon: state.image(.erase),
|
||||
title: "Erase",
|
||||
minWidth: 160.0,
|
||||
selected: component.isDisplayingTool == .cutoutErase
|
||||
)),
|
||||
effectAlignment: .center,
|
||||
action: {
|
||||
openDrawing(.cutoutErase)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: 44.0)
|
||||
)
|
||||
totalButtonsWidth += eraseButtonSize.width
|
||||
|
||||
var buttonOriginX = floorToScreenPixels((availableSize.width - totalButtonsWidth) / 2.0)
|
||||
|
||||
if hasRestoreButton {
|
||||
let restoreButtonSize = self.restoreButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(CutoutButtonContentComponent(
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||
icon: state.image(.restore),
|
||||
title: "Restore",
|
||||
minWidth: 160.0,
|
||||
selected: component.isDisplayingTool == .cutoutRestore
|
||||
)),
|
||||
effectAlignment: .center,
|
||||
action: {
|
||||
openDrawing(.cutoutRestore)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: 44.0)
|
||||
)
|
||||
totalButtonsWidth += restoreButtonSize.width
|
||||
|
||||
buttonOriginX = floorToScreenPixels((availableSize.width - totalButtonsWidth) / 2.0)
|
||||
let restoreButtonFrame = CGRect(
|
||||
origin: CGPoint(x: buttonOriginX + eraseButtonSize.width + buttonSpacing, y: stickerFrameRect.maxY + 35.0),
|
||||
size: restoreButtonSize
|
||||
)
|
||||
if let restoreButtonView = self.restoreButton.view {
|
||||
var positionTransition = transition
|
||||
if restoreButtonView.superview == nil {
|
||||
self.addSubview(restoreButtonView)
|
||||
|
||||
restoreButtonView.alpha = stickerButtonsAlpha
|
||||
restoreButtonView.layer.animateAlpha(from: 0.0, to: stickerButtonsAlpha, duration: 0.2, delay: 0.0)
|
||||
restoreButtonView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: 0.0)
|
||||
restoreButtonView.layer.animatePosition(from: CGPoint(x: 0.0, y: 64.0), to: .zero, duration: 0.3, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
positionTransition = .immediate
|
||||
}
|
||||
positionTransition.setPosition(view: restoreButtonView, position: restoreButtonFrame.center)
|
||||
restoreButtonView.bounds = CGRect(origin: .zero, size: restoreButtonFrame.size)
|
||||
transition.setAlpha(view: restoreButtonView, alpha: stickerButtonsAlpha)
|
||||
}
|
||||
}
|
||||
|
||||
let eraseButtonFrame = CGRect(
|
||||
origin: CGPoint(x: buttonOriginX, y: stickerFrameRect.maxY + 35.0),
|
||||
size: eraseButtonSize
|
||||
)
|
||||
if let eraseButtonView = self.eraseButton.view {
|
||||
var positionTransition = transition
|
||||
if eraseButtonView.superview == nil {
|
||||
self.addSubview(eraseButtonView)
|
||||
|
||||
eraseButtonView.alpha = stickerButtonsAlpha
|
||||
eraseButtonView.layer.animatePosition(from: CGPoint(x: 0.0, y: 64.0), to: .zero, duration: 0.3, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
eraseButtonView.layer.animateAlpha(from: 0.0, to: stickerButtonsAlpha, duration: 0.2, delay: 0.0)
|
||||
eraseButtonView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: 0.0)
|
||||
positionTransition = .immediate
|
||||
}
|
||||
positionTransition.setPosition(view: eraseButtonView, position: eraseButtonFrame.center)
|
||||
eraseButtonView.bounds = CGRect(origin: .zero, size: eraseButtonFrame.size)
|
||||
transition.setAlpha(view: eraseButtonView, alpha: stickerButtonsAlpha)
|
||||
}
|
||||
} else {
|
||||
if let eraseButtonView = self.eraseButton.view, eraseButtonView.superview != nil {
|
||||
eraseButtonView.alpha = 0.0
|
||||
eraseButtonView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.0, completion: { _ in
|
||||
eraseButtonView.removeFromSuperview()
|
||||
})
|
||||
eraseButtonView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, delay: 0.0)
|
||||
}
|
||||
}
|
||||
if !hasRestoreButton {
|
||||
if let restoreButtonView = self.restoreButton.view, restoreButtonView.superview != nil {
|
||||
restoreButtonView.alpha = 0.0
|
||||
restoreButtonView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.0, completion: { _ in
|
||||
restoreButtonView.removeFromSuperview()
|
||||
})
|
||||
restoreButtonView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, delay: 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
if hasOutlineButton {
|
||||
let outlineButtonSize = self.outlineButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(CutoutButtonContentComponent(
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||
icon: state.image(.outline),
|
||||
title: "Add Outline",
|
||||
minWidth: 160.0,
|
||||
selected: isOutlineActive
|
||||
)),
|
||||
effectAlignment: .center,
|
||||
action: { [weak self, weak controller] in
|
||||
guard let self, let mediaEditor = controller?.node.mediaEditor else {
|
||||
return
|
||||
}
|
||||
if let value = mediaEditor.values.toolValues[.stickerOutline] as? Float, value > 0.0 {
|
||||
mediaEditor.setToolValue(.stickerOutline, value: Float(0.0))
|
||||
} else {
|
||||
mediaEditor.setToolValue(.stickerOutline, value: Float(0.5))
|
||||
}
|
||||
self.state?.updated(transition: .easeInOut(duration: 0.25))
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: 44.0)
|
||||
)
|
||||
|
||||
let outlineButtonFrame = CGRect(
|
||||
origin: CGPoint(x: floorToScreenPixels((availableSize.width - outlineButtonSize.width) / 2.0), y: stickerFrameRect.maxY + 35.0 + 40.0 + 16.0),
|
||||
size: outlineButtonSize
|
||||
)
|
||||
if let outlineButtonView = self.outlineButton.view {
|
||||
let outlineButtonAlpha = buttonsAreHidden ? 0.0 : bottomButtonsAlpha
|
||||
var positionTransition = transition
|
||||
if outlineButtonView.superview == nil {
|
||||
self.addSubview(outlineButtonView)
|
||||
|
||||
outlineButtonView.alpha = outlineButtonAlpha
|
||||
outlineButtonView.layer.animateAlpha(from: 0.0, to: outlineButtonAlpha, duration: 0.2, delay: 0.0)
|
||||
outlineButtonView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: 0.0)
|
||||
outlineButtonView.layer.animatePosition(from: CGPoint(x: 0.0, y: 64.0), to: .zero, duration: 0.3, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
|
||||
positionTransition = .immediate
|
||||
}
|
||||
positionTransition.setPosition(view: outlineButtonView, position: outlineButtonFrame.center)
|
||||
outlineButtonView.bounds = CGRect(origin: .zero, size: outlineButtonFrame.size)
|
||||
transition.setAlpha(view: outlineButtonView, alpha: outlineButtonAlpha)
|
||||
}
|
||||
} else {
|
||||
if let outlineButtonView = self.outlineButton.view, outlineButtonView.superview != nil {
|
||||
outlineButtonView.alpha = 0.0
|
||||
outlineButtonView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.0, completion: { _ in
|
||||
outlineButtonView.removeFromSuperview()
|
||||
})
|
||||
outlineButtonView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, delay: 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var timeoutValue: String
|
||||
switch component.privacy.timeout {
|
||||
case 21600:
|
||||
@ -1460,6 +1160,12 @@ final class MediaEditorScreenComponent: Component {
|
||||
)
|
||||
}
|
||||
|
||||
let mediaEditor = controller.node.mediaEditor
|
||||
var isOutlineActive = false
|
||||
if let value = mediaEditor?.values.toolValues[.stickerOutline] as? Float, value > 0.0 {
|
||||
isOutlineActive = true
|
||||
}
|
||||
|
||||
var isEditingTextEntity = false
|
||||
var sizeSliderVisible = false
|
||||
var sizeValue: CGFloat?
|
||||
@ -1479,6 +1185,8 @@ final class MediaEditorScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
let displayTopButtons = !(self.inputPanelExternalState.isEditing || isEditingTextEntity || component.isDisplayingTool != nil)
|
||||
|
||||
if case .storyEditor = controller.mode {
|
||||
let nextInputMode: MessageInputPanelComponent.InputMode
|
||||
switch self.currentInputMode {
|
||||
@ -1895,9 +1603,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let displayTopButtons = !(self.inputPanelExternalState.isEditing || isEditingTextEntity || component.isDisplayingTool != nil)
|
||||
|
||||
|
||||
let saveContentComponent: AnyComponentWithIdentity<Empty>
|
||||
if component.hasAppeared {
|
||||
saveContentComponent = AnyComponentWithIdentity(
|
||||
@ -2256,6 +1962,300 @@ final class MediaEditorScreenComponent: Component {
|
||||
}
|
||||
|
||||
}
|
||||
if case .stickerEditor = controller.mode {
|
||||
var stickerButtonsHidden = buttonsAreHidden
|
||||
if let displayingTool = component.isDisplayingTool, [.cutoutErase, .cutoutRestore].contains(displayingTool) {
|
||||
stickerButtonsHidden = false
|
||||
}
|
||||
let stickerButtonsAlpha = stickerButtonsHidden ? 0.0 : bottomButtonsAlpha
|
||||
|
||||
let stickerFrameWidth = floorToScreenPixels(previewSize.width * 0.97)
|
||||
let stickerFrameRect = CGRect(origin: CGPoint(x: previewFrame.minX + floorToScreenPixels((previewSize.width - stickerFrameWidth) / 2.0), y: previewFrame.minY + floorToScreenPixels((previewSize.height - stickerFrameWidth) / 2.0)), size: CGSize(width: stickerFrameWidth, height: stickerFrameWidth))
|
||||
|
||||
var hasCutoutButton = false
|
||||
var hasUndoButton = false
|
||||
var hasEraseButton = false
|
||||
var hasRestoreButton = false
|
||||
var hasOutlineButton = false
|
||||
|
||||
if let canCutout = controller.node.canCutout {
|
||||
if controller.node.isCutout || controller.node.stickerMaskDrawingView?.internalState.canUndo == true {
|
||||
hasUndoButton = true
|
||||
}
|
||||
if canCutout && !controller.node.isCutout {
|
||||
hasCutoutButton = true
|
||||
} else {
|
||||
hasEraseButton = true
|
||||
if hasUndoButton {
|
||||
hasRestoreButton = true
|
||||
}
|
||||
}
|
||||
if hasUndoButton || controller.node.hasTransparency {
|
||||
hasOutlineButton = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasUndoButton {
|
||||
let undoButtonSize = self.undoButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(CutoutButtonContentComponent(
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||
icon: state.image(.undo),
|
||||
title: "Undo"
|
||||
)),
|
||||
effectAlignment: .center,
|
||||
action: {
|
||||
cutoutUndo()
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: 44.0)
|
||||
)
|
||||
let undoButtonFrame = CGRect(
|
||||
origin: CGPoint(x: floorToScreenPixels((availableSize.width - undoButtonSize.width) / 2.0), y: stickerFrameRect.minY - 35.0 - undoButtonSize.height),
|
||||
size: undoButtonSize
|
||||
)
|
||||
if let undoButtonView = self.undoButton.view {
|
||||
var positionTransition = transition
|
||||
if undoButtonView.superview == nil {
|
||||
self.addSubview(undoButtonView)
|
||||
|
||||
undoButtonView.alpha = stickerButtonsAlpha
|
||||
undoButtonView.layer.animateAlpha(from: 0.0, to: stickerButtonsAlpha, duration: 0.2, delay: 0.0)
|
||||
undoButtonView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: 0.0)
|
||||
positionTransition = .immediate
|
||||
}
|
||||
positionTransition.setPosition(view: undoButtonView, position: undoButtonFrame.center)
|
||||
undoButtonView.bounds = CGRect(origin: .zero, size: undoButtonFrame.size)
|
||||
transition.setAlpha(view: undoButtonView, alpha: !isEditingTextEntity && !component.isDismissing ? stickerButtonsAlpha : 0.0)
|
||||
transition.setScale(view: undoButtonView, scale: !isEditingTextEntity ? 1.0 : 0.01)
|
||||
}
|
||||
} else {
|
||||
if let undoButtonView = self.undoButton.view, undoButtonView.superview != nil {
|
||||
undoButtonView.alpha = 0.0
|
||||
undoButtonView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.0, completion: { _ in
|
||||
undoButtonView.removeFromSuperview()
|
||||
})
|
||||
undoButtonView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, delay: 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
if hasCutoutButton {
|
||||
let cutoutButtonSize = self.cutoutButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(CutoutButtonContentComponent(
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||
icon: state.image(.cutout),
|
||||
title: "Cut Out an Object"
|
||||
)),
|
||||
effectAlignment: .center,
|
||||
action: {
|
||||
openDrawing(.cutout)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: 44.0)
|
||||
)
|
||||
let cutoutButtonFrame = CGRect(
|
||||
origin: CGPoint(x: floorToScreenPixels((availableSize.width - cutoutButtonSize.width) / 2.0), y: stickerFrameRect.maxY + 35.0),
|
||||
size: cutoutButtonSize
|
||||
)
|
||||
if let cutoutButtonView = self.cutoutButton.view {
|
||||
var positionTransition = transition
|
||||
if cutoutButtonView.superview == nil {
|
||||
self.addSubview(cutoutButtonView)
|
||||
|
||||
cutoutButtonView.layer.animatePosition(from: CGPoint(x: 0.0, y: 64.0), to: .zero, duration: 0.3, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
cutoutButtonView.layer.animateAlpha(from: 0.0, to: stickerButtonsAlpha, duration: 0.2, delay: 0.0)
|
||||
cutoutButtonView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: 0.0)
|
||||
positionTransition = .immediate
|
||||
}
|
||||
positionTransition.setPosition(view: cutoutButtonView, position: cutoutButtonFrame.center)
|
||||
cutoutButtonView.bounds = CGRect(origin: .zero, size: cutoutButtonFrame.size)
|
||||
transition.setAlpha(view: cutoutButtonView, alpha: stickerButtonsAlpha)
|
||||
}
|
||||
} else {
|
||||
if let cutoutButtonView = self.cutoutButton.view, cutoutButtonView.superview != nil {
|
||||
cutoutButtonView.alpha = 0.0
|
||||
if transition.animation.isImmediate {
|
||||
cutoutButtonView.removeFromSuperview()
|
||||
} else {
|
||||
cutoutButtonView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.0, completion: { _ in
|
||||
cutoutButtonView.removeFromSuperview()
|
||||
})
|
||||
cutoutButtonView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, delay: 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hasEraseButton {
|
||||
let buttonSpacing: CGFloat = hasRestoreButton ? 10.0 : 0.0
|
||||
var totalButtonsWidth = buttonSpacing
|
||||
|
||||
let eraseButtonSize = self.eraseButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(CutoutButtonContentComponent(
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||
icon: state.image(.erase),
|
||||
title: "Erase",
|
||||
minWidth: 160.0,
|
||||
selected: component.isDisplayingTool == .cutoutErase
|
||||
)),
|
||||
effectAlignment: .center,
|
||||
action: {
|
||||
openDrawing(.cutoutErase)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: 44.0)
|
||||
)
|
||||
totalButtonsWidth += eraseButtonSize.width
|
||||
|
||||
var buttonOriginX = floorToScreenPixels((availableSize.width - totalButtonsWidth) / 2.0)
|
||||
|
||||
if hasRestoreButton {
|
||||
let restoreButtonSize = self.restoreButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(CutoutButtonContentComponent(
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||
icon: state.image(.restore),
|
||||
title: "Restore",
|
||||
minWidth: 160.0,
|
||||
selected: component.isDisplayingTool == .cutoutRestore
|
||||
)),
|
||||
effectAlignment: .center,
|
||||
action: {
|
||||
openDrawing(.cutoutRestore)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: 44.0)
|
||||
)
|
||||
totalButtonsWidth += restoreButtonSize.width
|
||||
|
||||
buttonOriginX = floorToScreenPixels((availableSize.width - totalButtonsWidth) / 2.0)
|
||||
let restoreButtonFrame = CGRect(
|
||||
origin: CGPoint(x: buttonOriginX + eraseButtonSize.width + buttonSpacing, y: stickerFrameRect.maxY + 35.0),
|
||||
size: restoreButtonSize
|
||||
)
|
||||
if let restoreButtonView = self.restoreButton.view {
|
||||
var positionTransition = transition
|
||||
if restoreButtonView.superview == nil {
|
||||
self.addSubview(restoreButtonView)
|
||||
|
||||
restoreButtonView.alpha = stickerButtonsAlpha
|
||||
restoreButtonView.layer.animateAlpha(from: 0.0, to: stickerButtonsAlpha, duration: 0.2, delay: 0.0)
|
||||
restoreButtonView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: 0.0)
|
||||
restoreButtonView.layer.animatePosition(from: CGPoint(x: 0.0, y: 64.0), to: .zero, duration: 0.3, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
positionTransition = .immediate
|
||||
}
|
||||
positionTransition.setPosition(view: restoreButtonView, position: restoreButtonFrame.center)
|
||||
restoreButtonView.bounds = CGRect(origin: .zero, size: restoreButtonFrame.size)
|
||||
transition.setAlpha(view: restoreButtonView, alpha: stickerButtonsAlpha)
|
||||
}
|
||||
}
|
||||
|
||||
let eraseButtonFrame = CGRect(
|
||||
origin: CGPoint(x: buttonOriginX, y: stickerFrameRect.maxY + 35.0),
|
||||
size: eraseButtonSize
|
||||
)
|
||||
if let eraseButtonView = self.eraseButton.view {
|
||||
var positionTransition = transition
|
||||
if eraseButtonView.superview == nil {
|
||||
self.addSubview(eraseButtonView)
|
||||
|
||||
eraseButtonView.alpha = stickerButtonsAlpha
|
||||
eraseButtonView.layer.animatePosition(from: CGPoint(x: 0.0, y: 64.0), to: .zero, duration: 0.3, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
eraseButtonView.layer.animateAlpha(from: 0.0, to: stickerButtonsAlpha, duration: 0.2, delay: 0.0)
|
||||
eraseButtonView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: 0.0)
|
||||
positionTransition = .immediate
|
||||
}
|
||||
positionTransition.setPosition(view: eraseButtonView, position: eraseButtonFrame.center)
|
||||
eraseButtonView.bounds = CGRect(origin: .zero, size: eraseButtonFrame.size)
|
||||
transition.setAlpha(view: eraseButtonView, alpha: stickerButtonsAlpha)
|
||||
}
|
||||
} else {
|
||||
if let eraseButtonView = self.eraseButton.view, eraseButtonView.superview != nil {
|
||||
eraseButtonView.alpha = 0.0
|
||||
eraseButtonView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.0, completion: { _ in
|
||||
eraseButtonView.removeFromSuperview()
|
||||
})
|
||||
eraseButtonView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, delay: 0.0)
|
||||
}
|
||||
}
|
||||
if !hasRestoreButton {
|
||||
if let restoreButtonView = self.restoreButton.view, restoreButtonView.superview != nil {
|
||||
restoreButtonView.alpha = 0.0
|
||||
restoreButtonView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.0, completion: { _ in
|
||||
restoreButtonView.removeFromSuperview()
|
||||
})
|
||||
restoreButtonView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, delay: 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
if hasOutlineButton {
|
||||
let outlineButtonSize = self.outlineButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(CutoutButtonContentComponent(
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||
icon: state.image(.outline),
|
||||
title: "Add Outline",
|
||||
minWidth: 160.0,
|
||||
selected: isOutlineActive
|
||||
)),
|
||||
effectAlignment: .center,
|
||||
action: { [weak self, weak controller] in
|
||||
guard let self, let mediaEditor = controller?.node.mediaEditor else {
|
||||
return
|
||||
}
|
||||
if let value = mediaEditor.values.toolValues[.stickerOutline] as? Float, value > 0.0 {
|
||||
mediaEditor.setToolValue(.stickerOutline, value: Float(0.0))
|
||||
} else {
|
||||
mediaEditor.setToolValue(.stickerOutline, value: Float(0.5))
|
||||
}
|
||||
self.state?.updated(transition: .easeInOut(duration: 0.25))
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: 44.0)
|
||||
)
|
||||
|
||||
let outlineButtonFrame = CGRect(
|
||||
origin: CGPoint(x: floorToScreenPixels((availableSize.width - outlineButtonSize.width) / 2.0), y: stickerFrameRect.maxY + 35.0 + 40.0 + 16.0),
|
||||
size: outlineButtonSize
|
||||
)
|
||||
if let outlineButtonView = self.outlineButton.view {
|
||||
let outlineButtonAlpha = buttonsAreHidden ? 0.0 : bottomButtonsAlpha
|
||||
var positionTransition = transition
|
||||
if outlineButtonView.superview == nil {
|
||||
self.addSubview(outlineButtonView)
|
||||
|
||||
outlineButtonView.alpha = outlineButtonAlpha
|
||||
outlineButtonView.layer.animateAlpha(from: 0.0, to: outlineButtonAlpha, duration: 0.2, delay: 0.0)
|
||||
outlineButtonView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: 0.0)
|
||||
outlineButtonView.layer.animatePosition(from: CGPoint(x: 0.0, y: 64.0), to: .zero, duration: 0.3, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
|
||||
positionTransition = .immediate
|
||||
}
|
||||
positionTransition.setPosition(view: outlineButtonView, position: outlineButtonFrame.center)
|
||||
outlineButtonView.bounds = CGRect(origin: .zero, size: outlineButtonFrame.size)
|
||||
transition.setAlpha(view: outlineButtonView, alpha: outlineButtonAlpha)
|
||||
}
|
||||
} else {
|
||||
if let outlineButtonView = self.outlineButton.view, outlineButtonView.superview != nil {
|
||||
outlineButtonView.alpha = 0.0
|
||||
outlineButtonView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.0, completion: { _ in
|
||||
outlineButtonView.removeFromSuperview()
|
||||
})
|
||||
outlineButtonView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, delay: 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let textCancelButtonSize = self.textCancelButton.update(
|
||||
transition: transition,
|
||||
@ -6690,7 +6690,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
if let navigationController {
|
||||
Queue.mainQueue().after(0.2) {
|
||||
let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash)
|
||||
let controller = self.context.sharedContext.makeStickerPackScreen(context: self.context, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [.result(info: info, items: items, installed: true)], isEditing: false, expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: nil)
|
||||
let controller = self.context.sharedContext.makeStickerPackScreen(context: self.context, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [.result(info: info, items: items, installed: true)], isEditing: false, expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: self.sendSticker)
|
||||
(navigationController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
||||
}
|
||||
}
|
||||
@ -6886,7 +6886,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
let navigationController = self.navigationController as? NavigationController
|
||||
if let navigationController {
|
||||
Queue.mainQueue().after(0.2) {
|
||||
let controller = self.context.sharedContext.makeStickerPackScreen(context: self.context, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], isEditing: false, expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: nil)
|
||||
let controller = self.context.sharedContext.makeStickerPackScreen(context: self.context, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], isEditing: false, expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: self.sendSticker)
|
||||
(navigationController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
||||
|
||||
Queue.mainQueue().after(0.1) {
|
||||
|
@ -68,7 +68,7 @@ final class StickerCutoutOutlineView: UIView {
|
||||
let randomBeginTime = (previousBeginTime + 4) % 6
|
||||
previousBeginTime = randomBeginTime
|
||||
|
||||
let duration = min(6.5, max(3.0, path.length / 100.0))
|
||||
let duration = min(8.0, max(3.0, path.length / 135.0))
|
||||
|
||||
let outlineAnimation = CAKeyframeAnimation(keyPath: "emitterPosition")
|
||||
outlineAnimation.path = path.path.cgPath
|
||||
@ -157,16 +157,18 @@ final class StickerCutoutOutlineView: UIView {
|
||||
}
|
||||
|
||||
private func getPathFromMaskImage(_ image: CIImage, size: CGSize, values: MediaEditorValues) -> BezierPath? {
|
||||
let edges = image.applyingFilter("CILineOverlay", parameters: ["inputEdgeIntensity": 0.1])
|
||||
// let edges = image.applyingFilter("CILineOverlay", parameters: ["inputEdgeIntensity": 0.1])
|
||||
|
||||
guard let pixelBuffer = getEdgesBitmap(edges) else {
|
||||
guard let pixelBuffer = getEdgesBitmap(image) else {
|
||||
return nil
|
||||
}
|
||||
let minSide = min(size.width, size.height)
|
||||
let scaledImageSize = image.extent.size.aspectFilled(CGSize(width: minSide, height: minSide))
|
||||
let contourImageSize = image.extent.size.aspectFilled(CGSize(width: 256.0, height: 256.0))
|
||||
|
||||
var contour = findContours(pixelBuffer: pixelBuffer)
|
||||
// var contour = findContours(pixelBuffer: pixelBuffer)
|
||||
|
||||
var contour = findEdgePoints(in: pixelBuffer)
|
||||
guard !contour.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
@ -203,6 +205,86 @@ private func getPathFromMaskImage(_ image: CIImage, size: CGSize, values: MediaE
|
||||
return nil
|
||||
}
|
||||
|
||||
func findEdgePoints(in pixelBuffer: CVPixelBuffer) -> [CGPoint] {
|
||||
struct Point: Hashable {
|
||||
let x: Int
|
||||
let y: Int
|
||||
|
||||
var cgPoint: CGPoint {
|
||||
return CGPoint(x: x, y: y)
|
||||
}
|
||||
}
|
||||
|
||||
let width = CVPixelBufferGetWidth(pixelBuffer)
|
||||
let height = CVPixelBufferGetHeight(pixelBuffer)
|
||||
var edgePoints: Set<Point> = []
|
||||
var edgePath: [Point] = []
|
||||
|
||||
CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly)
|
||||
let baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer)
|
||||
let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)
|
||||
|
||||
func isPixelWhiteAt(x: Int, y: Int) -> Bool {
|
||||
let pixelOffset = y * bytesPerRow + x
|
||||
let pixelPtr = baseAddress?.advanced(by: pixelOffset)
|
||||
let pixel = pixelPtr?.load(as: UInt8.self) ?? 0
|
||||
return pixel >= 235
|
||||
}
|
||||
|
||||
let directions = [(1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)]
|
||||
var lastDirectionIndex = 0
|
||||
|
||||
var startPoint: Point? = nil
|
||||
outerLoop: for y in 0..<height {
|
||||
for x in 0..<width {
|
||||
if isPixelWhiteAt(x: x, y: y) {
|
||||
startPoint = Point(x: x, y: y)
|
||||
break outerLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guard let startingPoint = startPoint else { return [] }
|
||||
edgePoints.insert(startingPoint)
|
||||
edgePath.append(startingPoint)
|
||||
var currentPoint = startingPoint
|
||||
|
||||
let tolerance: Int = 1
|
||||
func isCloseEnough(_ point: Point, to startPoint: Point) -> Bool {
|
||||
return abs(point.x - startPoint.x) <= tolerance && abs(point.y - startPoint.y) <= tolerance
|
||||
}
|
||||
|
||||
repeat {
|
||||
var foundNextPoint = false
|
||||
for i in 0..<directions.count {
|
||||
let directionIndex = (lastDirectionIndex + i) % directions.count
|
||||
let dir = directions[directionIndex]
|
||||
let nextX = Int(currentPoint.x) + dir.0
|
||||
let nextY = Int(currentPoint.y) + dir.1
|
||||
|
||||
if nextX >= 0, nextX < width, nextY >= 0, nextY < height, isPixelWhiteAt(x: nextX, y: nextY) {
|
||||
let nextPoint = Point(x: nextX, y: nextY)
|
||||
if !edgePoints.contains(nextPoint) {
|
||||
edgePoints.insert(nextPoint)
|
||||
edgePath.append(nextPoint)
|
||||
currentPoint = nextPoint
|
||||
lastDirectionIndex = (directionIndex + 6) % directions.count
|
||||
foundNextPoint = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !foundNextPoint || (edgePath.count > 3 && isCloseEnough(currentPoint, to: startingPoint)) {
|
||||
break
|
||||
}
|
||||
} while true
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly)
|
||||
|
||||
return Array(edgePath.map { $0.cgPoint })
|
||||
}
|
||||
|
||||
private func findContours(pixelBuffer: CVPixelBuffer) -> [CGPoint] {
|
||||
struct Point: Hashable {
|
||||
let x: Int
|
||||
|
@ -794,7 +794,7 @@ public func stickerPackEditTitleController(context: AccountContext, forceDark: B
|
||||
})
|
||||
contentNode.actionNodes.last?.actionEnabled = false
|
||||
contentNode.inputFieldNode.textChanged = { [weak contentNode] title in
|
||||
contentNode?.actionNodes.last?.actionEnabled = title.trimmingTrailingSpaces().count >= 3
|
||||
contentNode?.actionNodes.last?.actionEnabled = title.trimmingTrailingSpaces().trimmingEmojis.count >= 3
|
||||
}
|
||||
controller.willDismiss = { [weak contentNode] in
|
||||
contentNode?.inputFieldNode.deactivateInput()
|
||||
|
@ -4393,15 +4393,32 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumDemoScreen(context: self.context, subject: .noAds, action: {
|
||||
let controller = PremiumIntroScreen(context: self.context, source: .ads)
|
||||
replaceImpl?(controller)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
controller?.replace(with: c)
|
||||
if self.context.isPremium {
|
||||
self.present(UndoOverlayController(presentationData: self.presentationData, content: .actionSucceeded(title: nil, text: self.presentationData.strings.ReportAd_Hidden, cancel: nil, destructive: false), elevatedLayout: false, action: { _ in
|
||||
return true
|
||||
}), in: .current)
|
||||
|
||||
var adOpaqueId: Data?
|
||||
self.chatDisplayNode.historyNode.forEachVisibleMessageItemNode { itemView in
|
||||
if let adAttribute = itemView.item?.message.adAttribute {
|
||||
adOpaqueId = adAttribute.opaqueId
|
||||
}
|
||||
}
|
||||
let _ = self.context.engine.accountData.updateAdMessagesEnabled(enabled: false).start()
|
||||
if let adOpaqueId {
|
||||
self.removeAd(opaqueId: adOpaqueId)
|
||||
}
|
||||
} else {
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumDemoScreen(context: self.context, subject: .noAds, action: {
|
||||
let controller = PremiumIntroScreen(context: self.context, source: .ads)
|
||||
replaceImpl?(controller)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
controller?.replace(with: c)
|
||||
}
|
||||
self.push(controller)
|
||||
}
|
||||
self.push(controller)
|
||||
}, openAdsInfo: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
|
@ -180,19 +180,9 @@ public func chatTranslationState(context: AccountContext, peerId: EnginePeer.Id)
|
||||
}
|
||||
}
|
||||
|
||||
var justUpdated = false
|
||||
return cachedChatTranslationState(engine: context.engine, peerId: peerId)
|
||||
|> mapToSignal { cached in
|
||||
let skipCached: Bool
|
||||
#if DEBUG
|
||||
skipCached = true
|
||||
if justUpdated {
|
||||
return .complete()
|
||||
}
|
||||
#else
|
||||
skipCached = false
|
||||
#endif
|
||||
if let cached, cached.baseLang == baseLang, !skipCached {
|
||||
if let cached, cached.baseLang == baseLang {
|
||||
if !dontTranslateLanguages.contains(cached.fromLang) {
|
||||
return .single(cached)
|
||||
} else {
|
||||
@ -290,7 +280,6 @@ public func chatTranslationState(context: AccountContext, peerId: EnginePeer.Id)
|
||||
}
|
||||
let state = ChatTranslationState(baseLang: baseLang, fromLang: fromLang, toLang: nil, isEnabled: false)
|
||||
let _ = updateChatTranslationState(engine: context.engine, peerId: peerId, state: state).start()
|
||||
justUpdated = true
|
||||
if !dontTranslateLanguages.contains(fromLang) {
|
||||
return state
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user