Merge commit 'd4c13120f8d9df314e0447065ea88b897f25143d'

This commit is contained in:
Isaac 2024-04-10 13:59:56 +04:00
commit 4ec8f3449e
5 changed files with 418 additions and 330 deletions

View File

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

View File

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

View File

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

View File

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

View File

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