Make stroke thickness per-stroke; restore Clear button
Each drawn stroke now stores its own width and color, so changing the
thickness slider no longer retroactively resizes existing strokes. The
Clear button is back in the draw toolbar.
💘 Generated with Crush
Assisted-by: Crush:deepseek-v4-flash-0731
This commit is contained in:
@@ -91,9 +91,9 @@ final class CaptureModel: ObservableObject {
|
||||
/// Drag selection rect in IMAGE point space (nil = none)
|
||||
@Published var selection: CGRect?
|
||||
/// Freehand drawing strokes in IMAGE point space
|
||||
@Published var strokes: [[CGPoint]] = []
|
||||
@Published var strokes: [Stroke] = []
|
||||
/// Redo stack of removed strokes
|
||||
@Published var redoStack: [[CGPoint]] = []
|
||||
@Published var redoStack: [Stroke] = []
|
||||
/// Whether the pen is active (drag draws instead of selecting)
|
||||
@Published var isDrawing = false
|
||||
/// Current drawing color
|
||||
@@ -101,6 +101,12 @@ final class CaptureModel: ObservableObject {
|
||||
/// Stroke thickness in points (image space)
|
||||
@Published var strokeWidth: CGFloat = 3.0
|
||||
|
||||
struct Stroke {
|
||||
let points: [CGPoint]
|
||||
let width: CGFloat
|
||||
let color: NSColor
|
||||
}
|
||||
|
||||
enum CaptureMode {
|
||||
case screenshot
|
||||
case text
|
||||
@@ -143,15 +149,15 @@ final class CaptureModel: ObservableObject {
|
||||
let ctx = NSGraphicsContext.current!.cgContext
|
||||
// CGImage draws with a flipped Y in NSGraphicsContext
|
||||
ctx.draw(image, in: NSRect(origin: .zero, size: size))
|
||||
drawColor.setStroke()
|
||||
ctx.setLineWidth(strokeWidth)
|
||||
ctx.setLineCap(.round)
|
||||
ctx.setLineJoin(.round)
|
||||
for stroke in strokes {
|
||||
guard stroke.count > 1 else { continue }
|
||||
guard stroke.points.count > 1 else { continue }
|
||||
stroke.color.setStroke()
|
||||
ctx.setLineWidth(stroke.width)
|
||||
ctx.beginPath()
|
||||
ctx.move(to: stroke[0])
|
||||
for point in stroke.dropFirst() {
|
||||
ctx.move(to: stroke.points[0])
|
||||
for point in stroke.points.dropFirst() {
|
||||
ctx.addLine(to: point)
|
||||
}
|
||||
ctx.strokePath()
|
||||
@@ -162,7 +168,7 @@ final class CaptureModel: ObservableObject {
|
||||
|
||||
func addStroke(_ points: [CGPoint]) {
|
||||
guard points.count > 1 else { return }
|
||||
strokes.append(points)
|
||||
strokes.append(Stroke(points: points, width: strokeWidth, color: drawColor))
|
||||
redoStack = []
|
||||
}
|
||||
|
||||
@@ -387,6 +393,16 @@ struct ResultView: View {
|
||||
.disabled(!model.canRedo)
|
||||
.keyboardShortcut("z", modifiers: [.command, .shift])
|
||||
.help("Redo stroke (⌘⇧Z)")
|
||||
|
||||
Button {
|
||||
model.clearAnnotations()
|
||||
} label: {
|
||||
Label("Clear", systemImage: "eraser")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.controlSize(.small)
|
||||
.disabled(model.strokes.isEmpty)
|
||||
.help("Remove all drawn annotations")
|
||||
}
|
||||
.padding(.horizontal, 14)
|
||||
.padding(.vertical, 8)
|
||||
@@ -490,15 +506,15 @@ struct ScreenshotPane: View {
|
||||
|
||||
// Completed strokes
|
||||
Canvas { context, _ in
|
||||
var path = Path()
|
||||
for stroke in model.strokes {
|
||||
guard stroke.count > 1 else { continue }
|
||||
path.move(to: CGPoint(x: origin.x + stroke[0].x * scale, y: origin.y + stroke[0].y * scale))
|
||||
for point in stroke.dropFirst() {
|
||||
guard stroke.points.count > 1 else { continue }
|
||||
var path = Path()
|
||||
path.move(to: CGPoint(x: origin.x + stroke.points[0].x * scale, y: origin.y + stroke.points[0].y * scale))
|
||||
for point in stroke.points.dropFirst() {
|
||||
path.addLine(to: CGPoint(x: origin.x + point.x * scale, y: origin.y + point.y * scale))
|
||||
}
|
||||
context.stroke(path, with: .color(Color(nsColor: stroke.color)), lineWidth: stroke.width)
|
||||
}
|
||||
context.stroke(path, with: .color(Color(nsColor: model.drawColor)), lineWidth: model.strokeWidth)
|
||||
}
|
||||
.allowsHitTesting(false)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user