mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-07-08 05:45:17 +02:00
feat: add pen tool persistence
This commit is contained in:
@@ -7,6 +7,9 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
EC0D14212BF79C73009BFE5F /* ToolObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14202BF79C73009BFE5F /* ToolObject.swift */; };
|
||||||
|
EC0D14242BF79C98009BFE5F /* MemolaModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14222BF79C98009BFE5F /* MemolaModel.xcdatamodeld */; };
|
||||||
|
EC0D14262BF7A8C9009BFE5F /* PenObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14252BF7A8C9009BFE5F /* PenObject.swift */; };
|
||||||
EC3565522BEFC65F00A4E0BF /* NSManagedObjectContext++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3565512BEFC65F00A4E0BF /* NSManagedObjectContext++.swift */; };
|
EC3565522BEFC65F00A4E0BF /* NSManagedObjectContext++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3565512BEFC65F00A4E0BF /* NSManagedObjectContext++.swift */; };
|
||||||
EC3565542BEFC6AD00A4E0BF /* View++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3565532BEFC6AD00A4E0BF /* View++.swift */; };
|
EC3565542BEFC6AD00A4E0BF /* View++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3565532BEFC6AD00A4E0BF /* View++.swift */; };
|
||||||
EC3565562BEFC7B300A4E0BF /* NSManagedObject++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3565552BEFC7B300A4E0BF /* NSManagedObject++.swift */; };
|
EC3565562BEFC7B300A4E0BF /* NSManagedObject++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3565552BEFC7B300A4E0BF /* NSManagedObject++.swift */; };
|
||||||
@@ -70,7 +73,6 @@
|
|||||||
ECA738F42BE612A000A4542E /* Array++.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA738F32BE612A000A4542E /* Array++.swift */; };
|
ECA738F42BE612A000A4542E /* Array++.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA738F32BE612A000A4542E /* Array++.swift */; };
|
||||||
ECA738F62BE612B700A4542E /* MTLDevice++.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA738F52BE612B700A4542E /* MTLDevice++.swift */; };
|
ECA738F62BE612B700A4542E /* MTLDevice++.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA738F52BE612B700A4542E /* MTLDevice++.swift */; };
|
||||||
ECA738FC2BE61C5200A4542E /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA738FB2BE61C5200A4542E /* Persistence.swift */; };
|
ECA738FC2BE61C5200A4542E /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA738FB2BE61C5200A4542E /* Persistence.swift */; };
|
||||||
ECA739012BE61D9C00A4542E /* MemolaModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = ECA738FF2BE61D9C00A4542E /* MemolaModel.xcdatamodeld */; };
|
|
||||||
ECA739082BE623F300A4542E /* PenToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA739072BE623F300A4542E /* PenToolView.swift */; };
|
ECA739082BE623F300A4542E /* PenToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA739072BE623F300A4542E /* PenToolView.swift */; };
|
||||||
ECEC01A82BEE11BA006DA24C /* QuadShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECEC01A72BEE11BA006DA24C /* QuadShape.swift */; };
|
ECEC01A82BEE11BA006DA24C /* QuadShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECEC01A72BEE11BA006DA24C /* QuadShape.swift */; };
|
||||||
ECFA15202BEF21EF00455818 /* MemoObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA151F2BEF21EF00455818 /* MemoObject.swift */; };
|
ECFA15202BEF21EF00455818 /* MemoObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA151F2BEF21EF00455818 /* MemoObject.swift */; };
|
||||||
@@ -81,6 +83,9 @@
|
|||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
EC0D14202BF79C73009BFE5F /* ToolObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolObject.swift; sourceTree = "<group>"; };
|
||||||
|
EC0D14232BF79C98009BFE5F /* MemolaModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MemolaModel.xcdatamodel; sourceTree = "<group>"; };
|
||||||
|
EC0D14252BF7A8C9009BFE5F /* PenObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenObject.swift; sourceTree = "<group>"; };
|
||||||
EC3565512BEFC65F00A4E0BF /* NSManagedObjectContext++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext++.swift"; sourceTree = "<group>"; };
|
EC3565512BEFC65F00A4E0BF /* NSManagedObjectContext++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext++.swift"; sourceTree = "<group>"; };
|
||||||
EC3565532BEFC6AD00A4E0BF /* View++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View++.swift"; sourceTree = "<group>"; };
|
EC3565532BEFC6AD00A4E0BF /* View++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View++.swift"; sourceTree = "<group>"; };
|
||||||
EC3565552BEFC7B300A4E0BF /* NSManagedObject++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObject++.swift"; sourceTree = "<group>"; };
|
EC3565552BEFC7B300A4E0BF /* NSManagedObject++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObject++.swift"; sourceTree = "<group>"; };
|
||||||
@@ -146,7 +151,6 @@
|
|||||||
ECA738F32BE612A000A4542E /* Array++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array++.swift"; sourceTree = "<group>"; };
|
ECA738F32BE612A000A4542E /* Array++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array++.swift"; sourceTree = "<group>"; };
|
||||||
ECA738F52BE612B700A4542E /* MTLDevice++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MTLDevice++.swift"; sourceTree = "<group>"; };
|
ECA738F52BE612B700A4542E /* MTLDevice++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MTLDevice++.swift"; sourceTree = "<group>"; };
|
||||||
ECA738FB2BE61C5200A4542E /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
|
ECA738FB2BE61C5200A4542E /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
|
||||||
ECA739002BE61D9C00A4542E /* MemolaModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MemolaModel.xcdatamodel; sourceTree = "<group>"; };
|
|
||||||
ECA739072BE623F300A4542E /* PenToolView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenToolView.swift; sourceTree = "<group>"; };
|
ECA739072BE623F300A4542E /* PenToolView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenToolView.swift; sourceTree = "<group>"; };
|
||||||
ECEC01A72BEE11BA006DA24C /* QuadShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuadShape.swift; sourceTree = "<group>"; };
|
ECEC01A72BEE11BA006DA24C /* QuadShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuadShape.swift; sourceTree = "<group>"; };
|
||||||
ECFA151F2BEF21EF00455818 /* MemoObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoObject.swift; sourceTree = "<group>"; };
|
ECFA151F2BEF21EF00455818 /* MemoObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoObject.swift; sourceTree = "<group>"; };
|
||||||
@@ -556,7 +560,7 @@
|
|||||||
ECA738FE2BE61D5700A4542E /* Models */ = {
|
ECA738FE2BE61D5700A4542E /* Models */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
ECA738FF2BE61D9C00A4542E /* MemolaModel.xcdatamodeld */,
|
EC0D14222BF79C98009BFE5F /* MemolaModel.xcdatamodeld */,
|
||||||
);
|
);
|
||||||
path = Models;
|
path = Models;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -586,6 +590,8 @@
|
|||||||
ECFA15232BEF223300455818 /* GraphicContextObject.swift */,
|
ECFA15232BEF223300455818 /* GraphicContextObject.swift */,
|
||||||
ECFA15252BEF224900455818 /* StrokeObject.swift */,
|
ECFA15252BEF224900455818 /* StrokeObject.swift */,
|
||||||
ECFA15272BEF225000455818 /* QuadObject.swift */,
|
ECFA15272BEF225000455818 /* QuadObject.swift */,
|
||||||
|
EC0D14202BF79C73009BFE5F /* ToolObject.swift */,
|
||||||
|
EC0D14252BF7A8C9009BFE5F /* PenObject.swift */,
|
||||||
);
|
);
|
||||||
path = Objects;
|
path = Objects;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -696,8 +702,9 @@
|
|||||||
ECA738EE2BE6125D00A4542E /* simd_float4x4++.swift in Sources */,
|
ECA738EE2BE6125D00A4542E /* simd_float4x4++.swift in Sources */,
|
||||||
ECA7388C2BE6009600A4542E /* Textures.swift in Sources */,
|
ECA7388C2BE6009600A4542E /* Textures.swift in Sources */,
|
||||||
ECA738B82BE60DDC00A4542E /* HistoryEvent.swift in Sources */,
|
ECA738B82BE60DDC00A4542E /* HistoryEvent.swift in Sources */,
|
||||||
|
EC0D14262BF7A8C9009BFE5F /* PenObject.swift in Sources */,
|
||||||
ECA738952BE6012D00A4542E /* ViewPort.metal in Sources */,
|
ECA738952BE6012D00A4542E /* ViewPort.metal in Sources */,
|
||||||
ECA739012BE61D9C00A4542E /* MemolaModel.xcdatamodeld in Sources */,
|
EC0D14242BF79C98009BFE5F /* MemolaModel.xcdatamodeld in Sources */,
|
||||||
ECA738F02BE6127700A4542E /* CGSize++.swift in Sources */,
|
ECA738F02BE6127700A4542E /* CGSize++.swift in Sources */,
|
||||||
ECFA15242BEF223300455818 /* GraphicContextObject.swift in Sources */,
|
ECFA15242BEF223300455818 /* GraphicContextObject.swift in Sources */,
|
||||||
EC3565562BEFC7B300A4E0BF /* NSManagedObject++.swift in Sources */,
|
EC3565562BEFC7B300A4E0BF /* NSManagedObject++.swift in Sources */,
|
||||||
@@ -710,6 +717,7 @@
|
|||||||
ECA7388F2BE600DA00A4542E /* Grid.metal in Sources */,
|
ECA7388F2BE600DA00A4542E /* Grid.metal in Sources */,
|
||||||
ECA738C92BE60EF700A4542E /* GraphicContext.swift in Sources */,
|
ECA738C92BE60EF700A4542E /* GraphicContext.swift in Sources */,
|
||||||
ECA738F62BE612B700A4542E /* MTLDevice++.swift in Sources */,
|
ECA738F62BE612B700A4542E /* MTLDevice++.swift in Sources */,
|
||||||
|
EC0D14212BF79C73009BFE5F /* ToolObject.swift in Sources */,
|
||||||
EC50500D2BF6674400B4D86E /* DraggableViewModifier.swift in Sources */,
|
EC50500D2BF6674400B4D86E /* DraggableViewModifier.swift in Sources */,
|
||||||
ECA7389E2BE601CB00A4542E /* QuadVertex.swift in Sources */,
|
ECA7389E2BE601CB00A4542E /* QuadVertex.swift in Sources */,
|
||||||
ECA738B32BE60D9E00A4542E /* CanvasView.swift in Sources */,
|
ECA738B32BE60D9E00A4542E /* CanvasView.swift in Sources */,
|
||||||
@@ -944,13 +952,14 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCVersionGroup section */
|
/* Begin XCVersionGroup section */
|
||||||
ECA738FF2BE61D9C00A4542E /* MemolaModel.xcdatamodeld */ = {
|
EC0D14222BF79C98009BFE5F /* MemolaModel.xcdatamodeld */ = {
|
||||||
isa = XCVersionGroup;
|
isa = XCVersionGroup;
|
||||||
children = (
|
children = (
|
||||||
ECA739002BE61D9C00A4542E /* MemolaModel.xcdatamodel */,
|
EC0D14232BF79C98009BFE5F /* MemolaModel.xcdatamodel */,
|
||||||
);
|
);
|
||||||
currentVersion = ECA739002BE61D9C00A4542E /* MemolaModel.xcdatamodel */;
|
currentVersion = EC0D14232BF79C98009BFE5F /* MemolaModel.xcdatamodel */;
|
||||||
path = MemolaModel.xcdatamodeld;
|
name = MemolaModel.xcdatamodeld;
|
||||||
|
path = /Users/dscyrescotti/Documents/Projects/Memola/Memola/Resources/Models/MemolaModel.xcdatamodeld;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
versionGroupType = wrapper.xcdatamodel;
|
versionGroupType = wrapper.xcdatamodel;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,27 +6,54 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import CoreData
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class Tool: NSObject, ObservableObject {
|
public class Tool: NSObject, ObservableObject {
|
||||||
@Published var pens: [Pen]
|
let object: ToolObject
|
||||||
|
|
||||||
|
@Published var pens: [Pen] = []
|
||||||
@Published var selectedPen: Pen?
|
@Published var selectedPen: Pen?
|
||||||
@Published var draggedPen: Pen?
|
@Published var draggedPen: Pen?
|
||||||
|
|
||||||
override init() {
|
init(object: ToolObject) {
|
||||||
pens = [
|
self.object = object
|
||||||
Pen(for: .eraser),
|
|
||||||
Pen(for: .marker)
|
|
||||||
]
|
|
||||||
super.init()
|
|
||||||
selectedPen = pens[1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func changePen(_ pen: Pen) {
|
func load() {
|
||||||
selectedPen = pen
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
guard let self else { return }
|
||||||
|
pens = object.pens.sortedArray(using: [NSSortDescriptor(key: "orderIndex", ascending: true)]).compactMap {
|
||||||
|
guard let pen = $0 as? PenObject else { return nil }
|
||||||
|
return Pen(object: pen)
|
||||||
|
}
|
||||||
|
if let selectedPen = pens.first(where: { $0.isSelected }) {
|
||||||
|
selectPen(selectedPen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectPen(_ pen: Pen) {
|
||||||
|
if let selectedPen {
|
||||||
|
unselectPen(selectedPen)
|
||||||
|
}
|
||||||
|
withAnimation {
|
||||||
|
selectedPen = pen
|
||||||
|
}
|
||||||
|
selectedPen?.isSelected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func unselectPen(_ pen: Pen) {
|
||||||
|
pen.isSelected = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func addPen(_ pen: Pen) {
|
func addPen(_ pen: Pen) {
|
||||||
pens.append(pen)
|
withAnimation {
|
||||||
|
pens.append(pen)
|
||||||
|
}
|
||||||
|
selectPen(pen)
|
||||||
|
if let _pen = pen.object {
|
||||||
|
object.pens.add(_pen)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,32 +10,41 @@ import Foundation
|
|||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
class Pen: NSObject, ObservableObject, Identifiable {
|
class Pen: NSObject, ObservableObject, Identifiable {
|
||||||
let id: String
|
var object: PenObject?
|
||||||
@Published var style: any PenStyle
|
|
||||||
@Published var color: [CGFloat]
|
|
||||||
@Published var thickness: CGFloat
|
|
||||||
|
|
||||||
init(style: any PenStyle, color: [CGFloat], thickness: CGFloat) {
|
let id: String
|
||||||
self.id = UUID().uuidString
|
@Published var style: any PenStyle {
|
||||||
self.style = style
|
didSet {
|
||||||
self.color = color
|
object?.style = strokeStyle.rawValue
|
||||||
self.thickness = thickness
|
}
|
||||||
|
}
|
||||||
|
@Published var color: [CGFloat] {
|
||||||
|
didSet {
|
||||||
|
object?.color = color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Published var thickness: CGFloat {
|
||||||
|
didSet {
|
||||||
|
object?.thickness = thickness
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Published var isSelected: Bool {
|
||||||
|
didSet {
|
||||||
|
object?.isSelected = isSelected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(object: PenObject) {
|
||||||
|
self.object = object
|
||||||
|
self.id = object.objectID.uriRepresentation().absoluteString
|
||||||
|
self.style = (Stroke.Style(rawValue: object.style) ?? .marker).anyPenStyle
|
||||||
|
self.color = object.color
|
||||||
|
self.thickness = object.thickness
|
||||||
|
self.isSelected = object.isSelected
|
||||||
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
var strokeStyle: Stroke.Style {
|
var strokeStyle: Stroke.Style {
|
||||||
switch style {
|
style.strokeStyle
|
||||||
case is MarkerPenStyle:
|
|
||||||
return .marker
|
|
||||||
case is EraserPenStyle:
|
|
||||||
return .eraser
|
|
||||||
default:
|
|
||||||
return .marker
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Pen {
|
|
||||||
convenience init(for style: any PenStyle) {
|
|
||||||
self.init(style: style, color: style.color, thickness: style.thinkness.min)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,4 +22,15 @@ extension PenStyle {
|
|||||||
func loadTexture(on device: MTLDevice) -> MTLTexture? {
|
func loadTexture(on device: MTLDevice) -> MTLTexture? {
|
||||||
Textures.createPenTexture(with: textureName, on: device)
|
Textures.createPenTexture(with: textureName, on: device)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var strokeStyle: Stroke.Style {
|
||||||
|
switch self {
|
||||||
|
case is MarkerPenStyle:
|
||||||
|
return .marker
|
||||||
|
case is EraserPenStyle:
|
||||||
|
return .eraser
|
||||||
|
default:
|
||||||
|
return .marker
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ extension CanvasViewController {
|
|||||||
|
|
||||||
extension CanvasViewController {
|
extension CanvasViewController {
|
||||||
func loadMemo() {
|
func loadMemo() {
|
||||||
|
tool.load()
|
||||||
canvas.load()
|
canvas.load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ import CoreData
|
|||||||
|
|
||||||
struct MemoView: View {
|
struct MemoView: View {
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
@Environment(\.managedObjectContext) var managedObjectContext
|
|
||||||
|
|
||||||
@StateObject var tool = Tool()
|
@StateObject var tool: Tool
|
||||||
@StateObject var canvas: Canvas
|
@StateObject var canvas: Canvas
|
||||||
@StateObject var history = History()
|
@StateObject var history = History()
|
||||||
|
|
||||||
@@ -20,6 +19,7 @@ struct MemoView: View {
|
|||||||
|
|
||||||
init(memo: MemoObject) {
|
init(memo: MemoObject) {
|
||||||
self.memo = memo
|
self.memo = memo
|
||||||
|
self._tool = StateObject(wrappedValue: Tool(object: memo.tool))
|
||||||
self._canvas = StateObject(wrappedValue: Canvas(size: memo.canvas.size, canvasID: memo.canvas.objectID))
|
self._canvas = StateObject(wrappedValue: Canvas(size: memo.canvas.size, canvasID: memo.canvas.objectID))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,13 +97,8 @@ struct MemoView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func closeMemo() {
|
func closeMemo() {
|
||||||
history.resetRedo()
|
withPersistenceSync(\.viewContext) { context in
|
||||||
if managedObjectContext.hasChanges {
|
try context.saveIfNeeded()
|
||||||
do {
|
|
||||||
try managedObjectContext.save()
|
|
||||||
} catch {
|
|
||||||
NSLog("[Memola] - \(error.localizedDescription)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ struct PenDropDelegate: DropDelegate {
|
|||||||
tool.pens.move(fromOffsets: IndexSet(integer: fromIndex), toOffset: toIndex > fromIndex ? toIndex + 1 : toIndex)
|
tool.pens.move(fromOffsets: IndexSet(integer: fromIndex), toOffset: toIndex > fromIndex ? toIndex + 1 : toIndex)
|
||||||
tool.objectWillChange.send()
|
tool.objectWillChange.send()
|
||||||
}
|
}
|
||||||
|
withPersistence(\.viewContext) { context in
|
||||||
|
for (index, pen) in tool.pens.enumerated() {
|
||||||
|
pen.object?.orderIndex = Int16(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,11 +82,11 @@ struct PenToolView: View {
|
|||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if tool.selectedPen === pen {
|
if tool.selectedPen === pen {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
tool.selectedPen = nil
|
tool.unselectPen(pen)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
tool.changePen(pen)
|
tool.selectPen(pen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,9 +95,13 @@ struct PenToolView: View {
|
|||||||
|
|
||||||
var newPenButton: some View {
|
var newPenButton: some View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
let pen = Pen(for: .marker)
|
let pen = PenObject.createObject(\.viewContext, penStyle: .marker)
|
||||||
pen.color = [Color.red, Color.blue, Color.green, Color.black, Color.orange].randomElement()!.components
|
pen.color = [Color.red, Color.blue, Color.green, Color.black, Color.orange].randomElement()!.components
|
||||||
tool.addPen(pen)
|
pen.isSelected = true
|
||||||
|
pen.tool = tool.object
|
||||||
|
pen.orderIndex = Int16(tool.pens.count)
|
||||||
|
let _pen = Pen(object: pen)
|
||||||
|
tool.addPen(_pen)
|
||||||
}) {
|
}) {
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
.font(.title3)
|
.font(.title3)
|
||||||
|
|||||||
@@ -62,28 +62,45 @@ struct MemosView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createMemo(title: String) {
|
func createMemo(title: String) {
|
||||||
do {
|
let memoObject = MemoObject(\.viewContext)
|
||||||
let memoObject = MemoObject(context: managedObjectContext)
|
memoObject.title = title
|
||||||
memoObject.title = title
|
memoObject.createdAt = .now
|
||||||
memoObject.createdAt = .now
|
memoObject.updatedAt = .now
|
||||||
memoObject.updatedAt = .now
|
|
||||||
|
|
||||||
let canvasObject = CanvasObject(context: managedObjectContext)
|
let canvasObject = CanvasObject(context: managedObjectContext)
|
||||||
canvasObject.width = 8_000
|
canvasObject.width = 8_000
|
||||||
canvasObject.height = 8_000
|
canvasObject.height = 8_000
|
||||||
|
|
||||||
let graphicContextObject = GraphicContextObject(context: managedObjectContext)
|
let toolObject = ToolObject(\.viewContext)
|
||||||
graphicContextObject.strokes = []
|
toolObject.pens = []
|
||||||
|
|
||||||
memoObject.canvas = canvasObject
|
let eraserPenObject = PenObject.createObject(\.viewContext, penStyle: .eraser)
|
||||||
canvasObject.memo = memoObject
|
eraserPenObject.orderIndex = 0
|
||||||
canvasObject.graphicContext = graphicContextObject
|
let markerPenObject = PenObject.createObject(\.viewContext, penStyle: .marker)
|
||||||
graphicContextObject.canvas = canvasObject
|
markerPenObject.orderIndex = 1
|
||||||
|
|
||||||
try managedObjectContext.save()
|
let graphicContextObject = GraphicContextObject(\.viewContext)
|
||||||
openMemo(for: memoObject)
|
graphicContextObject.strokes = []
|
||||||
} catch {
|
|
||||||
NSLog("[Memola] - \(error.localizedDescription)")
|
memoObject.canvas = canvasObject
|
||||||
|
memoObject.tool = toolObject
|
||||||
|
|
||||||
|
canvasObject.memo = memoObject
|
||||||
|
canvasObject.graphicContext = graphicContextObject
|
||||||
|
|
||||||
|
toolObject.memo = memoObject
|
||||||
|
toolObject.pens = [eraserPenObject, markerPenObject]
|
||||||
|
|
||||||
|
eraserPenObject.tool = toolObject
|
||||||
|
markerPenObject.tool = toolObject
|
||||||
|
|
||||||
|
graphicContextObject.canvas = canvasObject
|
||||||
|
|
||||||
|
withPersistenceSync(\.viewContext) { context in
|
||||||
|
try context.save()
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
openMemo(for: memoObject)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import Foundation
|
|||||||
|
|
||||||
@objc(MemoObject)
|
@objc(MemoObject)
|
||||||
final class MemoObject: NSManagedObject, Identifiable {
|
final class MemoObject: NSManagedObject, Identifiable {
|
||||||
@NSManaged var title: String
|
|
||||||
@NSManaged var data: Data
|
@NSManaged var data: Data
|
||||||
|
@NSManaged var title: String
|
||||||
@NSManaged var createdAt: Date
|
@NSManaged var createdAt: Date
|
||||||
@NSManaged var updatedAt: Date
|
@NSManaged var updatedAt: Date
|
||||||
|
@NSManaged var tool: ToolObject
|
||||||
@NSManaged var canvas: CanvasObject
|
@NSManaged var canvas: CanvasObject
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// PenObject.swift
|
||||||
|
// Memola
|
||||||
|
//
|
||||||
|
// Created by Dscyre Scotti on 5/17/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@objc(PenObject)
|
||||||
|
class PenObject: NSManagedObject {
|
||||||
|
@NSManaged var color: [CGFloat]
|
||||||
|
@NSManaged var style: Int16
|
||||||
|
@NSManaged var thickness: CGFloat
|
||||||
|
@NSManaged var isSelected: Bool
|
||||||
|
@NSManaged var orderIndex: Int16
|
||||||
|
@NSManaged var tool: ToolObject?
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PenObject {
|
||||||
|
static func createObject(_ keyPath: KeyPath<Persistence, NSManagedObjectContext>, penStyle: any PenStyle) -> PenObject {
|
||||||
|
let object = PenObject(context: Persistence.shared[keyPath: keyPath])
|
||||||
|
object.color = penStyle.color
|
||||||
|
object.style = penStyle.strokeStyle.rawValue
|
||||||
|
object.isSelected = false
|
||||||
|
object.thickness = penStyle.thinkness.min
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// ToolObject.swift
|
||||||
|
// Memola
|
||||||
|
//
|
||||||
|
// Created by Dscyre Scotti on 5/17/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@objc(ToolObject)
|
||||||
|
class ToolObject: NSManagedObject {
|
||||||
|
@NSManaged var pens: NSMutableSet
|
||||||
|
@NSManaged var memo: MemoObject?
|
||||||
|
}
|
||||||
@@ -15,6 +15,15 @@
|
|||||||
<attribute name="title" attributeType="String"/>
|
<attribute name="title" attributeType="String"/>
|
||||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<relationship name="canvas" maxCount="1" deletionRule="Cascade" destinationEntity="CanvasObject" inverseName="memo" inverseEntity="CanvasObject"/>
|
<relationship name="canvas" maxCount="1" deletionRule="Cascade" destinationEntity="CanvasObject" inverseName="memo" inverseEntity="CanvasObject"/>
|
||||||
|
<relationship name="tool" maxCount="1" deletionRule="Cascade" destinationEntity="ToolObject" inverseName="memo" inverseEntity="ToolObject"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="PenObject" representedClassName="PenObject" syncable="YES">
|
||||||
|
<attribute name="color" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromDataTransformer" customClassName="[CGFloat]"/>
|
||||||
|
<attribute name="isSelected" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="orderIndex" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="style" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="thickness" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||||
|
<relationship name="tool" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ToolObject" inverseName="pens" inverseEntity="ToolObject"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="QuadObject" representedClassName="QuadObject" syncable="YES">
|
<entity name="QuadObject" representedClassName="QuadObject" syncable="YES">
|
||||||
<attribute name="color" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromDataTransformer" customClassName="[CGFloat]"/>
|
<attribute name="color" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromDataTransformer" customClassName="[CGFloat]"/>
|
||||||
@@ -34,4 +43,8 @@
|
|||||||
<relationship name="graphicContext" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="GraphicContextObject" inverseName="strokes" inverseEntity="GraphicContextObject"/>
|
<relationship name="graphicContext" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="GraphicContextObject" inverseName="strokes" inverseEntity="GraphicContextObject"/>
|
||||||
<relationship name="quads" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="QuadObject" inverseName="stroke" inverseEntity="QuadObject"/>
|
<relationship name="quads" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="QuadObject" inverseName="stroke" inverseEntity="QuadObject"/>
|
||||||
</entity>
|
</entity>
|
||||||
|
<entity name="ToolObject" representedClassName="ToolObject" syncable="YES">
|
||||||
|
<relationship name="memo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MemoObject" inverseName="tool" inverseEntity="MemoObject"/>
|
||||||
|
<relationship name="pens" toMany="YES" deletionRule="Cascade" destinationEntity="PenObject" inverseName="tool" inverseEntity="PenObject"/>
|
||||||
|
</entity>
|
||||||
</model>
|
</model>
|
||||||
Reference in New Issue
Block a user