mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-05-11 02:10:05 +02:00
feat: add pen tool persistence
This commit is contained in:
@@ -7,6 +7,9 @@
|
||||
objects = {
|
||||
|
||||
/* 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 */; };
|
||||
EC3565542BEFC6AD00A4E0BF /* View++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3565532BEFC6AD00A4E0BF /* View++.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 */; };
|
||||
ECA738F62BE612B700A4542E /* MTLDevice++.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA738F52BE612B700A4542E /* MTLDevice++.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 */; };
|
||||
ECEC01A82BEE11BA006DA24C /* QuadShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECEC01A72BEE11BA006DA24C /* QuadShape.swift */; };
|
||||
ECFA15202BEF21EF00455818 /* MemoObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA151F2BEF21EF00455818 /* MemoObject.swift */; };
|
||||
@@ -81,6 +83,9 @@
|
||||
/* End PBXBuildFile 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>"; };
|
||||
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>"; };
|
||||
@@ -146,7 +151,6 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -556,7 +560,7 @@
|
||||
ECA738FE2BE61D5700A4542E /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ECA738FF2BE61D9C00A4542E /* MemolaModel.xcdatamodeld */,
|
||||
EC0D14222BF79C98009BFE5F /* MemolaModel.xcdatamodeld */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
@@ -586,6 +590,8 @@
|
||||
ECFA15232BEF223300455818 /* GraphicContextObject.swift */,
|
||||
ECFA15252BEF224900455818 /* StrokeObject.swift */,
|
||||
ECFA15272BEF225000455818 /* QuadObject.swift */,
|
||||
EC0D14202BF79C73009BFE5F /* ToolObject.swift */,
|
||||
EC0D14252BF7A8C9009BFE5F /* PenObject.swift */,
|
||||
);
|
||||
path = Objects;
|
||||
sourceTree = "<group>";
|
||||
@@ -696,8 +702,9 @@
|
||||
ECA738EE2BE6125D00A4542E /* simd_float4x4++.swift in Sources */,
|
||||
ECA7388C2BE6009600A4542E /* Textures.swift in Sources */,
|
||||
ECA738B82BE60DDC00A4542E /* HistoryEvent.swift in Sources */,
|
||||
EC0D14262BF7A8C9009BFE5F /* PenObject.swift in Sources */,
|
||||
ECA738952BE6012D00A4542E /* ViewPort.metal in Sources */,
|
||||
ECA739012BE61D9C00A4542E /* MemolaModel.xcdatamodeld in Sources */,
|
||||
EC0D14242BF79C98009BFE5F /* MemolaModel.xcdatamodeld in Sources */,
|
||||
ECA738F02BE6127700A4542E /* CGSize++.swift in Sources */,
|
||||
ECFA15242BEF223300455818 /* GraphicContextObject.swift in Sources */,
|
||||
EC3565562BEFC7B300A4E0BF /* NSManagedObject++.swift in Sources */,
|
||||
@@ -710,6 +717,7 @@
|
||||
ECA7388F2BE600DA00A4542E /* Grid.metal in Sources */,
|
||||
ECA738C92BE60EF700A4542E /* GraphicContext.swift in Sources */,
|
||||
ECA738F62BE612B700A4542E /* MTLDevice++.swift in Sources */,
|
||||
EC0D14212BF79C73009BFE5F /* ToolObject.swift in Sources */,
|
||||
EC50500D2BF6674400B4D86E /* DraggableViewModifier.swift in Sources */,
|
||||
ECA7389E2BE601CB00A4542E /* QuadVertex.swift in Sources */,
|
||||
ECA738B32BE60D9E00A4542E /* CanvasView.swift in Sources */,
|
||||
@@ -944,13 +952,14 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
ECA738FF2BE61D9C00A4542E /* MemolaModel.xcdatamodeld */ = {
|
||||
EC0D14222BF79C98009BFE5F /* MemolaModel.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
ECA739002BE61D9C00A4542E /* MemolaModel.xcdatamodel */,
|
||||
EC0D14232BF79C98009BFE5F /* MemolaModel.xcdatamodel */,
|
||||
);
|
||||
currentVersion = ECA739002BE61D9C00A4542E /* MemolaModel.xcdatamodel */;
|
||||
path = MemolaModel.xcdatamodeld;
|
||||
currentVersion = EC0D14232BF79C98009BFE5F /* MemolaModel.xcdatamodel */;
|
||||
name = MemolaModel.xcdatamodeld;
|
||||
path = /Users/dscyrescotti/Documents/Projects/Memola/Memola/Resources/Models/MemolaModel.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
};
|
||||
|
||||
@@ -6,27 +6,54 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import CoreData
|
||||
import Foundation
|
||||
|
||||
class Tool: NSObject, ObservableObject {
|
||||
@Published var pens: [Pen]
|
||||
public class Tool: NSObject, ObservableObject {
|
||||
let object: ToolObject
|
||||
|
||||
@Published var pens: [Pen] = []
|
||||
@Published var selectedPen: Pen?
|
||||
@Published var draggedPen: Pen?
|
||||
|
||||
override init() {
|
||||
pens = [
|
||||
Pen(for: .eraser),
|
||||
Pen(for: .marker)
|
||||
]
|
||||
super.init()
|
||||
selectedPen = pens[1]
|
||||
init(object: ToolObject) {
|
||||
self.object = object
|
||||
}
|
||||
|
||||
func changePen(_ pen: Pen) {
|
||||
selectedPen = pen
|
||||
func load() {
|
||||
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) {
|
||||
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
|
||||
|
||||
class Pen: NSObject, ObservableObject, Identifiable {
|
||||
let id: String
|
||||
@Published var style: any PenStyle
|
||||
@Published var color: [CGFloat]
|
||||
@Published var thickness: CGFloat
|
||||
var object: PenObject?
|
||||
|
||||
init(style: any PenStyle, color: [CGFloat], thickness: CGFloat) {
|
||||
self.id = UUID().uuidString
|
||||
self.style = style
|
||||
self.color = color
|
||||
self.thickness = thickness
|
||||
let id: String
|
||||
@Published var style: any PenStyle {
|
||||
didSet {
|
||||
object?.style = strokeStyle.rawValue
|
||||
}
|
||||
}
|
||||
@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 {
|
||||
switch style {
|
||||
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)
|
||||
style.strokeStyle
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,15 @@ extension PenStyle {
|
||||
func loadTexture(on device: MTLDevice) -> MTLTexture? {
|
||||
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 {
|
||||
func loadMemo() {
|
||||
tool.load()
|
||||
canvas.load()
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,8 @@ import CoreData
|
||||
|
||||
struct MemoView: View {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@Environment(\.managedObjectContext) var managedObjectContext
|
||||
|
||||
@StateObject var tool = Tool()
|
||||
@StateObject var tool: Tool
|
||||
@StateObject var canvas: Canvas
|
||||
@StateObject var history = History()
|
||||
|
||||
@@ -20,6 +19,7 @@ struct MemoView: View {
|
||||
|
||||
init(memo: MemoObject) {
|
||||
self.memo = memo
|
||||
self._tool = StateObject(wrappedValue: Tool(object: memo.tool))
|
||||
self._canvas = StateObject(wrappedValue: Canvas(size: memo.canvas.size, canvasID: memo.canvas.objectID))
|
||||
}
|
||||
|
||||
@@ -97,13 +97,8 @@ struct MemoView: View {
|
||||
}
|
||||
|
||||
func closeMemo() {
|
||||
history.resetRedo()
|
||||
if managedObjectContext.hasChanges {
|
||||
do {
|
||||
try managedObjectContext.save()
|
||||
} catch {
|
||||
NSLog("[Memola] - \(error.localizedDescription)")
|
||||
}
|
||||
withPersistenceSync(\.viewContext) { context in
|
||||
try context.saveIfNeeded()
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
|
||||
@@ -27,6 +27,11 @@ struct PenDropDelegate: DropDelegate {
|
||||
tool.pens.move(fromOffsets: IndexSet(integer: fromIndex), toOffset: toIndex > fromIndex ? toIndex + 1 : toIndex)
|
||||
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 {
|
||||
if tool.selectedPen === pen {
|
||||
withAnimation {
|
||||
tool.selectedPen = nil
|
||||
tool.unselectPen(pen)
|
||||
}
|
||||
} else {
|
||||
withAnimation {
|
||||
tool.changePen(pen)
|
||||
tool.selectPen(pen)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,9 +95,13 @@ struct PenToolView: View {
|
||||
|
||||
var newPenButton: some View {
|
||||
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
|
||||
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")
|
||||
.font(.title3)
|
||||
|
||||
@@ -62,28 +62,45 @@ struct MemosView: View {
|
||||
}
|
||||
|
||||
func createMemo(title: String) {
|
||||
do {
|
||||
let memoObject = MemoObject(context: managedObjectContext)
|
||||
memoObject.title = title
|
||||
memoObject.createdAt = .now
|
||||
memoObject.updatedAt = .now
|
||||
let memoObject = MemoObject(\.viewContext)
|
||||
memoObject.title = title
|
||||
memoObject.createdAt = .now
|
||||
memoObject.updatedAt = .now
|
||||
|
||||
let canvasObject = CanvasObject(context: managedObjectContext)
|
||||
canvasObject.width = 8_000
|
||||
canvasObject.height = 8_000
|
||||
let canvasObject = CanvasObject(context: managedObjectContext)
|
||||
canvasObject.width = 8_000
|
||||
canvasObject.height = 8_000
|
||||
|
||||
let graphicContextObject = GraphicContextObject(context: managedObjectContext)
|
||||
graphicContextObject.strokes = []
|
||||
let toolObject = ToolObject(\.viewContext)
|
||||
toolObject.pens = []
|
||||
|
||||
memoObject.canvas = canvasObject
|
||||
canvasObject.memo = memoObject
|
||||
canvasObject.graphicContext = graphicContextObject
|
||||
graphicContextObject.canvas = canvasObject
|
||||
let eraserPenObject = PenObject.createObject(\.viewContext, penStyle: .eraser)
|
||||
eraserPenObject.orderIndex = 0
|
||||
let markerPenObject = PenObject.createObject(\.viewContext, penStyle: .marker)
|
||||
markerPenObject.orderIndex = 1
|
||||
|
||||
try managedObjectContext.save()
|
||||
openMemo(for: memoObject)
|
||||
} catch {
|
||||
NSLog("[Memola] - \(error.localizedDescription)")
|
||||
let graphicContextObject = GraphicContextObject(\.viewContext)
|
||||
graphicContextObject.strokes = []
|
||||
|
||||
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)
|
||||
final class MemoObject: NSManagedObject, Identifiable {
|
||||
@NSManaged var title: String
|
||||
@NSManaged var data: Data
|
||||
@NSManaged var title: String
|
||||
@NSManaged var createdAt: Date
|
||||
@NSManaged var updatedAt: Date
|
||||
@NSManaged var tool: ToolObject
|
||||
@NSManaged var canvas: CanvasObject
|
||||
}
|
||||
|
||||
30
Memola/Persistence/Objects/PenObject.swift
Normal file
30
Memola/Persistence/Objects/PenObject.swift
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
15
Memola/Persistence/Objects/ToolObject.swift
Normal file
15
Memola/Persistence/Objects/ToolObject.swift
Normal file
@@ -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="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<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 name="QuadObject" representedClassName="QuadObject" syncable="YES">
|
||||
<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="quads" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="QuadObject" inverseName="stroke" inverseEntity="QuadObject"/>
|
||||
</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>
|
||||
Reference in New Issue
Block a user