|
|
@@ -13,10 +13,13 @@ enum DrawPrintService {
|
|
|
|
|
|
// MARK: - Canvas Model
|
|
|
|
|
|
-enum CanvasTool: CaseIterable {
|
|
|
+enum CanvasTool {
|
|
|
+ case pointer
|
|
|
case text
|
|
|
case image
|
|
|
case draw
|
|
|
+
|
|
|
+ static let toolbarTools: [CanvasTool] = [.text, .image, .draw]
|
|
|
}
|
|
|
|
|
|
struct CanvasStroke {
|
|
|
@@ -264,6 +267,7 @@ final class DrawPrintOverlayView: NSView, AppearanceRefreshable {
|
|
|
guard panel.runModal() == .OK, let url = panel.url,
|
|
|
let image = NSImage(contentsOf: url) else { return }
|
|
|
canvasView.addImage(image, at: placement)
|
|
|
+ toolsPanel.setActiveTool(.pointer)
|
|
|
}
|
|
|
|
|
|
private func commitAddedText(_ text: String) {
|
|
|
@@ -276,6 +280,7 @@ final class DrawPrintOverlayView: NSView, AppearanceRefreshable {
|
|
|
canvasContainer.layoutSubtreeIfNeeded()
|
|
|
canvasView.layoutSubtreeIfNeeded()
|
|
|
canvasView.addTextItem(text: text, at: placement)
|
|
|
+ toolsPanel.setActiveTool(.pointer)
|
|
|
|
|
|
DispatchQueue.main.async { [weak self] in
|
|
|
self?.hideAddTextPrompt(animated: false)
|
|
|
@@ -434,6 +439,8 @@ final class BlankCanvasView: NSView, AppearanceRefreshable {
|
|
|
override func resetCursorRects() {
|
|
|
let cursor: NSCursor
|
|
|
switch activeTool {
|
|
|
+ case .pointer:
|
|
|
+ cursor = .arrow
|
|
|
case .draw:
|
|
|
cursor = .crosshair
|
|
|
case .text:
|
|
|
@@ -451,7 +458,8 @@ final class BlankCanvasView: NSView, AppearanceRefreshable {
|
|
|
return hit
|
|
|
}
|
|
|
}
|
|
|
- if activeTool == .text, editingTextID == nil, textIndex(at: point) != nil {
|
|
|
+ if (activeTool == .text || activeTool == .pointer),
|
|
|
+ editingTextID == nil, textIndex(at: point) != nil {
|
|
|
return self
|
|
|
}
|
|
|
return super.hitTest(point)
|
|
|
@@ -492,7 +500,8 @@ final class BlankCanvasView: NSView, AppearanceRefreshable {
|
|
|
item.image.draw(in: rect, from: .zero, operation: .sourceOver, fraction: 1)
|
|
|
}
|
|
|
|
|
|
- if activeTool == .text, let selectedID = selectedTextID,
|
|
|
+ if (activeTool == .text || activeTool == .pointer),
|
|
|
+ let selectedID = selectedTextID,
|
|
|
let item = textItems.first(where: { $0.id == selectedID }),
|
|
|
item.id != editingTextID {
|
|
|
drawTextSelection(for: item)
|
|
|
@@ -544,6 +553,34 @@ final class BlankCanvasView: NSView, AppearanceRefreshable {
|
|
|
}
|
|
|
|
|
|
switch activeTool {
|
|
|
+ case .pointer:
|
|
|
+ if let index = textIndex(at: point) {
|
|
|
+ let id = textItems[index].id
|
|
|
+ selectedTextID = id
|
|
|
+ selectedImageID = nil
|
|
|
+ if event.clickCount >= 2 {
|
|
|
+ beginEditingText(id: id)
|
|
|
+ } else {
|
|
|
+ draggingTextID = id
|
|
|
+ dragStartPoint = point
|
|
|
+ dragStartOrigin = textItems[index].origin
|
|
|
+ textDragSnapshotPushed = false
|
|
|
+ }
|
|
|
+ } else if let index = imageIndex(at: point) {
|
|
|
+ selectedImageID = imageItems[index].id
|
|
|
+ selectedTextID = nil
|
|
|
+ draggingImageID = imageItems[index].id
|
|
|
+ dragStartPoint = point
|
|
|
+ dragStartOrigin = imageItems[index].origin
|
|
|
+ imageDragSnapshotPushed = false
|
|
|
+ } else {
|
|
|
+ selectedTextID = nil
|
|
|
+ selectedImageID = nil
|
|
|
+ if editingTextID != nil {
|
|
|
+ endEditingText()
|
|
|
+ }
|
|
|
+ needsDisplay = true
|
|
|
+ }
|
|
|
case .draw:
|
|
|
pushSnapshot()
|
|
|
currentStroke = CanvasStroke(points: [point], color: drawColor, lineWidth: drawLineWidth)
|
|
|
@@ -581,7 +618,7 @@ final class BlankCanvasView: NSView, AppearanceRefreshable {
|
|
|
override func mouseDragged(with event: NSEvent) {
|
|
|
let point = convert(event.locationInWindow, from: nil)
|
|
|
|
|
|
- if activeTool == .text,
|
|
|
+ if (activeTool == .text || activeTool == .pointer),
|
|
|
let id = draggingTextID,
|
|
|
let index = textItems.firstIndex(where: { $0.id == id }) {
|
|
|
if !textDragSnapshotPushed {
|
|
|
@@ -599,7 +636,7 @@ final class BlankCanvasView: NSView, AppearanceRefreshable {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- if activeTool == .image,
|
|
|
+ if (activeTool == .image || activeTool == .pointer),
|
|
|
let id = draggingImageID,
|
|
|
let index = imageItems.firstIndex(where: { $0.id == id }) {
|
|
|
if !imageDragSnapshotPushed {
|
|
|
@@ -624,14 +661,14 @@ final class BlankCanvasView: NSView, AppearanceRefreshable {
|
|
|
}
|
|
|
|
|
|
override func mouseUp(with event: NSEvent) {
|
|
|
- if activeTool == .text, draggingTextID != nil {
|
|
|
+ if (activeTool == .text || activeTool == .pointer), draggingTextID != nil {
|
|
|
draggingTextID = nil
|
|
|
textDragSnapshotPushed = false
|
|
|
notifyUndoState()
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- if activeTool == .image, draggingImageID != nil {
|
|
|
+ if (activeTool == .image || activeTool == .pointer), draggingImageID != nil {
|
|
|
draggingImageID = nil
|
|
|
imageDragSnapshotPushed = false
|
|
|
notifyUndoState()
|
|
|
@@ -932,6 +969,10 @@ final class CanvasToolsPanel: NSView, AppearanceRefreshable {
|
|
|
@available(*, unavailable)
|
|
|
required init?(coder: NSCoder) { nil }
|
|
|
|
|
|
+ func setActiveTool(_ tool: CanvasTool) {
|
|
|
+ selectTool(tool)
|
|
|
+ }
|
|
|
+
|
|
|
func refreshAppearance() {
|
|
|
layer?.backgroundColor = AppTheme.cardBackground.cgColor
|
|
|
layer?.borderColor = AppTheme.paywallBorder.cgColor
|
|
|
@@ -955,7 +996,7 @@ final class CanvasToolsPanel: NSView, AppearanceRefreshable {
|
|
|
subToolbar.layer?.cornerRadius = 20
|
|
|
subToolbar.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
|
- for tool in CanvasTool.allCases {
|
|
|
+ for tool in CanvasTool.toolbarTools {
|
|
|
let button = CanvasToolButton(tool: tool)
|
|
|
button.onClick = { [weak self] in self?.selectTool(tool) }
|
|
|
toolButtons[tool] = button
|
|
|
@@ -983,7 +1024,11 @@ final class CanvasToolsPanel: NSView, AppearanceRefreshable {
|
|
|
|
|
|
private func selectTool(_ tool: CanvasTool) {
|
|
|
activeTool = tool
|
|
|
- toolButtons.forEach { $0.value.setSelected($0.key == tool) }
|
|
|
+ if tool == .pointer {
|
|
|
+ toolButtons.values.forEach { $0.setSelected(false) }
|
|
|
+ } else {
|
|
|
+ toolButtons.forEach { $0.value.setSelected($0.key == tool) }
|
|
|
+ }
|
|
|
onToolChanged?(tool)
|
|
|
rebuildSubToolbar()
|
|
|
}
|
|
|
@@ -997,6 +1042,8 @@ final class CanvasToolsPanel: NSView, AppearanceRefreshable {
|
|
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
|
switch activeTool {
|
|
|
+ case .pointer:
|
|
|
+ break
|
|
|
case .text:
|
|
|
stack.addArrangedSubview(makeSubButton(symbol: "text.alignleft", action: { [weak self] in
|
|
|
self?.onTextAlignment?(.left)
|
|
|
@@ -1035,7 +1082,7 @@ final class CanvasToolsPanel: NSView, AppearanceRefreshable {
|
|
|
stack.bottomAnchor.constraint(equalTo: subToolbar.bottomAnchor, constant: -6),
|
|
|
])
|
|
|
|
|
|
- subToolbar.isHidden = activeTool == .image
|
|
|
+ subToolbar.isHidden = activeTool == .image || activeTool == .pointer
|
|
|
|
|
|
let itemCount = max(stack.arrangedSubviews.count, 1)
|
|
|
subToolbarHeight?.constant = 40
|
|
|
@@ -1582,6 +1629,7 @@ private final class CanvasToolButton: NSControl, AppearanceRefreshable {
|
|
|
|
|
|
let symbol: String
|
|
|
switch tool {
|
|
|
+ case .pointer: symbol = "arrow.up.left"
|
|
|
case .text: symbol = "character.cursor.ibeam"
|
|
|
case .image: symbol = "photo.on.rectangle.angled"
|
|
|
case .draw: symbol = "paintbrush.pointed.fill"
|
|
|
@@ -1607,6 +1655,7 @@ private final class CanvasToolButton: NSControl, AppearanceRefreshable {
|
|
|
|
|
|
private var toolLabel: String {
|
|
|
switch tool {
|
|
|
+ case .pointer: return "Pointer"
|
|
|
case .text: return "Text"
|
|
|
case .image: return "Image"
|
|
|
case .draw: return "Draw"
|