mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-18 11:30:04 +00:00
Drawing improvements
This commit is contained in:
parent
53adba6b1f
commit
f9ca86db34
@ -8565,6 +8565,12 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Paint.MoveForward" = "Move Forward";
|
||||
|
||||
"Paint.ColorTitle" = "Colors";
|
||||
"Paint.ColorGrid" = "Grid";
|
||||
"Paint.ColorSpectrum" = "Spectrum";
|
||||
"Paint.ColorSliders" = "Sliders";
|
||||
"Paint.ColorOpacity" = "OPACITY";
|
||||
|
||||
"StorageManagement.Title" = "Storage Usage";
|
||||
"StorageManagement.TitleCleared" = "Storage Cleared";
|
||||
|
||||
|
@ -2026,6 +2026,8 @@ private final class ColorPickerContent: CombinedComponent {
|
||||
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
|
||||
let component = context.component
|
||||
let state = context.state
|
||||
let strings = environment.strings
|
||||
|
||||
state.colorChanged = component.colorChanged
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
@ -2079,7 +2081,7 @@ private final class ColorPickerContent: CombinedComponent {
|
||||
let title = title.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "Colors",
|
||||
string: strings.Paint_ColorTitle,
|
||||
font: Font.semibold(17.0),
|
||||
textColor: .white,
|
||||
paragraphAlignment: .center
|
||||
@ -2098,7 +2100,7 @@ private final class ColorPickerContent: CombinedComponent {
|
||||
|
||||
let modeControl = modeControl.update(
|
||||
component: SegmentedControlComponent(
|
||||
values: ["Grid", "Spectrum", "Sliders"],
|
||||
values: [strings.Paint_ColorGrid, strings.Paint_ColorSpectrum, strings.Paint_ColorSliders],
|
||||
selectedIndex: 0,
|
||||
selectionChanged: { [weak state] index in
|
||||
state?.updateSelectedMode(index)
|
||||
@ -2172,7 +2174,7 @@ private final class ColorPickerContent: CombinedComponent {
|
||||
let opacityTitle = opacityTitle.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "OPACITY",
|
||||
string: strings.Paint_ColorOpacity,
|
||||
font: Font.semibold(13.0),
|
||||
textColor: UIColor(rgb: 0x9b9da5),
|
||||
paragraphAlignment: .center
|
||||
|
@ -134,6 +134,8 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
|
||||
|
||||
public var getEntityCenterPosition: () -> CGPoint = { return .zero }
|
||||
public var getEntityInitialRotation: () -> CGFloat = { return 0.0 }
|
||||
public var getEntityAdditionalScale: () -> CGFloat = { return 1.0 }
|
||||
|
||||
public var hasSelectionChanged: (Bool) -> Void = { _ in }
|
||||
var selectionChanged: (DrawingEntity?) -> Void = { _ in }
|
||||
var requestedMenuForEntityView: (DrawingEntityView, Bool) -> Void = { _, _ in }
|
||||
|
@ -60,8 +60,9 @@ final class NeonTool: DrawingElement {
|
||||
self.layer.addSublayer(self.fillLayer)
|
||||
}
|
||||
|
||||
fileprivate func updatePath(_ path: CGPath) {
|
||||
fileprivate func updatePath(_ path: CGPath, shadowPath: CGPath) {
|
||||
self.shadowLayer.path = path
|
||||
self.shadowLayer.shadowPath = shadowPath
|
||||
self.borderLayer.path = path
|
||||
self.fillLayer.path = path
|
||||
}
|
||||
@ -81,6 +82,7 @@ final class NeonTool: DrawingElement {
|
||||
private var addedPaths = 0
|
||||
|
||||
fileprivate var renderPath: CGPath?
|
||||
fileprivate var shadowRenderPath: CGPath?
|
||||
|
||||
var translation: CGPoint = .zero
|
||||
|
||||
@ -91,7 +93,7 @@ final class NeonTool: DrawingElement {
|
||||
}
|
||||
|
||||
var bounds: CGRect {
|
||||
if let renderPath = self.renderPath {
|
||||
if let renderPath = self.shadowRenderPath {
|
||||
return normalizeDrawingRect(renderPath.boundingBoxOfPath.insetBy(dx: -self.renderShadowRadius - 30.0, dy: -self.renderShadowRadius - 30.0), drawingSize: self.drawingSize)
|
||||
} else {
|
||||
return .zero
|
||||
@ -103,8 +105,8 @@ final class NeonTool: DrawingElement {
|
||||
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 strokeWidth = min(drawingSize.width, drawingSize.height) * 0.008
|
||||
let shadowRadius = min(drawingSize.width, drawingSize.height) * 0.02
|
||||
|
||||
let minLineWidth = max(1.0, max(drawingSize.width, drawingSize.height) * 0.002)
|
||||
let maxLineWidth = max(10.0, max(drawingSize.width, drawingSize.height) * 0.07)
|
||||
@ -138,9 +140,11 @@ final class NeonTool: DrawingElement {
|
||||
if let activePath {
|
||||
path?.addPath(activePath.cgPath)
|
||||
}
|
||||
if let renderPath = path?.copy(strokingWithWidth: self.renderLineWidth, lineCap: .round, lineJoin: .round, miterLimit: 0.0) {
|
||||
if let renderPath = path?.copy(strokingWithWidth: self.renderLineWidth, lineCap: .round, lineJoin: .round, miterLimit: 0.0),
|
||||
let shadowRenderPath = path?.copy(strokingWithWidth: self.renderLineWidth * 2.0, lineCap: .round, lineJoin: .round, miterLimit: 0.0) {
|
||||
self.renderPath = renderPath
|
||||
currentRenderView.updatePath(renderPath)
|
||||
self.shadowRenderPath = shadowRenderPath
|
||||
currentRenderView.updatePath(renderPath, shadowPath: shadowRenderPath)
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,7 +159,7 @@ final class NeonTool: DrawingElement {
|
||||
}
|
||||
|
||||
func draw(in context: CGContext, size: CGSize) {
|
||||
guard let path = self.renderPath else {
|
||||
guard let path = self.renderPath, let shadowPath = self.shadowRenderPath else {
|
||||
return
|
||||
}
|
||||
context.saveGState()
|
||||
@ -173,14 +177,19 @@ final class NeonTool: DrawingElement {
|
||||
shadowColor = UIColor(rgb: 0x440881)
|
||||
}
|
||||
|
||||
context.addPath(path)
|
||||
let shadowOffset = CGSize(width: 3000.0, height: 3000.0)
|
||||
context.translateBy(x: -shadowOffset.width, y: -shadowOffset.height)
|
||||
|
||||
context.addPath(shadowPath)
|
||||
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.setShadow(offset: shadowOffset, blur: self.renderShadowRadius * 1.9, color: shadowColor.withAlphaComponent(0.87).cgColor)
|
||||
context.drawPath(using: .fillStroke)
|
||||
|
||||
context.translateBy(x: shadowOffset.width, y: shadowOffset.height)
|
||||
|
||||
context.addPath(path)
|
||||
context.setShadow(offset: .zero, blur: 0.0, color: UIColor.clear.cgColor)
|
||||
context.setLineWidth(self.renderStrokeWidth)
|
||||
|
@ -2,6 +2,8 @@ import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
|
||||
private let activeWidthFactor: CGFloat = 0.7
|
||||
|
||||
final class PenTool: DrawingElement {
|
||||
class RenderView: UIView, DrawingRenderView {
|
||||
private weak var element: PenTool?
|
||||
@ -22,7 +24,6 @@ final class PenTool: DrawingElement {
|
||||
self.isOpaque = false
|
||||
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)
|
||||
|
||||
@ -80,7 +81,26 @@ final class PenTool: DrawingElement {
|
||||
})
|
||||
}
|
||||
|
||||
var displaySize: CGSize?
|
||||
var onDryingUp: () -> Void = {}
|
||||
|
||||
var isDryingUp = false {
|
||||
didSet {
|
||||
if !self.isDryingUp {
|
||||
self.onDryingUp()
|
||||
}
|
||||
}
|
||||
}
|
||||
var dryingLayersCount: Int = 0 {
|
||||
didSet {
|
||||
if self.dryingLayersCount > 0 {
|
||||
self.isDryingUp = true
|
||||
} else {
|
||||
self.isDryingUp = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var displaySize: CGSize?
|
||||
fileprivate func draw(element: PenTool, rect: CGRect) {
|
||||
self.element = element
|
||||
|
||||
@ -120,15 +140,71 @@ final class PenTool: DrawingElement {
|
||||
self.start = newStart
|
||||
}
|
||||
|
||||
if !element.isEraser && !element.isBlur {
|
||||
let count = CGFloat(element.segments.count - self.segmentsCount)
|
||||
if count > 0 {
|
||||
let dryingPath = CGMutablePath()
|
||||
var abFactor: CGFloat = activeWidthFactor * 1.35
|
||||
let delta: CGFloat = (1.0 - abFactor) / count
|
||||
for i in self.segmentsCount ..< element.segments.count {
|
||||
let segmentPath = element.pathForSegment(element.segments[i], abFactor: abFactor, cdFactor: abFactor + delta)
|
||||
dryingPath.addPath(segmentPath)
|
||||
abFactor += delta
|
||||
}
|
||||
self.setupDrying(path: dryingPath)
|
||||
}
|
||||
}
|
||||
|
||||
self.segmentsCount = element.segments.count
|
||||
|
||||
if let rect = rect {
|
||||
self.activeView?.setNeedsDisplay(rect.insetBy(dx: -10.0, dy: -10.0).applying(CGAffineTransform(scaleX: 1.0 / self.drawScale.width, y: 1.0 / self.drawScale.height)))
|
||||
self.activeView?.setNeedsDisplay(rect.insetBy(dx: -40.0, dy: -40.0).applying(CGAffineTransform(scaleX: 1.0 / self.drawScale.width, y: 1.0 / self.drawScale.height)))
|
||||
} else {
|
||||
self.activeView?.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
private let dryingFactor: CGFloat = 0.4
|
||||
func setupDrying(path: CGPath) {
|
||||
guard let element = self.element else {
|
||||
return
|
||||
}
|
||||
|
||||
let dryingLayer = CAShapeLayer()
|
||||
dryingLayer.contentsScale = 1.0
|
||||
dryingLayer.fillColor = element.renderColor.cgColor
|
||||
dryingLayer.strokeColor = element.renderColor.cgColor
|
||||
dryingLayer.lineWidth = element.renderLineWidth * self.dryingFactor
|
||||
dryingLayer.path = path
|
||||
dryingLayer.animate(from: dryingLayer.lineWidth as NSNumber, to: 0.0 as NSNumber, keyPath: "lineWidth", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.4, removeOnCompletion: false, completion: { [weak dryingLayer] _ in
|
||||
dryingLayer?.removeFromSuperlayer()
|
||||
self.dryingLayersCount -= 1
|
||||
})
|
||||
dryingLayer.transform = CATransform3DMakeScale(1.0 / self.drawScale.width, 1.0 / self.drawScale.height, 1.0)
|
||||
dryingLayer.frame = self.bounds
|
||||
self.layer.addSublayer(dryingLayer)
|
||||
|
||||
self.dryingLayersCount += 1
|
||||
}
|
||||
|
||||
private var isActiveDrying = false
|
||||
func setupActiveSegmentsDrying() {
|
||||
guard let element = self.element else {
|
||||
return
|
||||
}
|
||||
|
||||
if !element.isEraser && !element.isBlur {
|
||||
let dryingPath = CGMutablePath()
|
||||
for segment in element.activeSegments {
|
||||
let segmentPath = element.pathForSegment(segment)
|
||||
dryingPath.addPath(segmentPath)
|
||||
}
|
||||
self.setupDrying(path: dryingPath)
|
||||
self.isActiveDrying = true
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
class ActiveView: UIView {
|
||||
weak var parent: RenderView?
|
||||
override func draw(_ rect: CGRect) {
|
||||
@ -139,7 +215,12 @@ 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)
|
||||
|
||||
if !element.isEraser || !element.isBlur {
|
||||
element.drawActiveSegments(in: context, strokeWidth: !parent.isActiveDrying ? element.renderLineWidth * parent.dryingFactor : nil)
|
||||
} else {
|
||||
element.drawActiveSegments(in: context, strokeWidth: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -170,6 +251,23 @@ final class PenTool: DrawingElement {
|
||||
|
||||
private weak var currentRenderView: DrawingRenderView?
|
||||
|
||||
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 var segmentPaths: [Int: CGPath] = [:]
|
||||
|
||||
private var useCubicBezier = true
|
||||
|
||||
var isValid: Bool {
|
||||
if self.hasArrow {
|
||||
return self.arrowStart != nil && self.arrowDirection != nil
|
||||
@ -249,6 +347,8 @@ final class PenTool: DrawingElement {
|
||||
|
||||
if state == .ended {
|
||||
if !self.activeSegments.isEmpty {
|
||||
(self.currentRenderView as? RenderView)?.setupActiveSegmentsDrying()
|
||||
|
||||
self.segments.append(contentsOf: self.activeSegments)
|
||||
self.smoothPoints.append(contentsOf: self.activeSmoothPoints)
|
||||
}
|
||||
@ -279,6 +379,9 @@ final class PenTool: DrawingElement {
|
||||
d: CGPoint(x: point.x + radius, y: point.y + 0.1),
|
||||
radius1: radius,
|
||||
radius2: radius,
|
||||
abCenter: CGPoint(x: point.x, y: point.y),
|
||||
cdCenter: CGPoint(x: point.x, y: point.y + 0.1),
|
||||
perpendicular: .zero,
|
||||
rect: CGRect(origin: CGPoint(x: point.x - radius, y: point.y - radius), size: CGSize(width: radius * 2.0, height: radius * 2.0))
|
||||
)
|
||||
)
|
||||
@ -363,6 +466,9 @@ final class PenTool: DrawingElement {
|
||||
let d: CGPoint
|
||||
let radius1: CGFloat
|
||||
let radius2: CGFloat
|
||||
let abCenter: CGPoint
|
||||
let cdCenter: CGPoint
|
||||
let perpendicular: CGPoint
|
||||
let rect: CGRect
|
||||
|
||||
init(
|
||||
@ -372,6 +478,9 @@ final class PenTool: DrawingElement {
|
||||
d: CGPoint,
|
||||
radius1: CGFloat,
|
||||
radius2: CGFloat,
|
||||
abCenter: CGPoint,
|
||||
cdCenter: CGPoint,
|
||||
perpendicular: CGPoint,
|
||||
rect: CGRect
|
||||
) {
|
||||
self.a = a
|
||||
@ -380,8 +489,43 @@ final class PenTool: DrawingElement {
|
||||
self.d = d
|
||||
self.radius1 = radius1
|
||||
self.radius2 = radius2
|
||||
self.abCenter = abCenter
|
||||
self.cdCenter = cdCenter
|
||||
self.perpendicular = perpendicular
|
||||
self.rect = rect
|
||||
}
|
||||
|
||||
func withMultiplied(abFactor: CGFloat, cdFactor: CGFloat) -> Segment {
|
||||
let a = CGPoint(
|
||||
x: self.abCenter.x + self.perpendicular.x * self.radius1 * abFactor,
|
||||
y: self.abCenter.y + self.perpendicular.y * self.radius1 * abFactor
|
||||
)
|
||||
let b = CGPoint(
|
||||
x: self.abCenter.x - self.perpendicular.x * self.radius1 * abFactor,
|
||||
y: self.abCenter.y - self.perpendicular.y * self.radius1 * abFactor
|
||||
)
|
||||
let c = CGPoint(
|
||||
x: self.cdCenter.x + self.perpendicular.x * self.radius2 * cdFactor,
|
||||
y: self.cdCenter.y + self.perpendicular.y * self.radius2 * cdFactor
|
||||
)
|
||||
let d = CGPoint(
|
||||
x: self.cdCenter.x - self.perpendicular.x * self.radius2 * cdFactor,
|
||||
y: self.cdCenter.y - self.perpendicular.y * self.radius2 * cdFactor
|
||||
)
|
||||
|
||||
return Segment(
|
||||
a: a,
|
||||
b: b,
|
||||
c: c,
|
||||
d: d,
|
||||
radius1: self.radius1 * abFactor,
|
||||
radius2: self.radius2 * cdFactor,
|
||||
abCenter: self.abCenter,
|
||||
cdCenter: self.cdCenter,
|
||||
perpendicular: self.perpendicular,
|
||||
rect: self.rect
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private struct Point {
|
||||
@ -397,26 +541,15 @@ final class PenTool: DrawingElement {
|
||||
}
|
||||
}
|
||||
|
||||
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 var currentVelocity: CGFloat?
|
||||
private func addPoint(_ point: DrawingPoint, state: DrawingGesturePipeline.DrawingGestureState, zoomScale: CGFloat) -> (Bool, CGRect)? {
|
||||
let filterDistance: CGFloat = 10.0 / zoomScale
|
||||
let filterDistance: CGFloat = 8.0 / zoomScale
|
||||
|
||||
var velocity = point.velocity
|
||||
if velocity.isZero {
|
||||
velocity = 1000.0
|
||||
}
|
||||
self.currentVelocity = velocity
|
||||
|
||||
var renderLineWidth = max(self.renderMinLineWidth, min(self.renderLineWidth - (velocity / 200.0), self.renderLineWidth))
|
||||
if let previousRenderLineWidth = self.previousRenderLineWidth {
|
||||
@ -492,9 +625,9 @@ final class PenTool: DrawingElement {
|
||||
private func currentSmoothPoints(_ ctr: Int) -> [Point]? {
|
||||
switch ctr {
|
||||
case 0:
|
||||
return [self.points[0]]
|
||||
return nil//return [self.points[0]]
|
||||
case 1:
|
||||
return self.smoothPoints(.line(self.points[0], self.points[1]))
|
||||
return nil//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:
|
||||
@ -667,15 +800,29 @@ final class PenTool: DrawingElement {
|
||||
let segmentRect = CGRect(x: minX, y: minY, width: maxX - minX, height: maxY - minY)
|
||||
updateRect = updateRect.union(segmentRect)
|
||||
|
||||
let segment = Segment(a: a, b: b, c: c, d: d, radius1: previousWidth / 2.0, radius2: currentWidth / 2.0, rect: segmentRect)
|
||||
let segment = Segment(
|
||||
a: a,
|
||||
b: b,
|
||||
c: c,
|
||||
d: d,
|
||||
radius1: previousWidth / 2.0,
|
||||
radius2: currentWidth / 2.0,
|
||||
abCenter: abCenter,
|
||||
cdCenter: cdCenter,
|
||||
perpendicular: perpendicular,
|
||||
rect: segmentRect
|
||||
)
|
||||
segments.append(segment)
|
||||
}
|
||||
return (segments, !updateRect.isNull ? updateRect : nil)
|
||||
}
|
||||
|
||||
private var segmentPaths: [Int: CGPath] = [:]
|
||||
private func pathForSegment(_ segment: Segment, abFactor: CGFloat = 1.0, cdFactor: CGFloat = 1.0) -> CGPath {
|
||||
var segment = segment
|
||||
if abFactor != 1.0 || cdFactor != 1.0 {
|
||||
segment = segment.withMultiplied(abFactor: abFactor, cdFactor: cdFactor)
|
||||
}
|
||||
|
||||
private func pathForSegment(_ segment: Segment) -> CGPath {
|
||||
let path = CGMutablePath()
|
||||
path.move(to: segment.b)
|
||||
|
||||
@ -713,6 +860,19 @@ final class PenTool: DrawingElement {
|
||||
return path
|
||||
}
|
||||
|
||||
func cachedPathForSegmentIndex(_ i: Int) -> CGPath {
|
||||
var segmentPath: CGPath
|
||||
if let current = self.segmentPaths[i] {
|
||||
segmentPath = current
|
||||
} else {
|
||||
let segment = self.segments[i]
|
||||
let path = self.pathForSegment(segment)
|
||||
self.segmentPaths[i] = path
|
||||
segmentPath = path
|
||||
}
|
||||
return segmentPath
|
||||
}
|
||||
|
||||
private func drawSegments(in context: CGContext, from: Int, to: Int) {
|
||||
context.setFillColor(self.renderColor.cgColor)
|
||||
|
||||
@ -733,13 +893,24 @@ final class PenTool: DrawingElement {
|
||||
}
|
||||
}
|
||||
|
||||
private func drawActiveSegments(in context: CGContext) {
|
||||
private func drawActiveSegments(in context: CGContext, strokeWidth: CGFloat?) {
|
||||
context.setFillColor(self.renderColor.cgColor)
|
||||
if let strokeWidth {
|
||||
context.setStrokeColor(self.renderColor.cgColor)
|
||||
context.setLineWidth(strokeWidth)
|
||||
}
|
||||
|
||||
var abFactor: CGFloat = activeWidthFactor
|
||||
let delta: CGFloat = (1.0 - activeWidthFactor) / CGFloat(self.activeSegments.count + 1)
|
||||
for segment in self.activeSegments {
|
||||
let path = self.pathForSegment(segment)
|
||||
context.addPath(path)
|
||||
if let _ = strokeWidth {
|
||||
context.drawPath(using: .fillStroke)
|
||||
} else {
|
||||
context.fillPath()
|
||||
}
|
||||
abFactor += delta
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -308,9 +308,11 @@ struct DrawingState: Equatable {
|
||||
|
||||
final class DrawingSettings: Codable, Equatable {
|
||||
let tools: [DrawingToolState]
|
||||
let colors: [DrawingColor]
|
||||
|
||||
init(tools: [DrawingToolState]) {
|
||||
init(tools: [DrawingToolState], colors: [DrawingColor]) {
|
||||
self.tools = tools
|
||||
self.colors = colors
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
@ -321,6 +323,12 @@ final class DrawingSettings: Codable, Equatable {
|
||||
} else {
|
||||
self.tools = DrawingState.initial.tools
|
||||
}
|
||||
|
||||
if let data = try container.decodeIfPresent(Data.self, forKey: "colors"), let colors = try? JSONDecoder().decode([DrawingColor].self, from: data) {
|
||||
self.colors = colors
|
||||
} else {
|
||||
self.colors = []
|
||||
}
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
@ -329,10 +337,13 @@ final class DrawingSettings: Codable, Equatable {
|
||||
if let data = try? JSONEncoder().encode(self.tools) {
|
||||
try container.encode(data, forKey: "tools")
|
||||
}
|
||||
if let data = try? JSONEncoder().encode(self.colors) {
|
||||
try container.encode(data, forKey: "colors")
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: DrawingSettings, rhs: DrawingSettings) -> Bool {
|
||||
return lhs.tools == rhs.tools
|
||||
return lhs.tools == rhs.tools && lhs.colors == rhs.colors
|
||||
}
|
||||
}
|
||||
|
||||
@ -734,7 +745,7 @@ private final class DrawingScreenComponent: CombinedComponent {
|
||||
let tools = self.drawingState.tools
|
||||
let _ = (self.context.sharedContext.accountManager.transaction { transaction -> Void in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.drawingSettings, { _ in
|
||||
return PreferencesEntry(DrawingSettings(tools: tools))
|
||||
return PreferencesEntry(DrawingSettings(tools: tools, colors: []))
|
||||
})
|
||||
}).start()
|
||||
}
|
||||
|
@ -390,9 +390,11 @@ final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate {
|
||||
self.textView.becomeFirstResponder()
|
||||
|
||||
UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 0.65, initialSpringVelocity: 0.0) {
|
||||
self.transform = .identity
|
||||
if let superview = self.superview {
|
||||
self.center = CGPoint(x: superview.bounds.width / 2.0, y: superview.bounds.height / 2.0)
|
||||
if let parentView = self.superview as? DrawingEntitiesView {
|
||||
let scale = parentView.getEntityAdditionalScale() / (parentView.drawingView?.zoomScale ?? 1.0)
|
||||
self.transform = CGAffineTransformMakeRotation(parentView.getEntityInitialRotation()).scaledBy(x: scale, y: scale)
|
||||
|
||||
self.center = parentView.getEntityCenterPosition()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -554,9 +554,15 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, UIPencilInt
|
||||
} else if case .blur = self.tool {
|
||||
self.currentDrawingViewContainer.mask = nil
|
||||
self.currentDrawingViewContainer.image = nil
|
||||
} else {
|
||||
if let renderView = currentDrawingRenderView as? PenTool.RenderView, renderView.isDryingUp {
|
||||
renderView.onDryingUp = { [weak renderView] in
|
||||
renderView?.removeFromSuperview()
|
||||
}
|
||||
} else {
|
||||
currentDrawingRenderView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
self.currentDrawingRenderView = nil
|
||||
}
|
||||
if let currentDrawingLayer = self.currentDrawingLayer {
|
||||
@ -651,10 +657,14 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, UIPencilInt
|
||||
|
||||
self.updateInternalState()
|
||||
}
|
||||
if let uncommitedElement = self.uncommitedElement as? PenTool, uncommitedElement.hasArrow {
|
||||
uncommitedElement.finishArrow({
|
||||
if let uncommitedElement = self.uncommitedElement as? PenTool {
|
||||
if uncommitedElement.hasArrow {
|
||||
uncommitedElement.finishArrow {
|
||||
complete(true)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
complete(true)
|
||||
}
|
||||
} else {
|
||||
complete(synchronous)
|
||||
}
|
||||
|
@ -231,9 +231,9 @@ final class TextFontComponent: Component {
|
||||
self.button.clipsToBounds = true
|
||||
self.button.setTitle(value.title, for: .normal)
|
||||
self.button.titleLabel?.font = value.uiFont(size: 13.0)
|
||||
self.button.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 26.0)
|
||||
self.button.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 20.0)
|
||||
var buttonSize = self.button.sizeThatFits(availableSize)
|
||||
buttonSize.width += 39.0 - 13.0
|
||||
buttonSize.width += 20.0
|
||||
buttonSize.height = 30.0
|
||||
transition.setFrame(view: self.button, frame: CGRect(origin: .zero, size: buttonSize))
|
||||
self.button.layer.cornerRadius = 11.0
|
||||
|
@ -53,6 +53,7 @@
|
||||
|
||||
@property (nonatomic, copy) CGPoint (^ _Nonnull getEntityCenterPosition)(void);
|
||||
@property (nonatomic, copy) CGFloat (^ _Nonnull getEntityInitialRotation)(void);
|
||||
@property (nonatomic, copy) CGFloat (^ _Nonnull getEntityAdditionalScale)(void);
|
||||
|
||||
@property (nonatomic, copy) void(^ _Nonnull hasSelectionChanged)(bool);
|
||||
@property (nonatomic, readonly) BOOL hasSelection;
|
||||
|
@ -246,6 +246,14 @@ const CGSize TGPhotoPaintingMaxSize = { 1920.0f, 1920.0f };
|
||||
return [strongSelf entityInitialRotation];
|
||||
};
|
||||
|
||||
_entitiesView.getEntityAdditionalScale = ^CGFloat {
|
||||
__strong TGPhotoDrawingController *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return 1.0f;
|
||||
|
||||
return strongSelf->_photoEditor.cropRect.size.width / strongSelf->_photoEditor.originalSize.width;
|
||||
};
|
||||
|
||||
[self.view setNeedsLayout];
|
||||
}
|
||||
|
||||
|
@ -625,6 +625,15 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
case .action, .optionalAction:
|
||||
break
|
||||
case let .openContextMenu(tapMessage, selectAll, subFrame):
|
||||
var tapMessage = tapMessage
|
||||
if selectAll, case let .group(messages) = item.content, tapMessage.text.isEmpty {
|
||||
for message in messages {
|
||||
if !message.0.text.isEmpty {
|
||||
tapMessage = message.0
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture, nil)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user