Various improvements

This commit is contained in:
Ilya Laktyushin 2022-12-25 10:33:53 +04:00
parent 6ece02e998
commit a0f3cfef58
27 changed files with 779 additions and 1894 deletions

View File

@ -8161,6 +8161,7 @@ Sorry for the inconvenience.";
"EmojiSearch.SearchReactionsEmptyResult" = "No emoji found";
"EmojiSearch.SearchStatusesPlaceholder" = "Search Statuses";
"EmojiSearch.SearchStatusesEmptyResult" = "No emoji found";
"EmojiSearch.SearchEmojiEmptyResult" = "No emoji found";
"ChatList.StartAction" = "Start";
"ChatList.CloseAction" = "Close";

View File

@ -458,7 +458,7 @@ private final class BrowserScreenNode: ViewControllerTracingNode {
strongSelf.interaction?.updateForceSerif(false)
action(.default)
}
})), .action(ContextMenuActionItem(text: strongSelf.presentationData.strings.InstantPage_FontNewYork, textFont: .custom(Font.with(size: 17.0, design: .serif, traits: [])), icon: forceSerif ? checkIcon : emptyIcon, action: { [weak self] (controller, action) in
})), .action(ContextMenuActionItem(text: strongSelf.presentationData.strings.InstantPage_FontNewYork, textFont: .custom(font: Font.with(size: 17.0, design: .serif, traits: []), height: nil, verticalOffset: nil), icon: forceSerif ? checkIcon : emptyIcon, action: { [weak self] (controller, action) in
if let strongSelf = self {
strongSelf.interaction?.updateForceSerif(true)
action(.default)

View File

@ -89,7 +89,7 @@ public final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
case .small:
titleFont = smallTextFont
titleBoldFont = smallBoldTextFont
case let .custom(customFont):
case let .custom(customFont, _, _):
titleFont = customFont
titleBoldFont = customFont
}
@ -340,7 +340,7 @@ public final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
titleFont = textFont
case .small:
titleFont = smallTextFont
case let .custom(customFont):
case let .custom(customFont, _, _):
titleFont = customFont
}
@ -387,7 +387,7 @@ public final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
titleFont = textFont
case .small:
titleFont = smallTextFont
case let .custom(customFont):
case let .custom(customFont, _, _):
titleFont = customFont
}

View File

@ -52,7 +52,7 @@ public enum ContextMenuActionResult {
public enum ContextMenuActionItemFont {
case regular
case small
case custom(UIFont)
case custom(font: UIFont, height: CGFloat?, verticalOffset: CGFloat?)
}
public struct ContextMenuActionItemIconSource {

View File

@ -201,12 +201,16 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
self.titleLabelNode.lineSpacing = 0.1
}
var forcedHeight: CGFloat?
var titleVerticalOffset: CGFloat?
let titleFont: UIFont
let titleBoldFont: UIFont
switch self.item.textFont {
case let .custom(font):
case let .custom(font, height, verticalOffset):
titleFont = font
titleBoldFont = font
forcedHeight = height
titleVerticalOffset = verticalOffset
case .small:
let smallTextFont = Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0))
titleFont = smallTextFont
@ -320,15 +324,22 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
} else {
minSize.width += sideInset
}
minSize.height += verticalInset * 2.0
minSize.height += titleSize.height
if subtitle != nil {
minSize.height += titleSubtitleSpacing
minSize.height += subtitleSize.height
if let forcedHeight {
minSize.height = forcedHeight
} else {
minSize.height += verticalInset * 2.0
minSize.height += titleSize.height
if subtitle != nil {
minSize.height += titleSubtitleSpacing
minSize.height += subtitleSize.height
}
}
return (minSize: minSize, apply: { size, transition in
let titleFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalInset), size: titleSize)
var titleFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalInset), size: titleSize)
if let titleVerticalOffset {
titleFrame = titleFrame.offsetBy(dx: 0.0, dy: titleVerticalOffset)
}
let subtitleFrame = CGRect(origin: CGPoint(x: sideInset, y: titleFrame.maxY + titleSubtitleSpacing), size: subtitleSize)
transition.updateFrame(node: self.highlightBackgroundNode, frame: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true)

View File

@ -41,7 +41,6 @@ float2 transformPointCoord(float2 pointCoord, float a, float2 anchor) {
return float2(x, y) + anchor;
}
vertex Point vertex_point_func(constant Point *points [[ buffer(0) ]],
constant Uniforms &uniforms [[ buffer(1) ]],
uint vid [[ vertex_id ]])
@ -62,23 +61,3 @@ fragment float4 fragment_point_func(Point point_data [[ stage_in ]],
float4 color = float4(tex2d.sample(textureSampler, tex_coord));
return float4(point_data.color.rgb, color.a * point_data.color.a);
};
// franment shader that applys original color of the texture
fragment half4 fragment_point_func_original(Point point_data [[ stage_in ]],
texture2d<float> tex2d [[ texture(0) ]],
float2 pointCoord [[ point_coord ]])
{
constexpr sampler textureSampler(mag_filter::linear, min_filter::linear);
half4 color = half4(tex2d.sample(textureSampler, pointCoord));
return half4(color.rgb, color.a * point_data.color.a);
};
fragment float4 fragment_point_func_without_texture(Point point_data [[ stage_in ]],
float2 pointCoord [[ point_coord ]])
{
float dist = length(pointCoord - float2(0.5));
if (dist >= 0.5) {
return float4(0);
}
return point_data.color;
}

View File

@ -178,7 +178,7 @@ final class DrawingBubbleEntityView: DrawingEntityView {
fileprivate var minimumSize: CGSize {
let minSize = min(self.bubbleEntity.referenceDrawingSize.width, self.bubbleEntity.referenceDrawingSize.height)
return CGSize(width: minSize * 0.1, height: minSize * 0.1)
return CGSize(width: minSize * 0.2, height: minSize * 0.2)
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {

File diff suppressed because it is too large Load Diff

View File

@ -49,11 +49,10 @@ final class DrawingMetalView: MTKView {
super.init(frame: CGRect(origin: .zero, size: size), device: device)
self.drawableSize = self.size
self.autoResizeDrawable = false
self.isOpaque = false
self.contentScaleFactor = 1.0
self.drawableSize = self.size
//self.presentsWithTransaction = true
self.setup()
}
@ -124,6 +123,8 @@ final class DrawingMetalView: MTKView {
}
self.drawable?.clear()
self.markerBrush?.pushPoint(CGPoint(x: 100.0, y: 100.0), color: DrawingColor.clear, size: 0.0, isEnd: true)
}
@ -137,9 +138,6 @@ final class DrawingMetalView: MTKView {
}
override func draw(_ rect: CGRect) {
guard !self.isHidden || (self.drawable?.isClearing ?? false) else {
return
}
super.draw(rect)
guard let drawable = self.drawable, let texture = drawable.texture?.texture else {
@ -187,19 +185,12 @@ final class DrawingMetalView: MTKView {
case marker
}
func updated(_ point: Polyline.Point, state: DrawingGesturePipeline.DrawingGestureState, brush: BrushType, color: DrawingColor, size: CGFloat) {
func updated(_ point: DrawingPoint, state: DrawingGesturePipeline.DrawingGestureState, brush: BrushType, color: DrawingColor, size: CGFloat) {
switch brush {
case .marker:
self.markerBrush?.updated(point, color: color, state: state, size: size)
}
}
func setup(_ points: [CGPoint], brush: BrushType, color: DrawingColor, size: CGFloat) {
switch brush {
case .marker:
self.markerBrush?.setup(points, color: color, size: size)
}
}
}
private class Drawable {
@ -229,9 +220,7 @@ private class Drawable {
self.updateBuffer(with: size)
}
var isClearing = false
func clear() {
self.isClearing = true
self.texture?.clear()
self.commit(wait: true)
}
@ -262,10 +251,6 @@ private class Drawable {
self.commandBuffer?.waitUntilCompleted()
}
self.commandBuffer = nil
if self.isClearing && wait {
self.isClearing = false
}
}
internal func makeTexture() -> Texture? {
@ -364,7 +349,7 @@ private class Brush {
}
private let bezier = BezierGenerator()
func updated(_ point: Polyline.Point, color: DrawingColor, state: DrawingGesturePipeline.DrawingGestureState, size: CGFloat) {
func updated(_ point: DrawingPoint, color: DrawingColor, state: DrawingGesturePipeline.DrawingGestureState, size: CGFloat) {
let point = point.location
switch state {
case .began:
@ -491,7 +476,7 @@ private class Stroke {
let overlapping = max(1, line.pointSize / line.pointStep)
var renderingColor = self.color
renderingColor.alpha = renderingColor.alpha / overlapping * 2.5
renderingColor.alpha = renderingColor.alpha / overlapping * 5.5
for i in 0 ..< Int(count) {
let index = CGFloat(i)
@ -524,30 +509,30 @@ class BezierGenerator {
}
init(beginPoint: CGPoint) {
begin(with: beginPoint)
self.begin(with: beginPoint)
}
func begin(with point: CGPoint) {
step = 0
points.removeAll()
points.append(point)
self.step = 0
self.points.removeAll()
self.points.append(point)
}
func pushPoint(_ point: CGPoint) -> [CGPoint] {
if point == points.last {
if point == self.points.last {
return []
}
points.append(point)
if points.count < 3 {
self.points.append(point)
if self.points.count < 3 {
return []
}
step += 1
self.step += 1
return self.generateSmoothPathPoints()
}
func finish() {
step = 0
points.removeAll()
self.step = 0
self.points.removeAll()
}
var points: [CGPoint] = []
@ -556,16 +541,16 @@ class BezierGenerator {
private func generateSmoothPathPoints() -> [CGPoint] {
var begin: CGPoint
var control: CGPoint
let end = CGPoint.middle(p1: points[step], p2: points[step + 1])
let end = CGPoint.middle(p1: self.points[step], p2: self.points[self.step + 1])
var vertices: [CGPoint] = []
if step == 1 {
begin = points[0]
let middle1 = CGPoint.middle(p1: points[0], p2: points[1])
control = CGPoint.middle(p1: middle1, p2: points[1])
if self.step == 1 {
begin = self.points[0]
let middle1 = CGPoint.middle(p1: self.points[0], p2: self.points[1])
control = CGPoint.middle(p1: middle1, p2: self.points[1])
} else {
begin = CGPoint.middle(p1: points[step - 1], p2: points[step])
control = points[step]
begin = CGPoint.middle(p1: self.points[self.step - 1], p2: self.points[self.step])
control = self.points[self.step]
}
let distance = begin.distance(to: end)
@ -573,9 +558,7 @@ class BezierGenerator {
for i in 0 ..< segements {
let t = CGFloat(i) / CGFloat(segements)
let x = pow(1 - t, 2) * begin.x + 2.0 * (1 - t) * t * control.x + t * t * end.x
let y = pow(1 - t, 2) * begin.y + 2.0 * (1 - t) * t * control.y + t * t * end.y
vertices.append(CGPoint(x: x, y: y))
vertices.append(begin.quadBezierPoint(to: end, controlPoint: control, t: t))
}
vertices.append(end)
return vertices

View File

@ -0,0 +1,277 @@
import Foundation
import UIKit
import Display
final class NeonTool: DrawingElement {
class RenderView: UIView, DrawingRenderView {
private weak var element: NeonTool?
private var drawScale = CGSize(width: 1.0, height: 1.0)
let shadowLayer = SimpleShapeLayer()
let borderLayer = SimpleShapeLayer()
let fillLayer = SimpleShapeLayer()
func setup(element: NeonTool, size: CGSize, screenSize: CGSize) {
self.element = element
self.backgroundColor = .clear
self.isOpaque = false
self.contentScaleFactor = 1.0
let shadowRadius = element.renderShadowRadius
let strokeWidth = element.renderStrokeWidth
var shadowColor = element.color.toUIColor()
var fillColor: UIColor = .white
if shadowColor.lightness < 0.01 {
fillColor = shadowColor
shadowColor = UIColor(rgb: 0x440881)
}
let bounds = CGRect(origin: .zero, size: size)
self.frame = bounds
self.shadowLayer.frame = bounds
self.shadowLayer.contentsScale = 1.0
self.shadowLayer.backgroundColor = UIColor.clear.cgColor
self.shadowLayer.lineWidth = strokeWidth * 0.5
self.shadowLayer.lineCap = .round
self.shadowLayer.lineJoin = .round
self.shadowLayer.fillColor = fillColor.cgColor
self.shadowLayer.strokeColor = fillColor.cgColor
self.shadowLayer.shadowColor = shadowColor.cgColor
self.shadowLayer.shadowRadius = shadowRadius
self.shadowLayer.shadowOpacity = 1.0
self.shadowLayer.shadowOffset = .zero
self.borderLayer.frame = bounds
self.borderLayer.contentsScale = 1.0
self.borderLayer.lineWidth = strokeWidth
self.borderLayer.lineCap = .round
self.borderLayer.lineJoin = .round
self.borderLayer.fillColor = UIColor.clear.cgColor
self.borderLayer.strokeColor = fillColor.mixedWith(shadowColor, alpha: 0.25).cgColor
self.fillLayer.frame = bounds
self.fillLayer.contentsScale = 1.0
self.fillLayer.fillColor = fillColor.cgColor
self.layer.addSublayer(self.shadowLayer)
self.layer.addSublayer(self.borderLayer)
self.layer.addSublayer(self.fillLayer)
}
fileprivate func updatePath(_ path: CGPath) {
self.shadowLayer.path = path
self.borderLayer.path = path
self.fillLayer.path = path
}
}
let uuid: UUID
let drawingSize: CGSize
let color: DrawingColor
let renderStrokeWidth: CGFloat
let renderShadowRadius: CGFloat
let renderLineWidth: CGFloat
let renderColor: UIColor
private var pathStarted = false
let path = UIBezierPath()
var activePath: UIBezierPath?
var addedSegments = 0
var renderPath: CGPath?
var translation: CGPoint = .zero
private weak var currentRenderView: DrawingRenderView?
var isValid: Bool {
return self.renderPath != nil
}
var bounds: CGRect {
if let renderPath = self.renderPath {
return normalizeDrawingRect(renderPath.boundingBoxOfPath.insetBy(dx: -self.renderShadowRadius - 30.0, dy: -self.renderShadowRadius - 30.0), drawingSize: self.drawingSize)
} else {
return .zero
}
}
required init(drawingSize: CGSize, color: DrawingColor, lineWidth: CGFloat) {
self.uuid = UUID()
self.drawingSize = drawingSize
self.color = color
let strokeWidth = min(drawingSize.width, drawingSize.height) * 0.01
let shadowRadius = min(drawingSize.width, drawingSize.height) * 0.03
let minLineWidth = max(1.0, max(drawingSize.width, drawingSize.height) * 0.002)
let maxLineWidth = max(10.0, max(drawingSize.width, drawingSize.height) * 0.07)
let lineWidth = minLineWidth + (maxLineWidth - minLineWidth) * lineWidth
self.renderStrokeWidth = strokeWidth
self.renderShadowRadius = shadowRadius
self.renderLineWidth = lineWidth
self.renderColor = color.withUpdatedAlpha(1.0).toUIColor()
}
func setupRenderView(screenSize: CGSize) -> DrawingRenderView? {
let view = RenderView()
view.setup(element: self, size: self.drawingSize, screenSize: screenSize)
self.currentRenderView = view
return view
}
func setupRenderLayer() -> DrawingRenderLayer? {
return nil
}
func updatePath(_ point: DrawingPoint, state: DrawingGesturePipeline.DrawingGestureState) {
guard self.addPoint(point, state: state) else {
return
}
if let currentRenderView = self.currentRenderView as? RenderView {
let path = self.path.cgPath.mutableCopy()
if let activePath {
path?.addPath(activePath.cgPath)
}
if let renderPath = path?.copy(strokingWithWidth: self.renderLineWidth, lineCap: .round, lineJoin: .round, miterLimit: 0.0) {
self.renderPath = renderPath
currentRenderView.updatePath(renderPath)
}
}
if state == .ended, self.addedSegments == 0, let point = self.points.first {
self.renderPath = CGPath(ellipseIn: CGRect(origin: CGPoint(x: point.x - self.renderLineWidth / 2.0, y: point.y - self.renderLineWidth / 2.0), size: CGSize(width: self.renderLineWidth, height: self.renderLineWidth)), transform: nil)
}
}
func draw(in context: CGContext, size: CGSize) {
guard let path = self.renderPath else {
return
}
context.saveGState()
context.translateBy(x: self.translation.x, y: self.translation.y)
context.setShouldAntialias(true)
context.setBlendMode(.normal)
var shadowColor = self.color.toUIColor()
var fillColor: UIColor = .white
if shadowColor.lightness < 0.01 {
fillColor = shadowColor
shadowColor = UIColor(rgb: 0x440881)
}
context.addPath(path)
context.setLineCap(.round)
context.setFillColor(fillColor.cgColor)
context.setStrokeColor(fillColor.cgColor)
context.setLineWidth(self.renderStrokeWidth * 0.5)
context.setShadow(offset: .zero, blur: self.renderShadowRadius * 1.9, color: shadowColor.cgColor)
context.drawPath(using: .fillStroke)
context.addPath(path)
context.setShadow(offset: .zero, blur: 0.0, color: UIColor.clear.cgColor)
context.setLineWidth(self.renderStrokeWidth)
context.setStrokeColor(fillColor.mixedWith(shadowColor, alpha: 0.25).cgColor)
context.strokePath()
context.addPath(path)
context.setFillColor(fillColor.cgColor)
context.fillPath()
context.restoreGState()
}
private var points: [CGPoint] = Array(repeating: .zero, count: 4)
private var pointPtr = 0
private func addPoint(_ point: DrawingPoint, state: DrawingGesturePipeline.DrawingGestureState) -> Bool {
let filterDistance: CGFloat = 10.0
if self.pointPtr == 0 {
self.points[0] = point.location
self.pointPtr += 1
} else {
let previousPoint = self.points[self.pointPtr - 1]
guard previousPoint.distance(to: point.location) > filterDistance else {
return false
}
if self.pointPtr >= 4 {
self.points[3] = self.points[2].point(to: point.location, t: 0.5)
if let bezierPath = self.currentBezierPath(3) {
self.path.append(bezierPath)
self.activePath = nil
}
self.points[0] = self.points[3]
self.pointPtr = 1
}
self.points[self.pointPtr] = point.location
self.pointPtr += 1
}
guard let bezierPath = self.currentBezierPath(self.pointPtr - 1) else {
return false
}
self.activePath = bezierPath
return true
// if self.points.isEmpty {
// self.points[self.ctr] = point
// return
// }
// self.ctr += 1
// self.points[self.ctr] = point
//
// if self.ctr == 4 {
// self.points[3] = CGPoint(x: (self.points[2]!.x + self.points[4]!.x) / 2.0, y: (self.points[2]!.y + self.points[4]!.y) / 2.0)
//
// self.path.move(to: self.points[0]!)
// self.path.addCurve(to: self.points[3]!, controlPoint1: self.points[1]!, controlPoint2: self.points[2]!)
//
// self.points[0] = self.points[3]!
// self.points[1] = self.points[4]!
//
// self.ctr = 1
// self.addedSegments += 1
// }
}
private func currentBezierPath(_ ctr: Int) -> UIBezierPath? {
switch ctr {
case 0:
return nil
case 1:
let path = UIBezierPath()
path.move(to: self.points[0])
path.addLine(to: self.points[1])
return path
case 2:
let path = UIBezierPath()
path.move(to: self.points[0])
path.addQuadCurve(to: self.points[2], controlPoint: self.points[1])
return path
case 3:
let path = UIBezierPath()
path.move(to: self.points[0])
path.addCurve(to: self.points[3], controlPoint1: self.points[1], controlPoint2: self.points[2])
return path
default:
return nil
}
}
}

View File

@ -23,7 +23,6 @@ final class PenTool: DrawingElement {
self.contentMode = .redraw
//let scale = CGSize(width: screenSize.width / max(1.0, size.width), height: screenSize.height / max(1.0, size.height))
let scale = CGSize(width: 0.33, height: 0.33)
let viewSize = CGSize(width: size.width * scale.width, height: size.height * scale.height)
@ -140,6 +139,7 @@ final class PenTool: DrawingElement {
parent.displaySize = rect.size
context.scaleBy(x: 1.0 / parent.drawScale.width, y: 1.0 / parent.drawScale.height)
element.drawSegments(in: context, from: parent.start, to: parent.segmentsCount)
element.drawActiveSegments(in: context)
}
}
}
@ -153,7 +153,7 @@ final class PenTool: DrawingElement {
let hasArrow: Bool
let renderArrowLength: CGFloat
let renderArrowLineWidth: CGFloat
var renderArrowLineWidth: CGFloat
let isEraser: Bool
@ -179,7 +179,7 @@ final class PenTool: DrawingElement {
}
var bounds: CGRect {
return boundingRect(from: 0, to: self.segments.count).insetBy(dx: -20.0, dy: -20.0)
return normalizeDrawingRect(boundingRect(from: 0, to: self.segments.count).insetBy(dx: -20.0, dy: -20.0), drawingSize: self.drawingSize)
}
required init(drawingSize: CGSize, color: DrawingColor, lineWidth: CGFloat, hasArrow: Bool, isEraser: Bool, isBlur: Bool, blurredImage: UIImage?) {
@ -229,41 +229,36 @@ final class PenTool: DrawingElement {
return nil
}
var previousPoint: CGPoint?
func updatePath(_ path: DrawingGesturePipeline.DrawingResult, state: DrawingGesturePipeline.DrawingGestureState) {
guard case let .point(point) = path else {
return
func updatePath(_ point: DrawingPoint, state: DrawingGesturePipeline.DrawingGestureState) {
let result = self.addPoint(point, state: state)
let resetActiveRect = result?.0 ?? false
let updatedRect = result?.1
var combinedRect = updatedRect
if let previousActiveRect = self.previousActiveRect {
combinedRect = updatedRect?.union(previousActiveRect) ?? previousActiveRect
}
if resetActiveRect {
self.previousActiveRect = updatedRect
} else {
self.previousActiveRect = combinedRect
}
let filterDistance: CGFloat = 20.0
if let previousPoint, point.location.distance(to: previousPoint) < filterDistance, state == .changed, self.segments.count > 1 {
return
}
self.previousPoint = point.location
var velocity = point.velocity
if velocity.isZero {
velocity = 1000.0
}
var effectiveRenderLineWidth = max(self.renderMinLineWidth, min(self.renderLineWidth - (velocity / 100.0), self.renderLineWidth))
if let previousRenderLineWidth = self.previousRenderLineWidth {
effectiveRenderLineWidth = effectiveRenderLineWidth * 0.2 + previousRenderLineWidth * 0.8
}
self.previousRenderLineWidth = effectiveRenderLineWidth
let rect = append(point: Point(position: point.location, width: effectiveRenderLineWidth))
if let currentRenderView = self.currentRenderView as? RenderView, let rect = rect {
currentRenderView.draw(element: self, rect: rect)
if let currentRenderView = self.currentRenderView as? RenderView, let combinedRect {
currentRenderView.draw(element: self, rect: combinedRect)
}
if state == .ended {
if !self.activeSegments.isEmpty {
self.segments.append(contentsOf: self.activeSegments)
self.smoothPoints.append(contentsOf: self.activeSmoothPoints)
}
if self.hasArrow {
var direction: CGFloat?
if self.smoothPoints.count > 4 {
let p2 = self.smoothPoints[self.smoothPoints.count - 1].position
let p2 = self.smoothPoints[self.smoothPoints.count - 1].location
for i in 1 ..< min(self.smoothPoints.count - 2, 200) {
let p1 = self.smoothPoints[self.smoothPoints.count - 1 - i].position
let p1 = self.smoothPoints[self.smoothPoints.count - 1 - i].location
if p1.distance(to: p2) > self.renderArrowLength * 0.5 {
direction = p2.angle(to: p1)
break
@ -271,7 +266,7 @@ final class PenTool: DrawingElement {
}
}
self.arrowStart = self.smoothPoints.last?.position
self.arrowStart = self.smoothPoints.last?.location
self.arrowDirection = direction
self.maybeSetupArrow()
} else if self.segments.isEmpty {
@ -284,7 +279,7 @@ final class PenTool: DrawingElement {
d: CGPoint(x: point.x + radius, y: point.y + 0.1),
radius1: radius,
radius2: radius,
rect: .zero
rect: CGRect(origin: CGPoint(x: point.x - radius, y: point.y - radius), size: CGSize(width: radius * 2.0, height: radius * 2.0))
)
)
}
@ -303,6 +298,7 @@ final class PenTool: DrawingElement {
self.arrowLeftPath = arrowLeftPath
self.arrowRightPath = arrowRightPath
self.renderArrowLineWidth = self.smoothPoints.last?.width ?? self.renderArrowLineWidth
}
}
@ -369,7 +365,15 @@ final class PenTool: DrawingElement {
let radius2: CGFloat
let rect: CGRect
init(a: CGPoint, b: CGPoint, c: CGPoint, d: CGPoint, radius1: CGFloat, radius2: CGFloat, rect: CGRect) {
init(
a: CGPoint,
b: CGPoint,
c: CGPoint,
d: CGPoint,
radius1: CGFloat,
radius2: CGFloat,
rect: CGRect
) {
self.a = a
self.b = b
self.c = c
@ -381,95 +385,185 @@ final class PenTool: DrawingElement {
}
private struct Point {
let position: CGPoint
let location: CGPoint
let width: CGFloat
init(
position: CGPoint,
location: CGPoint,
width: CGFloat
) {
self.position = position
self.location = location
self.width = width
}
}
private var points: [Point] = []
private var points: [Point] = Array(repeating: Point(location: .zero, width: 0.0), count: 4)
private var pointPtr = 0
private var smoothPoints: [Point] = []
private var activeSmoothPoints: [Point] = []
private var segments: [Segment] = []
private var activeSegments: [Segment] = []
private var previousActiveRect: CGRect?
private var previousRenderLineWidth: CGFloat?
private func append(point: Point) -> CGRect? {
self.points.append(point)
guard self.points.count > 2 else { return nil }
private func addPoint(_ point: DrawingPoint, state: DrawingGesturePipeline.DrawingGestureState) -> (Bool, CGRect)? {
let filterDistance: CGFloat = 10.0
var velocity = point.velocity
if velocity.isZero {
velocity = 1000.0
}
let index = self.points.count - 1
let point0 = self.points[index - 2]
let point1 = self.points[index - 1]
let point2 = self.points[index]
var renderLineWidth = max(self.renderMinLineWidth, min(self.renderLineWidth - (velocity / 200.0), self.renderLineWidth))
if let previousRenderLineWidth = self.previousRenderLineWidth {
renderLineWidth = renderLineWidth * 0.3 + previousRenderLineWidth * 0.7
}
self.previousRenderLineWidth = renderLineWidth
var resetActiveRect = false
var finalizedRect: CGRect?
if self.pointPtr == 0 {
self.points[0] = Point(location: point.location, width: renderLineWidth)
self.pointPtr += 1
} else {
let previousPoint = self.points[self.pointPtr - 1].location
guard previousPoint.distance(to: point.location) > filterDistance else {
return nil
}
if self.pointPtr >= 4 {
self.points[3] = Point(
location: self.points[2].location.point(to: point.location, t: 0.5),
width: self.points[2].width
)
if var smoothPoints = self.currentSmoothPoints(3) {
if let previousSmoothPoint = self.smoothPoints.last {
smoothPoints.insert(previousSmoothPoint, at: 0)
}
let (segments, rect) = self.segments(fromSmoothPoints: smoothPoints)
self.smoothPoints.append(contentsOf: smoothPoints)
self.segments.append(contentsOf: segments)
finalizedRect = rect
self.activeSmoothPoints.removeAll()
self.activeSegments.removeAll()
resetActiveRect = true
}
self.points[0] = self.points[3]
self.pointPtr = 1
}
let point = Point(location: point.location, width: renderLineWidth)
self.points[self.pointPtr] = point
self.pointPtr += 1
}
var newSmoothPoints = self.smoothPoints(
fromPoint0: point0,
point1: point1,
point2: point2
)
guard let smoothPoints = self.currentSmoothPoints(self.pointPtr - 1) else {
if let finalizedRect {
return (resetActiveRect, finalizedRect)
} else {
return nil
}
}
let previousSmoothPoint = self.smoothPoints.last
self.smoothPoints.append(contentsOf: newSmoothPoints)
let (segments, rect) = self.segments(fromSmoothPoints: smoothPoints)
self.activeSmoothPoints = smoothPoints
self.activeSegments = segments
guard self.smoothPoints.count > 1 else {
var combinedRect: CGRect?
if let finalizedRect, let rect {
combinedRect = finalizedRect.union(rect)
} else {
combinedRect = rect ?? finalizedRect
}
if let combinedRect {
return (resetActiveRect, combinedRect)
} else {
return nil
}
if let previousSmoothPoint = previousSmoothPoint {
newSmoothPoints.insert(previousSmoothPoint, at: 0)
}
let (nextSegments, rect) = self.segments(fromSmoothPoints: newSmoothPoints)
self.segments.append(contentsOf: nextSegments)
for i in self.segments.count - nextSegments.count ..< self.segments.count {
let segment = self.segments[i]
let path = self.pathForSegment(segment)
self.segmentPaths[i] = path
}
return rect
}
private func smoothPoints(fromPoint0 point0: Point, point1: Point, point2: Point) -> [Point] {
var smoothPoints: [Point] = []
private func currentSmoothPoints(_ ctr: Int) -> [Point]? {
switch ctr {
case 0:
return [self.points[0]]
case 1:
return self.smoothPoints(.line(self.points[0], self.points[1]))
case 2:
return self.smoothPoints(.quad(self.points[0], self.points[1], self.points[2]))
case 3:
return self.smoothPoints(.cubic(self.points[0], self.points[1], self.points[2], self.points[3]))
default:
return nil
}
}
let midPoint1 = CGPoint(
x: (point0.position.x + point1.position.x) * 0.5,
y: (point0.position.y + point1.position.y) * 0.5
)
let midPoint2 = CGPoint(
x: (point1.position.x + point2.position.x) * 0.5,
y: (point1.position.y + point2.position.y) * 0.5
)
private enum SmootherInput {
case line(Point, Point)
case quad(Point, Point, Point)
case cubic(Point, Point, Point, Point)
let midWidth1 = (point0.width + point1.width) * 0.5
let midWidth2 = (point1.width + point2.width) * 0.5
var start: Point {
switch self {
case let .line(start, _), let .quad(start, _, _), let .cubic(start, _, _, _):
return start
}
}
var end: Point {
switch self {
case let .line(_, end), let .quad(_, _, end), let .cubic(_, _, _, end):
return end
}
}
var distance: CGFloat {
return self.start.location.distance(to: self.end.location)
}
}
private func smoothPoints(_ input: SmootherInput) -> [Point] {
let segmentDistance: CGFloat = 6.0
let distance = midPoint1.distance(to: midPoint2)
let distance = input.distance
let numberOfSegments = min(48, max(floor(distance / segmentDistance), 24))
let step = 1.0 / numberOfSegments
var smoothPoints: [Point] = []
for t in stride(from: 0, to: 1, by: step) {
let x = midPoint1.x * pow(1 - t, 2) + point1.position.x * 2.0 * (1 - t) * t + midPoint2.x * t * t
let y = midPoint1.y * pow(1 - t, 2) + point1.position.y * 2.0 * (1 - t) * t + midPoint2.y * t * t
let w = midWidth1 * pow(1 - t, 2) + point1.width * 2.0 * (1 - t) * t + midWidth2 * t * t
smoothPoints.append(Point(position: CGPoint(x: x, y: y), width: w))
let point: Point
switch input {
case let .line(start, end):
point = Point(
location: start.location.linearBezierPoint(to: end.location, t: t),
width: CGPoint(x: start.width, y: 0.0).linearBezierPoint(to: CGPoint(x: end.width, y: 0.0), t: t).x
)
case let .quad(start, control, end):
let location = start.location.quadBezierPoint(to: end.location, controlPoint: control.location, t: t)
let width = CGPoint(x: start.width, y: 0.0).quadBezierPoint(to: CGPoint(x: end.width, y: 0.0), controlPoint: CGPoint(x: (start.width + end.width) / 2.0, y: 0.0), t: t).x
point = Point(
location: location,
width: width
)
case let .cubic(start, control1, control2, end):
let location = start.location.cubicBezierPoint(to: end.location, controlPoint1: control1.location, controlPoint2: control2.location, t: t)
let width = CGPoint(x: start.width, y: 0.0).cubicBezierPoint(to: CGPoint(x: end.width, y: 0.0), controlPoint1: CGPoint(x: (start.width + control1.width) / 2.0, y: 0.0), controlPoint2: CGPoint(x: (control2.width + end.width) / 2.0, y: 0.0), t: t).x
point = Point(
location: location,
width: width
)
}
smoothPoints.append(point)
}
smoothPoints.append(Point(position: midPoint2, width: midWidth2))
smoothPoints.append(input.end)
return smoothPoints
}
fileprivate func boundingRect(from: Int, to: Int) -> CGRect {
var minX: CGFloat = .greatestFiniteMagnitude
var minY: CGFloat = .greatestFiniteMagnitude
@ -500,9 +594,9 @@ final class PenTool: DrawingElement {
var segments: [Segment] = []
var updateRect = CGRect.null
for i in 1 ..< smoothPoints.count {
let previousPoint = smoothPoints[i - 1].position
let previousPoint = smoothPoints[i - 1].location
let previousWidth = smoothPoints[i - 1].width
let currentPoint = smoothPoints[i].position
let currentPoint = smoothPoints[i].location
let currentWidth = smoothPoints[i].width
let direction = CGPoint(
x: currentPoint.x - previousPoint.x,
@ -638,5 +732,14 @@ final class PenTool: DrawingElement {
context.fillPath()
}
}
private func drawActiveSegments(in context: CGContext) {
context.setFillColor(self.renderColor.cgColor)
for segment in self.activeSegments {
let path = self.pathForSegment(segment)
context.addPath(path)
context.fillPath()
}
}
}

View File

@ -844,7 +844,7 @@ private final class DrawingScreenComponent: CombinedComponent {
action: { [weak self] f in
f.dismissWithResult(.default)
if let strongSelf = self {
strongSelf.insertEntity.invoke(DrawingVectorEntity(type: .oneSidedArrow, color: strongSelf.currentColor, lineWidth: 0.5))
strongSelf.insertEntity.invoke(DrawingVectorEntity(type: .oneSidedArrow, color: strongSelf.currentColor, lineWidth: 0.3))
}
}
)
@ -2299,9 +2299,16 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController {
guard !self.dismissFontPicker(), let validLayout = self.validLayout?.0 else {
return
}
if let entityView = self.entitiesView.selectedEntityView as? DrawingTextEntityView {
entityView.textChanged = { [weak self] in
self?.dismissFontPicker()
}
}
let fonts: [DrawingTextFont] = [
.sanFrancisco,
.other("AmericanTypewriter", "American Typewriter"),
.other("AmericanTypewriter", "Typewriter"),
.other("AvenirNext-DemiBoldItalic", "Avenir Next"),
.other("CourierNewPS-BoldMT", "Courier New"),
.other("Noteworthy-Bold", "Noteworthy"),
@ -2312,7 +2319,7 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController {
var items: [ContextMenuItem] = []
for font in fonts {
items.append(.action(ContextMenuActionItem(text: font.title, textFont: .custom(font.uiFont(size: 17.0)), icon: { _ in return nil }, animationName: nil, action: { [weak self] f in
items.append(.action(ContextMenuActionItem(text: font.title, textFont: .custom(font: font.uiFont(size: 17.0), height: 42.0, verticalOffset: font.title == "Noteworthy" ? -6.0 : nil), icon: { _ in return nil }, animationName: nil, action: { [weak self] f in
f.dismissWithResult(.default)
guard let strongSelf = self, let entityView = strongSelf.entitiesView.selectedEntityView as? DrawingTextEntityView, let textEntity = entityView.entity as? DrawingTextEntity else {
return

View File

@ -189,7 +189,7 @@ final class DrawingSimpleShapeEntityView: DrawingEntityView {
fileprivate var minimumSize: CGSize {
let minSize = min(self.shapeEntity.referenceDrawingSize.width, self.shapeEntity.referenceDrawingSize.height)
return CGSize(width: minSize * 0.1, height: minSize * 0.1)
return CGSize(width: minSize * 0.2, height: minSize * 0.2)
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {

View File

@ -44,7 +44,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
init(file: TelegramMediaFile) {
self.uuid = UUID()
self.isAnimated = file.isAnimatedSticker
self.isAnimated = file.isAnimatedSticker || file.isVideoSticker
self.file = file

View File

@ -241,6 +241,8 @@ final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate {
var customEmojiContainerView: CustomEmojiContainerView?
var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
var textChanged: () -> Void = {}
init(context: AccountContext, entity: DrawingTextEntity) {
self.textView = DrawingTextView(frame: .zero)
self.textView.clipsToBounds = false
@ -482,6 +484,8 @@ final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate {
self.sizeToFit()
self.update(afterAppendingEmoji: true)
self.textChanged()
}
func insertText(_ text: NSAttributedString) {
@ -1283,7 +1287,6 @@ private var availableFonts: [String: (String, String)] = {
var preferredFont: String?
for name in names {
print(name)
let originalName = name
let name = name.lowercased()
if (!name.contains("-") || name.contains("regular")) && preferredFont == nil {
@ -1299,6 +1302,5 @@ private var availableFonts: [String: (String, String)] = {
result[shortname] = (preferredFont, family)
}
}
//print(result)
return result
}()

View File

@ -2,7 +2,7 @@ import Foundation
import UIKit
import Display
final class MarkerTool: DrawingElement, Codable {
final class MarkerTool: DrawingElement {
let uuid: UUID
let drawingSize: CGSize
@ -17,11 +17,31 @@ final class MarkerTool: DrawingElement, Codable {
weak var metalView: DrawingMetalView?
var isValid: Bool {
return !self.points.isEmpty
return self.points.count > 6
}
var bounds: CGRect {
return CGRect(origin: .zero, size: self.drawingSize)
var minX: CGFloat = .greatestFiniteMagnitude
var minY: CGFloat = .greatestFiniteMagnitude
var maxX: CGFloat = 0.0
var maxY: CGFloat = 0.0
for point in self.points {
if point.x < minX {
minX = point.x
}
if point.x > maxX {
maxX = point.x
}
if point.y < minY {
minY = point.y
}
if point.y > maxY {
maxY = point.y
}
}
return normalizeDrawingRect(CGRect(x: minX, y: minY, width: maxX - minX, height: maxY - minY).insetBy(dx: -80.0, dy: -80.0), drawingSize: self.drawingSize)
}
required init(drawingSize: CGSize, color: DrawingColor, lineWidth: CGFloat) {
@ -35,33 +55,7 @@ final class MarkerTool: DrawingElement, Codable {
self.renderLineWidth = lineWidth
}
private enum CodingKeys: String, CodingKey {
case uuid
case drawingSize
case color
case renderLineWidth
case points
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.uuid = try container.decode(UUID.self, forKey: .uuid)
self.drawingSize = try container.decode(CGSize.self, forKey: .drawingSize)
self.color = try container.decode(DrawingColor.self, forKey: .color)
self.renderLineWidth = try container.decode(CGFloat.self, forKey: .renderLineWidth)
self.points = try container.decode([CGPoint].self, forKey: .points)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.uuid, forKey: .uuid)
try container.encode(self.drawingSize, forKey: .drawingSize)
try container.encode(self.color, forKey: .color)
try container.encode(self.renderLineWidth, forKey: .renderLineWidth)
try container.encode(self.points, forKey: .points)
}
func setupRenderView(screenSize: CGSize) -> DrawingRenderView? {
return nil
}
@ -71,12 +65,11 @@ final class MarkerTool: DrawingElement, Codable {
}
private var didSetup = false
func updatePath(_ path: DrawingGesturePipeline.DrawingResult, state: DrawingGesturePipeline.DrawingGestureState) {
guard case let .location(point) = path else {
return
func updatePath(_ point: DrawingPoint, state: DrawingGesturePipeline.DrawingGestureState) {
if let lastPoint = self.points.last, lastPoint.isEqual(to: point.location, epsilon: 0.1) {
} else {
self.points.append(point.location)
}
self.points.append(point.location)
self.didSetup = true
self.metalView?.updated(point, state: state, brush: .marker, color: self.color, size: self.renderLineWidth)
@ -90,203 +83,14 @@ final class MarkerTool: DrawingElement, Codable {
context.translateBy(x: self.translation.x, y: self.translation.y)
let didSetup = self.didSetup
if didSetup {
self.didSetup = false
} else {
self.metalView?.setup(self.points, brush: .marker, color: self.color, size: self.renderLineWidth)
}
self.metalView?.drawInContext(context)
if !didSetup {
self.metalView?.clear()
}
context.restoreGState()
}
}
final class NeonTool: DrawingElement, Codable {
class RenderLayer: SimpleLayer, DrawingRenderLayer {
var lineWidth: CGFloat = 0.0
let shadowLayer = SimpleShapeLayer()
let borderLayer = SimpleShapeLayer()
let fillLayer = SimpleShapeLayer()
func setup(size: CGSize, color: DrawingColor, lineWidth: CGFloat, strokeWidth: CGFloat, shadowRadius: CGFloat) {
self.contentsScale = 1.0
self.lineWidth = lineWidth
let bounds = CGRect(origin: .zero, size: size)
self.frame = bounds
self.shadowLayer.frame = bounds
self.shadowLayer.backgroundColor = UIColor.clear.cgColor
self.shadowLayer.contentsScale = 1.0
self.shadowLayer.lineWidth = strokeWidth * 0.5
self.shadowLayer.lineCap = .round
self.shadowLayer.lineJoin = .round
self.shadowLayer.fillColor = UIColor.white.cgColor
self.shadowLayer.strokeColor = UIColor.white.cgColor
self.shadowLayer.shadowColor = color.toCGColor()
self.shadowLayer.shadowRadius = shadowRadius
self.shadowLayer.shadowOpacity = 1.0
self.shadowLayer.shadowOffset = .zero
self.borderLayer.frame = bounds
self.borderLayer.contentsScale = 1.0
self.borderLayer.lineWidth = strokeWidth
self.borderLayer.lineCap = .round
self.borderLayer.lineJoin = .round
self.borderLayer.fillColor = UIColor.clear.cgColor
self.borderLayer.strokeColor = UIColor.white.mixedWith(color.toUIColor(), alpha: 0.25).cgColor
self.fillLayer.frame = bounds
self.fillLayer.contentsScale = 1.0
self.fillLayer.fillColor = UIColor.white.cgColor
self.addSublayer(self.shadowLayer)
self.addSublayer(self.borderLayer)
self.addSublayer(self.fillLayer)
}
func updatePath(_ path: CGPath) {
self.shadowLayer.path = path
self.borderLayer.path = path
self.fillLayer.path = path
}
}
let uuid: UUID
let drawingSize: CGSize
let color: DrawingColor
var renderPath: CGPath?
let renderStrokeWidth: CGFloat
let renderShadowRadius: CGFloat
let renderLineWidth: CGFloat
var translation = CGPoint()
private var currentRenderLayer: DrawingRenderLayer?
var isValid: Bool {
return self.renderPath != nil
}
var bounds: CGRect {
return CGRect(origin: .zero, size: self.drawingSize)
}
required init(drawingSize: CGSize, color: DrawingColor, lineWidth: CGFloat) {
self.uuid = UUID()
self.drawingSize = drawingSize
self.color = color
let strokeWidth = min(drawingSize.width, drawingSize.height) * 0.008
let shadowRadius = min(drawingSize.width, drawingSize.height) * 0.03
let minLineWidth = max(1.0, max(drawingSize.width, drawingSize.height) * 0.003)
let maxLineWidth = max(10.0, max(drawingSize.width, drawingSize.height) * 0.09)
let lineWidth = minLineWidth + (maxLineWidth - minLineWidth) * lineWidth
self.renderStrokeWidth = strokeWidth
self.renderShadowRadius = shadowRadius
self.renderLineWidth = lineWidth
}
private enum CodingKeys: String, CodingKey {
case uuid
case drawingSize
case color
case renderStrokeWidth
case renderShadowRadius
case renderLineWidth
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.uuid = try container.decode(UUID.self, forKey: .uuid)
self.drawingSize = try container.decode(CGSize.self, forKey: .drawingSize)
self.color = try container.decode(DrawingColor.self, forKey: .color)
self.renderStrokeWidth = try container.decode(CGFloat.self, forKey: .renderStrokeWidth)
self.renderShadowRadius = try container.decode(CGFloat.self, forKey: .renderShadowRadius)
self.renderLineWidth = try container.decode(CGFloat.self, forKey: .renderLineWidth)
// self.points = try container.decode([CGPoint].self, forKey: .points)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.uuid, forKey: .uuid)
try container.encode(self.drawingSize, forKey: .drawingSize)
try container.encode(self.color, forKey: .color)
try container.encode(self.renderStrokeWidth, forKey: .renderStrokeWidth)
try container.encode(self.renderShadowRadius, forKey: .renderShadowRadius)
try container.encode(self.renderLineWidth, forKey: .renderLineWidth)
// try container.encode(self.points, forKey: .points)
}
func setupRenderView(screenSize: CGSize) -> DrawingRenderView? {
return nil
}
func setupRenderLayer() -> DrawingRenderLayer? {
let layer = RenderLayer()
layer.setup(size: self.drawingSize, color: self.color, lineWidth: self.renderLineWidth, strokeWidth: self.renderStrokeWidth, shadowRadius: self.renderShadowRadius)
self.currentRenderLayer = layer
return layer
}
func updatePath(_ path: DrawingGesturePipeline.DrawingResult, state: DrawingGesturePipeline.DrawingGestureState) {
guard case let .smoothCurve(bezierPath) = path else {
return
}
let cgPath = bezierPath.path.cgPath.copy(strokingWithWidth: self.renderLineWidth, lineCap: .round, lineJoin: .round, miterLimit: 0.0)
self.renderPath = cgPath
if let currentRenderLayer = self.currentRenderLayer as? RenderLayer {
currentRenderLayer.updatePath(cgPath)
}
}
func draw(in context: CGContext, size: CGSize) {
guard let path = self.renderPath else {
return
}
context.saveGState()
context.translateBy(x: self.translation.x, y: self.translation.y)
context.setShouldAntialias(true)
context.setBlendMode(.normal)
context.addPath(path)
context.setFillColor(UIColor.white.cgColor)
context.setStrokeColor(UIColor.white.cgColor)
context.setLineWidth(self.renderStrokeWidth * 0.5)
context.setShadow(offset: .zero, blur: self.renderShadowRadius * 1.9, color: self.color.toCGColor())
context.drawPath(using: .fillStroke)
context.addPath(path)
context.setShadow(offset: .zero, blur: 0.0, color: UIColor.clear.cgColor)
context.setLineCap(.round)
context.setLineWidth(self.renderStrokeWidth)
context.setStrokeColor(UIColor.white.mixedWith(self.color.toUIColor(), alpha: 0.25).cgColor)
context.strokePath()
context.addPath(path)
context.setFillColor(UIColor.white.cgColor)
context.fillPath()
self.metalView?.clear()
context.restoreGState()
}
}
final class FillTool: DrawingElement, Codable {
final class FillTool: DrawingElement {
let uuid: UUID
let drawingSize: CGSize
@ -311,30 +115,7 @@ final class FillTool: DrawingElement, Codable {
self.isBlur = blur
self.blurredImage = blurredImage
}
private enum CodingKeys: String, CodingKey {
case uuid
case drawingSize
case color
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.uuid = try container.decode(UUID.self, forKey: .uuid)
self.drawingSize = try container.decode(CGSize.self, forKey: .drawingSize)
self.color = try container.decode(DrawingColor.self, forKey: .color)
self.isBlur = false
// self.points = try container.decode([CGPoint].self, forKey: .points)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.uuid, forKey: .uuid)
try container.encode(self.drawingSize, forKey: .drawingSize)
try container.encode(self.color, forKey: .color)
// try container.encode(self.points, forKey: .points)
}
func setupRenderView(screenSize: CGSize) -> DrawingRenderView? {
return nil
}
@ -343,7 +124,7 @@ final class FillTool: DrawingElement, Codable {
return nil
}
func updatePath(_ path: DrawingGesturePipeline.DrawingResult, state: DrawingGesturePipeline.DrawingGestureState) {
func updatePath(_ path: DrawingPoint, state: DrawingGesturePipeline.DrawingGestureState) {
}
func draw(in context: CGContext, size: CGSize) {
@ -365,12 +146,4 @@ final class FillTool: DrawingElement, Codable {
context.setBlendMode(.normal)
}
func containsPoint(_ point: CGPoint) -> Bool {
return false
}
func hasPointsInsidePath(_ path: UIBezierPath) -> Bool {
return false
}
}

View File

@ -587,118 +587,6 @@ private func configureControlPoints(data: [CGPoint]) -> [(ctrl1: CGPoint, ctrl2:
return []
}
class BezierPath {
struct Element {
enum ElementType {
case moveTo
case addLine
case cubicCurve
case quadCurve
}
let type: ElementType
var startPoint: Polyline.Point
var endPoint: Polyline.Point
var controlPoints: [CGPoint]
var lengthRange: ClosedRange<CGFloat>?
var calculatedLength: CGFloat?
func point(at t: CGFloat) -> CGPoint {
switch self.type {
case .addLine:
return self.startPoint.location.linearBezierPoint(to: self.endPoint.location, t: t)
case .cubicCurve:
return self.startPoint.location.cubicBezierPoint(to: self.endPoint.location, controlPoint1: self.controlPoints[0], controlPoint2: self.controlPoints[1], t: t)
case .quadCurve:
return self.startPoint.location.quadBezierPoint(to: self.endPoint.location, controlPoint: self.controlPoints[0], t: t)
default:
return .zero
}
}
}
let path = UIBezierPath()
var elements: [Element] = []
var elementCount: Int {
return self.elements.count
}
func element(at t: CGFloat) -> (element: Element, innerT: CGFloat)? {
let t = min(max(0.0, t), 1.0)
for element in elements {
if let lengthRange = element.lengthRange, lengthRange.contains(t) {
let innerT = (t - lengthRange.lowerBound) / (lengthRange.upperBound - lengthRange.lowerBound)
return (element, innerT)
}
}
return nil
}
var points: [Int: CGPoint] = [:]
func point(at t: CGFloat) -> CGPoint? {
if let (element, innerT) = self.element(at: t) {
return element.point(at: innerT)
} else {
return nil
}
}
func append(_ element: Element) {
self.elements.append(element)
switch element.type {
case .moveTo:
self.move(to: element.startPoint.location)
case .addLine:
self.addLine(to: element.endPoint.location)
case .cubicCurve:
self.addCurve(to: element.endPoint.location, controlPoint1: element.controlPoints[0], controlPoint2: element.controlPoints[1])
case .quadCurve:
self.addQuadCurve(to: element.endPoint.location, controlPoint: element.controlPoints[0])
}
}
private func move(to point: CGPoint) {
self.path.move(to: point)
}
private func addLine(to point: CGPoint) {
self.path.addLine(to: point)
}
private func addQuadCurve(to point: CGPoint, controlPoint: CGPoint) {
self.path.addQuadCurve(to: point, controlPoint: controlPoint)
}
private func addCurve(to point: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint) {
self.path.addCurve(to: point, controlPoint1: controlPoint1, controlPoint2: controlPoint2)
}
private func close() {
self.path.close()
}
func trimming(to elementIndex: Int) -> BezierPath {
let outputPath = BezierPath()
for element in self.elements[0 ... elementIndex] {
outputPath.append(element)
}
return outputPath
}
func closedCopy() -> BezierPath {
let outputPath = BezierPath()
for element in self.elements {
outputPath.append(element)
}
outputPath.close()
return outputPath
}
}
class Matrix {
private(set) var m: [Float]
@ -767,3 +655,22 @@ extension CGPoint {
return self.offsetBy(dx: offset.x, dy: offset.y)
}
}
func normalizeDrawingRect(_ rect: CGRect, drawingSize: CGSize) -> CGRect {
var rect = rect
if rect.origin.x < 0.0 {
rect.size.width += rect.origin.x
rect.origin.x = 0.0
}
if rect.origin.y < 0.0 {
rect.size.height += rect.origin.y
rect.origin.y = 0.0
}
if rect.maxX > drawingSize.width {
rect.size.width -= (rect.maxX - drawingSize.width)
}
if rect.maxY > drawingSize.height {
rect.size.height -= (rect.maxY - drawingSize.height)
}
return rect
}

View File

@ -23,7 +23,7 @@ protocol DrawingElement: AnyObject {
func setupRenderView(screenSize: CGSize) -> DrawingRenderView?
func setupRenderLayer() -> DrawingRenderLayer?
func updatePath(_ path: DrawingGesturePipeline.DrawingResult, state: DrawingGesturePipeline.DrawingGestureState)
func updatePath(_ point: DrawingPoint, state: DrawingGesturePipeline.DrawingGestureState)
func draw(in: CGContext, size: CGSize)
}
@ -84,7 +84,7 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, TGPhotoDraw
private var pannedSelectionView: UIView
private var metalView: DrawingMetalView
private var metalView: DrawingMetalView?
private let brushSizePreviewLayer: SimpleShapeLayer
@ -153,9 +153,6 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, TGPhotoDraw
self.pannedSelectionView.backgroundColor = .clear
self.pannedSelectionView.isUserInteractionEnabled = false
self.metalView = DrawingMetalView(size: size)!
self.metalView.isHidden = true
self.brushSizePreviewLayer = SimpleShapeLayer()
self.brushSizePreviewLayer.bounds = CGRect(origin: .zero, size: CGSize(width: 100.0, height: 100.0))
self.brushSizePreviewLayer.strokeColor = UIColor(rgb: 0x919191).cgColor
@ -178,8 +175,7 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, TGPhotoDraw
self.isExclusiveTouch = true
self.addSubview(self.currentDrawingViewContainer)
self.addSubview(self.metalView)
self.layer.addSublayer(self.brushSizePreviewLayer)
let drawingGesturePipeline = DrawingGesturePipeline(view: self)
@ -202,7 +198,7 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, TGPhotoDraw
return false
}
}
drawingGesturePipeline.onDrawing = { [weak self] state, path in
drawingGesturePipeline.onDrawing = { [weak self] state, point in
guard let strongSelf = self else {
return
}
@ -216,14 +212,14 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, TGPhotoDraw
strongSelf.finishDrawing(rect: CGRect(origin: .zero, size: strongSelf.imageSize), synchronous: true)
}
guard let newElement = strongSelf.prepareNewElement() else {
if case .marker = strongSelf.tool, let metalView = strongSelf.metalView {
metalView.isHidden = false
}
guard let newElement = strongSelf.setupNewElement() else {
return
}
if newElement is MarkerTool {
strongSelf.metalView.isHidden = false
}
if let renderView = newElement.setupRenderView(screenSize: strongSelf.screenSize) {
if let currentDrawingView = strongSelf.currentDrawingRenderView {
strongSelf.currentDrawingRenderView = nil
@ -267,84 +263,83 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, TGPhotoDraw
}
strongSelf.currentDrawingLayer = renderLayer
}
newElement.updatePath(path, state: state)
newElement.updatePath(point, state: state)
strongSelf.uncommitedElement = newElement
strongSelf.updateInternalState()
case .changed:
strongSelf.uncommitedElement?.updatePath(path, state: state)
if case let .polyline(line) = path, let lastPoint = line.points.last {
if let previousStrokePoint = strongSelf.previousStrokePoint, line.points.count > 10 {
let currentTimestamp = CACurrentMediaTime()
if lastPoint.location.distance(to: previousStrokePoint) > 10.0 {
strongSelf.previousStrokePoint = lastPoint.location
strongSelf.strokeRecognitionTimer?.invalidate()
strongSelf.strokeRecognitionTimer = nil
}
if strongSelf.strokeRecognitionTimer == nil, let startTimestamp = strongSelf.drawingGestureStartTimestamp, currentTimestamp - startTimestamp < 3.0 {
strongSelf.strokeRecognitionTimer = SwiftSignalKit.Timer(timeout: 0.85, repeat: false, completion: { [weak self] in
guard let strongSelf = self else {
return
}
if let previousStrokePoint = strongSelf.previousStrokePoint, lastPoint.location.distance(to: previousStrokePoint) <= 10.0 {
let strokeRecognizer = Unistroke(points: line.points.map { $0.location })
if let template = strokeRecognizer.match(templates: strongSelf.loadedTemplates, minThreshold: 0.5) {
let edges = line.bounds
let bounds = CGRect(origin: edges.origin, size: CGSize(width: edges.width - edges.minX, height: edges.height - edges.minY))
var entity: DrawingEntity?
if template == "shape_rectangle" {
let shapeEntity = DrawingSimpleShapeEntity(shapeType: .rectangle, drawType: .stroke, color: strongSelf.toolColor, lineWidth: strongSelf.toolBrushSize)
shapeEntity.referenceDrawingSize = strongSelf.imageSize
shapeEntity.position = bounds.center
shapeEntity.size = CGSize(width: bounds.size.width * 1.1, height: bounds.size.height * 1.1)
entity = shapeEntity
} else if template == "shape_circle" {
let shapeEntity = DrawingSimpleShapeEntity(shapeType: .ellipse, drawType: .stroke, color: strongSelf.toolColor, lineWidth: strongSelf.toolBrushSize)
shapeEntity.referenceDrawingSize = strongSelf.imageSize
shapeEntity.position = bounds.center
shapeEntity.size = CGSize(width: bounds.size.width * 1.1, height: bounds.size.height * 1.1)
entity = shapeEntity
} else if template == "shape_star" {
let shapeEntity = DrawingSimpleShapeEntity(shapeType: .star, drawType: .stroke, color: strongSelf.toolColor, lineWidth: strongSelf.toolBrushSize)
shapeEntity.referenceDrawingSize = strongSelf.imageSize
shapeEntity.position = bounds.center
shapeEntity.size = CGSize(width: max(bounds.width, bounds.height) * 1.1, height: max(bounds.width, bounds.height) * 1.1)
entity = shapeEntity
} else if template == "shape_arrow" {
let arrowEntity = DrawingVectorEntity(type: .oneSidedArrow, color: strongSelf.toolColor, lineWidth: strongSelf.toolBrushSize)
arrowEntity.referenceDrawingSize = strongSelf.imageSize
arrowEntity.start = line.points.first?.location ?? .zero
arrowEntity.end = line.points[line.points.count - 4].location
entity = arrowEntity
}
if let entity = entity {
strongSelf.entitiesView?.add(entity)
strongSelf.entitiesView?.selectEntity(entity)
strongSelf.cancelDrawing()
strongSelf.drawingGesturePipeline?.gestureRecognizer?.isEnabled = false
strongSelf.drawingGesturePipeline?.gestureRecognizer?.isEnabled = true
}
}
}
strongSelf.strokeRecognitionTimer?.invalidate()
strongSelf.strokeRecognitionTimer = nil
}, queue: Queue.mainQueue())
strongSelf.strokeRecognitionTimer?.start()
}
} else {
strongSelf.previousStrokePoint = lastPoint.location
}
}
strongSelf.uncommitedElement?.updatePath(point, state: state)
// if case let .direct(point) = path, let lastPoint = line.points.last {
// if let previousStrokePoint = strongSelf.previousStrokePoint, line.points.count > 10 {
// let currentTimestamp = CACurrentMediaTime()
// if lastPoint.location.distance(to: previousStrokePoint) > 10.0 {
// strongSelf.previousStrokePoint = lastPoint.location
//
// strongSelf.strokeRecognitionTimer?.invalidate()
// strongSelf.strokeRecognitionTimer = nil
// }
//
// if strongSelf.strokeRecognitionTimer == nil, let startTimestamp = strongSelf.drawingGestureStartTimestamp, currentTimestamp - startTimestamp < 3.0 {
// strongSelf.strokeRecognitionTimer = SwiftSignalKit.Timer(timeout: 0.85, repeat: false, completion: { [weak self] in
// guard let strongSelf = self else {
// return
// }
// if let previousStrokePoint = strongSelf.previousStrokePoint, lastPoint.location.distance(to: previousStrokePoint) <= 10.0 {
// let strokeRecognizer = Unistroke(points: line.points.map { $0.location })
// if let template = strokeRecognizer.match(templates: strongSelf.loadedTemplates, minThreshold: 0.5) {
// let edges = line.bounds
// let bounds = CGRect(origin: edges.origin, size: CGSize(width: edges.width - edges.minX, height: edges.height - edges.minY))
//
// var entity: DrawingEntity?
// if template == "shape_rectangle" {
// let shapeEntity = DrawingSimpleShapeEntity(shapeType: .rectangle, drawType: .stroke, color: strongSelf.toolColor, lineWidth: strongSelf.toolBrushSize)
// shapeEntity.referenceDrawingSize = strongSelf.imageSize
// shapeEntity.position = bounds.center
// shapeEntity.size = CGSize(width: bounds.size.width * 1.1, height: bounds.size.height * 1.1)
// entity = shapeEntity
// } else if template == "shape_circle" {
// let shapeEntity = DrawingSimpleShapeEntity(shapeType: .ellipse, drawType: .stroke, color: strongSelf.toolColor, lineWidth: strongSelf.toolBrushSize)
// shapeEntity.referenceDrawingSize = strongSelf.imageSize
// shapeEntity.position = bounds.center
// shapeEntity.size = CGSize(width: bounds.size.width * 1.1, height: bounds.size.height * 1.1)
// entity = shapeEntity
// } else if template == "shape_star" {
// let shapeEntity = DrawingSimpleShapeEntity(shapeType: .star, drawType: .stroke, color: strongSelf.toolColor, lineWidth: strongSelf.toolBrushSize)
// shapeEntity.referenceDrawingSize = strongSelf.imageSize
// shapeEntity.position = bounds.center
// shapeEntity.size = CGSize(width: max(bounds.width, bounds.height) * 1.1, height: max(bounds.width, bounds.height) * 1.1)
// entity = shapeEntity
// } else if template == "shape_arrow" {
// let arrowEntity = DrawingVectorEntity(type: .oneSidedArrow, color: strongSelf.toolColor, lineWidth: strongSelf.toolBrushSize)
// arrowEntity.referenceDrawingSize = strongSelf.imageSize
// arrowEntity.start = line.points.first?.location ?? .zero
// arrowEntity.end = line.points[line.points.count - 4].location
// entity = arrowEntity
// }
//
// if let entity = entity {
// strongSelf.entitiesView?.add(entity)
// strongSelf.entitiesView?.selectEntity(entity)
// strongSelf.cancelDrawing()
// strongSelf.drawingGesturePipeline?.gestureRecognizer?.isEnabled = false
// strongSelf.drawingGesturePipeline?.gestureRecognizer?.isEnabled = true
// }
// }
// }
// strongSelf.strokeRecognitionTimer?.invalidate()
// strongSelf.strokeRecognitionTimer = nil
// }, queue: Queue.mainQueue())
// strongSelf.strokeRecognitionTimer?.start()
// }
// } else {
// strongSelf.previousStrokePoint = lastPoint.location
// }
// }
case .ended:
strongSelf.isDrawing = false
strongSelf.strokeRecognitionTimer?.invalidate()
strongSelf.strokeRecognitionTimer = nil
strongSelf.uncommitedElement?.updatePath(path, state: state)
strongSelf.uncommitedElement?.updatePath(point, state: state)
let bounds = strongSelf.uncommitedElement?.bounds
Queue.mainQueue().after(0.05) {
@ -550,8 +545,8 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, TGPhotoDraw
}
if self.tool == .marker {
self.metalView.clear()
self.metalView.isHidden = true
//self.metalView?.clear()
self.metalView?.isHidden = true
}
completion()
}
@ -782,32 +777,33 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, TGPhotoDraw
let previousTool = self.tool
switch state {
case let .pen(brushState):
self.drawingGesturePipeline?.mode = .direct
self.tool = .pen
self.toolColor = brushState.color
self.toolBrushSize = brushState.size
case let .arrow(brushState):
self.drawingGesturePipeline?.mode = .direct
self.tool = .arrow
self.toolColor = brushState.color
self.toolBrushSize = brushState.size
case let .marker(brushState):
self.drawingGesturePipeline?.mode = .location
self.tool = .marker
self.toolColor = brushState.color
self.toolBrushSize = brushState.size
if self.metalView == nil, let metalView = DrawingMetalView(size: self.imageSize) {
metalView.transform = self.currentDrawingViewContainer.transform
metalView.frame = self.currentDrawingViewContainer.frame
self.insertSubview(metalView, aboveSubview: self.currentDrawingViewContainer)
self.metalView = metalView
}
case let .neon(brushState):
self.drawingGesturePipeline?.mode = .smoothCurve
self.tool = .neon
self.toolColor = brushState.color
self.toolBrushSize = brushState.size
case let .blur(blurState):
self.tool = .blur
self.drawingGesturePipeline?.mode = .direct
self.toolBrushSize = blurState.size
case let .eraser(eraserState):
self.tool = .eraser
self.drawingGesturePipeline?.mode = .direct
self.toolBrushSize = eraserState.size
}
@ -859,7 +855,7 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, TGPhotoDraw
self.updateInternalState()
}
private func prepareNewElement() -> DrawingElement? {
private func setupNewElement() -> DrawingElement? {
let scale = 1.0 / self.zoomScale
let element: DrawingElement?
switch self.tool {
@ -952,9 +948,11 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, TGPhotoDraw
self.drawingGesturePipeline?.transform = CGAffineTransformMakeScale(1.0 / scale, 1.0 / scale)
self.metalView.transform = transform
self.metalView.frame = self.bounds
if let metalView = self.metalView {
metalView.transform = transform
metalView.frame = self.bounds
}
self.brushSizePreviewLayer.position = CGPoint(x: self.bounds.width / 2.0, y: self.bounds.height / 2.0)
}

View File

@ -209,7 +209,6 @@ class StickerPickerScreen: ViewController {
let dim: ASDisplayNode
let wrappingView: UIView
let containerView: UIView
let scrollView: UIScrollView
let hostView: ComponentHostView<Empty>
private var content: StickerPickerInputData?
@ -234,16 +233,12 @@ class StickerPickerScreen: ViewController {
self.dim.alpha = 0.0
self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.25)
self.wrappingView = UIView()
self.containerView = UIView()
self.scrollView = UIScrollView()
self.wrappingView = SparseContainerView()
self.containerView = SparseContainerView()
self.hostView = ComponentHostView()
super.init()
self.scrollView.delegate = self
self.scrollView.showsVerticalScrollIndicator = false
self.containerView.clipsToBounds = true
self.containerView.backgroundColor = .clear
@ -251,8 +246,7 @@ class StickerPickerScreen: ViewController {
self.view.addSubview(self.wrappingView)
self.wrappingView.addSubview(self.containerView)
self.containerView.addSubview(self.scrollView)
self.scrollView.addSubview(self.hostView)
self.containerView.addSubview(self.hostView)
self.contentDisposable.set(controller.inputData.start(next: { [weak self] inputData in
if let strongSelf = self {
@ -359,6 +353,9 @@ class StickerPickerScreen: ViewController {
},
updateSearchQuery: { _, _ in
},
updateScrollingToItemGroup: { [weak self] in
self?.update(isExpanded: true, transition: .animated(duration: 0.4, curve: .spring))
},
chatPeerId: nil,
peekBehavior: nil,
customLayout: nil,
@ -433,6 +430,9 @@ class StickerPickerScreen: ViewController {
},
updateSearchQuery: { _, _ in
},
updateScrollingToItemGroup: { [weak self] in
self?.update(isExpanded: true, transition: .animated(duration: 0.4, curve: .spring))
},
chatPeerId: nil,
peekBehavior: nil,
customLayout: nil,
@ -554,6 +554,9 @@ class StickerPickerScreen: ViewController {
},
updateSearchQuery: { _, _ in
},
updateScrollingToItemGroup: { [weak self] in
self?.update(isExpanded: true, transition: .animated(duration: 0.4, curve: .spring))
},
chatPeerId: nil,
peekBehavior: stickerPeekBehavior,
customLayout: nil,
@ -599,11 +602,6 @@ class StickerPickerScreen: ViewController {
return true
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let contentOffset = self.scrollView.contentOffset.y
self.controller?.navigationBar?.updateBackgroundAlpha(min(30.0, contentOffset) / 30.0, transition: .immediate)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer {
if otherGestureRecognizer is PagerPanGestureRecognizer {
@ -727,7 +725,6 @@ class StickerPickerScreen: ViewController {
}
transition.setFrame(view: self.containerView, frame: clipFrame)
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: clipFrame.size), completion: nil)
if let content = self.content {
var stickersTransition: Transition = transition
@ -756,7 +753,6 @@ class StickerPickerScreen: ViewController {
)
contentSize.height = max(layout.size.height - navigationHeight, contentSize.height)
transition.setFrame(view: self.hostView, frame: CGRect(origin: CGPoint(), size: contentSize), completion: nil)
self.scrollView.contentSize = contentSize
}
}
@ -817,11 +813,10 @@ class StickerPickerScreen: ViewController {
let point = recognizer.location(in: self.view)
let currentHitView = self.hitTest(point, with: nil)
var scrollViewAndListNode = self.findScrollView(view: currentHitView)
if scrollViewAndListNode?.frame.height == self.frame.width {
scrollViewAndListNode = nil
var scrollView = self.findScrollView(view: currentHitView)
if scrollView?.frame.height == self.frame.width {
scrollView = nil
}
let scrollView = scrollViewAndListNode
let topInset: CGFloat
if self.isExpanded {

View File

@ -1458,6 +1458,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
}))
}
},
updateScrollingToItemGroup: {
},
chatPeerId: nil,
peekBehavior: nil,
customLayout: emojiContentLayout,

View File

@ -876,6 +876,8 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
}))
}
},
updateScrollingToItemGroup: {
},
chatPeerId: chatPeerId,
peekBehavior: nil,
customLayout: nil,
@ -1090,6 +1092,8 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
},
updateSearchQuery: { _, _ in
},
updateScrollingToItemGroup: {
},
chatPeerId: chatPeerId,
peekBehavior: stickerPeekBehavior,
customLayout: nil,
@ -1111,11 +1115,13 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
var inputData = inputData
inputData.gifs = gifs
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
if let emojiSearchResult = emojiSearchResult {
var emptySearchResults: EmojiPagerContentComponent.EmptySearchResults?
if !emojiSearchResult.groups.contains(where: { !$0.items.isEmpty }) {
emptySearchResults = EmojiPagerContentComponent.EmptySearchResults(
text: "No emoji found", //strongSelf.presentationData.strings.EmojiSearch_SearchStatusesEmptyResult,
text: presentationData.strings.EmojiSearch_SearchEmojiEmptyResult,
iconFile: nil
)
}
@ -1861,6 +1867,8 @@ public final class EntityInputView: UIInputView, AttachmentTextInputPanelInputVi
},
updateSearchQuery: { _, _ in
},
updateScrollingToItemGroup: {
},
chatPeerId: nil,
peekBehavior: nil,
customLayout: nil,

View File

@ -554,6 +554,8 @@ public final class EmojiStatusSelectionController: ViewController {
}))
}
},
updateScrollingToItemGroup: {
},
chatPeerId: nil,
peekBehavior: nil,
customLayout: nil,

View File

@ -2113,6 +2113,7 @@ public final class EmojiPagerContentComponent: Component {
public let navigationController: () -> NavigationController?
public let requestUpdate: (Transition) -> Void
public let updateSearchQuery: (String, String) -> Void
public let updateScrollingToItemGroup: () -> Void
public let chatPeerId: PeerId?
public let peekBehavior: EmojiContentPeekBehavior?
public let customLayout: CustomLayout?
@ -2135,6 +2136,7 @@ public final class EmojiPagerContentComponent: Component {
navigationController: @escaping () -> NavigationController?,
requestUpdate: @escaping (Transition) -> Void,
updateSearchQuery: @escaping (String, String) -> Void,
updateScrollingToItemGroup: @escaping () -> Void,
chatPeerId: PeerId?,
peekBehavior: EmojiContentPeekBehavior?,
customLayout: CustomLayout?,
@ -2156,6 +2158,7 @@ public final class EmojiPagerContentComponent: Component {
self.navigationController = navigationController
self.requestUpdate = requestUpdate
self.updateSearchQuery = updateSearchQuery
self.updateScrollingToItemGroup = updateScrollingToItemGroup
self.chatPeerId = chatPeerId
self.peekBehavior = peekBehavior
self.customLayout = customLayout
@ -7653,8 +7656,10 @@ public final class EmojiPagerContentComponent: Component {
}
}
let isMasks = stickerNamespaces.contains(Namespaces.ItemCollection.CloudMaskPacks)
return EmojiPagerContentComponent(
id: "stickers",
id: isMasks ? "masks" : "stickers",
context: context,
avatarPeer: avatarPeer,
animationCache: animationCache,

View File

@ -362,7 +362,7 @@ public final class EntityKeyboardComponent: Component {
guard let strongSelf = self else {
return
}
strongSelf.reorderPacks(category: .stickers, items: items)
strongSelf.reorderPacks(category: .masks, items: items)
}
))))
contentIcons.append(PagerComponentContentIcon(id: "masks", imageName: "Chat/Input/Media/EntityInputMasksIcon"))
@ -913,6 +913,8 @@ public final class EntityKeyboardComponent: Component {
if let topPanelView = pagerView.topPanelComponentView as? EntityKeyboardTopContainerPanelComponent.View {
topPanelView.internalUpdatePanelsAreCollapsed()
}
self.component?.emojiContent?.inputInteractionHolder.inputInteraction?.updateScrollingToItemGroup()
pagerContentView.scrollToItemGroup(id: groupId, subgroupId: subgroupId)
pagerView.collapseTopPanel()
}

View File

@ -964,6 +964,8 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
},
updateSearchQuery: { _, _ in
},
updateScrollingToItemGroup: {
},
chatPeerId: nil,
peekBehavior: nil,
customLayout: nil,

View File

@ -409,7 +409,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var actions: [ContextMenuItem] = []
actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Info, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0)), badge: nil, icon: { theme in
actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Info, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor)
}, iconSource: nil, action: { c, _ in
c.dismiss(completion: {
@ -419,7 +419,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
if !chatPresentationInterfaceState.isPremium && !premiumConfiguration.isPremiumDisabled {
actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Hide, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0)), badge: nil, icon: { theme in
actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Hide, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.actionSheet.primaryTextColor)
}, iconSource: nil, action: { c, _ in
c.dismiss(completion: {

View File

@ -256,11 +256,19 @@ private class ExtendedMediaOverlayNode: ASDisplayNode {
}
func reveal() {
self.isRevealed = true
self.blurredImageNode.removeFromSupernode()
self.dustNode.removeFromSupernode()
self.isUserInteractionEnabled = false
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event)
if self.isRevealed {
return nil
}
return result
}
func update(size: CGSize, text: String, imageSignal: (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize, CGSize)?, imageFrame: CGRect, corners: ImageCorners?) {
let spacing: CGFloat = 2.0
let padding: CGFloat = 10.0
@ -274,14 +282,15 @@ private class ExtendedMediaOverlayNode: ASDisplayNode {
apply()
self.blurredImageNode.isHidden = false
self.isUserInteractionEnabled = !self.dustNode.isRevealed
self.isRevealed = self.dustNode.isRevealed
self.dustNode.revealed = { [weak self] in
self?.isRevealed = true
self?.blurredImageNode.removeFromSupernode()
self?.isUserInteractionEnabled = false
}
self.dustNode.tapped = { [weak self] in
self?.isRevealed = true
self?.tapped()
}
} else {
@ -1916,7 +1925,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
} else if message.attributes.contains(where: { $0 is MediaSpoilerMessageAttribute }) {
displaySpoiler = true
}
if displaySpoiler {
if self.extendedMediaOverlayNode == nil {
let extendedMediaOverlayNode = ExtendedMediaOverlayNode()
@ -1929,6 +1938,16 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
}
self.extendedMediaOverlayNode?.frame = self.imageNode.frame
var tappable = false
switch state {
case .play, .pause, .download, .none:
tappable = false
default:
break
}
self.extendedMediaOverlayNode?.isUserInteractionEnabled = tappable
var paymentText: String = ""
outer: for attribute in message.attributes {
if let attribute = attribute as? ReplyMarkupMessageAttribute {