mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-03-23 18:01:14 +01:00
feat: add undo and redo logic for eraser stroke
This commit is contained in:
@@ -10,7 +10,6 @@ import MetalKit
|
||||
import CoreData
|
||||
import Foundation
|
||||
|
||||
#warning("TODO: to update history undo and redo logic")
|
||||
final class GraphicContext: @unchecked Sendable {
|
||||
var tree: RTree = RTree<AnyStroke>(maxEntries: 8)
|
||||
var eraserStrokes: Set<EraserStroke> = []
|
||||
@@ -46,26 +45,59 @@ final class GraphicContext: @unchecked Sendable {
|
||||
func undoGraphic(for event: HistoryEvent) {
|
||||
switch event {
|
||||
case .stroke(let stroke):
|
||||
guard let _stroke = stroke.stroke(as: PenStroke.self) else { return }
|
||||
let deletedStroke = tree.remove(_stroke.anyStroke, in: _stroke.strokeBox)
|
||||
withPersistence(\.backgroundContext) { [stroke = deletedStroke] context in
|
||||
stroke?.stroke(as: PenStroke.self)?.object?.graphicContext = nil
|
||||
try context.saveIfNeeded()
|
||||
switch stroke.style {
|
||||
case .marker:
|
||||
guard let penStroke = stroke.stroke(as: PenStroke.self) else { return }
|
||||
let deletedStroke = tree.remove(penStroke.anyStroke, in: penStroke.strokeBox)
|
||||
withPersistence(\.backgroundContext) { [stroke = deletedStroke] context in
|
||||
stroke?.stroke(as: PenStroke.self)?.object?.graphicContext = nil
|
||||
try context.saveIfNeeded()
|
||||
}
|
||||
case .eraser:
|
||||
guard let eraserStroke = stroke.stroke(as: EraserStroke.self) else { return }
|
||||
eraserStrokes.remove(eraserStroke)
|
||||
let penStrokes = eraserStroke.penStrokes
|
||||
withPersistence(\.backgroundContext) { [penStrokes] context in
|
||||
for penStroke in penStrokes {
|
||||
penStroke.eraserStrokes.remove(eraserStroke)
|
||||
if let object = eraserStroke.object {
|
||||
penStroke.object?.erasers.remove(object)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
previousStroke = nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func redoGraphic(for event: HistoryEvent) {
|
||||
switch event {
|
||||
case .stroke(let stroke):
|
||||
if let stroke = stroke.stroke(as: PenStroke.self) {
|
||||
tree.insert(stroke.anyStroke, in: stroke.strokeBox)
|
||||
}
|
||||
withPersistence(\.backgroundContext) { [weak self, stroke] context in
|
||||
stroke.stroke(as: PenStroke.self)?.object?.graphicContext = self?.object
|
||||
try context.saveIfNeeded()
|
||||
switch stroke.style {
|
||||
case .marker:
|
||||
guard let penStroke = stroke.stroke(as: PenStroke.self) else {
|
||||
break
|
||||
}
|
||||
tree.insert(penStroke.anyStroke, in: penStroke.strokeBox)
|
||||
withPersistence(\.backgroundContext) { [weak self, penStroke] context in
|
||||
penStroke.object?.graphicContext = self?.object
|
||||
try context.saveIfNeeded()
|
||||
}
|
||||
case .eraser:
|
||||
guard let eraserStroke = stroke.stroke(as: EraserStroke.self) else {
|
||||
break
|
||||
}
|
||||
eraserStrokes.insert(eraserStroke)
|
||||
let penStrokes = eraserStroke.penStrokes
|
||||
withPersistence(\.backgroundContext) { [eraserStroke, penStrokes] context in
|
||||
for penStroke in penStrokes {
|
||||
penStroke.eraserStrokes.insert(eraserStroke)
|
||||
if let object = eraserStroke.object {
|
||||
penStroke.object?.erasers.add(object)
|
||||
}
|
||||
}
|
||||
try context.saveIfNeeded()
|
||||
}
|
||||
}
|
||||
previousStroke = nil
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ final class EraserStroke: Stroke, @unchecked Sendable {
|
||||
weak var graphicContext: GraphicContext?
|
||||
|
||||
var finishesSaving: Bool = false
|
||||
var penStrokes: Set<PenStroke> = []
|
||||
|
||||
init(
|
||||
bounds: [CGFloat],
|
||||
@@ -58,7 +59,7 @@ final class EraserStroke: Stroke, @unchecked Sendable {
|
||||
bounds: object.bounds,
|
||||
color: object.color,
|
||||
style: style,
|
||||
createdAt: object.createdAt,
|
||||
createdAt: object.createdAt ?? .now,
|
||||
thickness: object.thickness
|
||||
)
|
||||
self.object = object
|
||||
@@ -111,6 +112,7 @@ final class EraserStroke: Stroke, @unchecked Sendable {
|
||||
for stroke in graphicContext.tree.search(box: _quad.quadBox) {
|
||||
if let _penStroke = stroke.stroke(as: PenStroke.self), !_penStroke.eraserStrokes.contains(self) {
|
||||
_penStroke.eraserStrokes.insert(self)
|
||||
penStrokes.insert(_penStroke)
|
||||
if let penStroke = _penStroke.object {
|
||||
penStroke.erasers.add(eraser)
|
||||
eraser.strokes.add(penStroke)
|
||||
|
||||
@@ -61,11 +61,12 @@ final class PenStroke: Stroke, @unchecked Sendable {
|
||||
|
||||
convenience init(object: StrokeObject) {
|
||||
let style = StrokeStyle(rawValue: object.style) ?? .marker
|
||||
#warning("TODO: revisit here and check if there is any crash")
|
||||
self.init(
|
||||
bounds: object.bounds,
|
||||
color: object.color,
|
||||
style: style,
|
||||
createdAt: object.createdAt, // sometimes crash here
|
||||
createdAt: object.createdAt ?? .now, // sometimes crash here
|
||||
thickness: object.thickness
|
||||
)
|
||||
self.object = object
|
||||
|
||||
@@ -52,11 +52,23 @@ class History: ObservableObject {
|
||||
for event in redoStack {
|
||||
switch event {
|
||||
case .stroke(let _stroke):
|
||||
withPersistence(\.backgroundContext) { context in
|
||||
if let stroke = _stroke.stroke(as: PenStroke.self)?.object {
|
||||
context.delete(stroke)
|
||||
switch _stroke.style {
|
||||
case .marker:
|
||||
guard let penStroke = _stroke.stroke(as: PenStroke.self) else { return }
|
||||
withPersistence(\.backgroundContext) { context in
|
||||
if let stroke = penStroke.object {
|
||||
context.delete(stroke)
|
||||
}
|
||||
try context.saveIfNeeded()
|
||||
}
|
||||
case .eraser:
|
||||
guard let eraserStroke = _stroke.stroke(as: EraserStroke.self) else { return }
|
||||
withPersistence(\.backgroundContext) { context in
|
||||
if let stroke = eraserStroke.object {
|
||||
context.delete(stroke)
|
||||
}
|
||||
try context.saveIfNeeded()
|
||||
}
|
||||
try context.saveIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ final class EraserObject: NSManagedObject {
|
||||
@NSManaged var bounds: [CGFloat]
|
||||
@NSManaged var color: [CGFloat]
|
||||
@NSManaged var style: Int16
|
||||
@NSManaged var createdAt: Date
|
||||
@NSManaged var createdAt: Date?
|
||||
@NSManaged var thickness: CGFloat
|
||||
@NSManaged var quads: NSMutableOrderedSet
|
||||
@NSManaged var strokes: NSMutableSet
|
||||
|
||||
@@ -13,7 +13,7 @@ final class StrokeObject: NSManagedObject {
|
||||
@NSManaged var bounds: [CGFloat]
|
||||
@NSManaged var color: [CGFloat]
|
||||
@NSManaged var style: Int16
|
||||
@NSManaged var createdAt: Date
|
||||
@NSManaged var createdAt: Date?
|
||||
@NSManaged var thickness: CGFloat
|
||||
@NSManaged var quads: NSMutableOrderedSet
|
||||
@NSManaged var erasers: NSMutableSet
|
||||
|
||||
Reference in New Issue
Block a user