second commit
This commit is contained in:
@@ -10,6 +10,7 @@ protocol EditorCanvasDelegate: AnyObject {
|
||||
func canvasDidChangeSelection(_ canvas: EditorCanvasView)
|
||||
func canvasDidChangeContent(_ canvas: EditorCanvasView)
|
||||
func canvasDidRequestToolChange(_ canvas: EditorCanvasView, to tool: ToolKind)
|
||||
func canvasDidPickColor(_ canvas: EditorCanvasView, color: NSColor)
|
||||
}
|
||||
|
||||
enum ResizeHandle: Int, CaseIterable {
|
||||
@@ -100,7 +101,6 @@ final class EditorCanvasView: NSView, NSTextViewDelegate {
|
||||
self.surface = surface
|
||||
super.init(frame: CGRect(origin: .zero, size: surface.imageSize))
|
||||
wantsLayer = true
|
||||
layer?.backgroundColor = NSColor.windowBackgroundColor.cgColor
|
||||
surface.onChange = { [weak self] in
|
||||
guard let self else { return }
|
||||
self.needsDisplay = true
|
||||
@@ -196,6 +196,7 @@ final class EditorCanvasView: NSView, NSTextViewDelegate {
|
||||
switch tool {
|
||||
case .select: cursor = .arrow
|
||||
case .text: cursor = .iBeam
|
||||
case .dropper: cursor = .crosshair
|
||||
default: cursor = .crosshair
|
||||
}
|
||||
addCursorRect(bounds, cursor: cursor)
|
||||
@@ -225,6 +226,12 @@ final class EditorCanvasView: NSView, NSTextViewDelegate {
|
||||
// The tool must stay on .text until editing finishes: switching tools calls
|
||||
// endTextEditing(), which would discard the element before anything is typed.
|
||||
beginTextEditing(element)
|
||||
case .dropper:
|
||||
// Sample the flattened image so annotation colours can be picked up too.
|
||||
if let color = ImageExporter.color(at: point, in: surface.render(), scale: surface.scale) {
|
||||
delegate?.canvasDidPickColor(self, color: color)
|
||||
}
|
||||
interaction = .none
|
||||
case .counter:
|
||||
let element = CounterElement(center: point, number: surface.nextCounterNumber, style: style)
|
||||
surface.nextCounterNumber += 1
|
||||
@@ -233,6 +240,7 @@ final class EditorCanvasView: NSView, NSTextViewDelegate {
|
||||
interaction = .none
|
||||
case .rectangle, .ellipse, .line, .arrow, .highlight, .obfuscate:
|
||||
let element = makeElement(for: tool, at: point)
|
||||
element.creationAnchor = point
|
||||
surface.add(element)
|
||||
surface.selection = element
|
||||
interaction = .creating(element)
|
||||
@@ -246,8 +254,8 @@ final class EditorCanvasView: NSView, NSTextViewDelegate {
|
||||
switch tool {
|
||||
case .rectangle: return RectangleElement(frame: frame, style: style)
|
||||
case .ellipse: return EllipseElement(frame: frame, style: style)
|
||||
case .line: return LineElement(frame: frame, style: style)
|
||||
case .arrow: return ArrowElement(frame: frame, style: style)
|
||||
case .line: return LineElement(start: point, end: point, style: style)
|
||||
case .arrow: return ArrowElement(start: point, end: point, style: style)
|
||||
case .highlight: return HighlightElement(frame: frame, style: style)
|
||||
case .obfuscate: return ObfuscateElement(frame: frame, style: style)
|
||||
default: return RectangleElement(frame: frame, style: style)
|
||||
@@ -287,6 +295,7 @@ final class EditorCanvasView: NSView, NSTextViewDelegate {
|
||||
surface.remove(existing)
|
||||
}
|
||||
let crop = CropElement(frame: CGRect(origin: point, size: .zero), style: style)
|
||||
crop.creationAnchor = point
|
||||
surface.add(crop)
|
||||
surface.selection = crop
|
||||
interaction = .creating(crop)
|
||||
@@ -298,20 +307,23 @@ final class EditorCanvasView: NSView, NSTextViewDelegate {
|
||||
|
||||
switch interaction {
|
||||
case .creating(let element):
|
||||
var target = point
|
||||
if constrain {
|
||||
if element is LineElement {
|
||||
target = constrainedLineEnd(from: element.frame.origin, to: point)
|
||||
} else {
|
||||
let side = max(abs(point.x - element.frame.origin.x), abs(point.y - element.frame.origin.y))
|
||||
target = CGPoint(x: element.frame.origin.x + side * (point.x < element.frame.origin.x ? -1 : 1),
|
||||
y: element.frame.origin.y + side * (point.y < element.frame.origin.y ? -1 : 1))
|
||||
if let line = element as? LineElement {
|
||||
let target = constrain ? constrainedLineEnd(from: line.start, to: point) : point
|
||||
line.setEnd(target)
|
||||
} else {
|
||||
let anchorPoint = element.creationAnchor ?? element.frame.origin
|
||||
var target = point
|
||||
if constrain {
|
||||
let side = max(abs(point.x - anchorPoint.x), abs(point.y - anchorPoint.y))
|
||||
target = CGPoint(x: anchorPoint.x + side * (point.x < anchorPoint.x ? -1 : 1),
|
||||
y: anchorPoint.y + side * (point.y < anchorPoint.y ? -1 : 1))
|
||||
}
|
||||
// Build the rect from the two corners so dragging up or left works.
|
||||
element.frame = CGRect(x: min(anchorPoint.x, target.x),
|
||||
y: min(anchorPoint.y, target.y),
|
||||
width: abs(target.x - anchorPoint.x),
|
||||
height: abs(target.y - anchorPoint.y))
|
||||
}
|
||||
element.frame = CGRect(x: element.frame.origin.x,
|
||||
y: element.frame.origin.y,
|
||||
width: target.x - element.frame.origin.x,
|
||||
height: target.y - element.frame.origin.y)
|
||||
surface.changed()
|
||||
case .moving(let element, let origin):
|
||||
element.move(by: CGVector(dx: point.x - origin.x, dy: point.y - origin.y))
|
||||
@@ -341,18 +353,16 @@ final class EditorCanvasView: NSView, NSTextViewDelegate {
|
||||
switch interaction {
|
||||
case .creating(let element):
|
||||
let rect = element.normalizedFrame
|
||||
let tooSmall = element is LineElement
|
||||
? hypot(element.frame.width, element.frame.height) < 4
|
||||
: (rect.width < 4 || rect.height < 4)
|
||||
let tooSmall = (element as? LineElement).map { $0.length < 4 }
|
||||
?? (rect.width < 4 || rect.height < 4)
|
||||
element.creationAnchor = nil
|
||||
if tooSmall {
|
||||
surface.remove(element)
|
||||
} else if !(element is LineElement) {
|
||||
element.frame = rect
|
||||
if element is CropElement { surface.cropElement = element as? CropElement }
|
||||
}
|
||||
if !(element is CropElement) {
|
||||
delegate?.canvasDidRequestToolChange(self, to: .select)
|
||||
}
|
||||
// The tool stays active so several shapes can be drawn in a row.
|
||||
case .moving, .resizing, .drawingFreehand, .none:
|
||||
break
|
||||
}
|
||||
@@ -448,9 +458,6 @@ final class EditorCanvasView: NSView, NSTextViewDelegate {
|
||||
surface.changed()
|
||||
needsDisplay = true
|
||||
|
||||
if tool == .text {
|
||||
delegate?.canvasDidRequestToolChange(self, to: .select)
|
||||
}
|
||||
}
|
||||
|
||||
func textDidEndEditing(_ notification: Notification) {
|
||||
|
||||
Reference in New Issue
Block a user