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