diff --git a/Memola.xcodeproj/project.pbxproj b/Memola.xcodeproj/project.pbxproj index 7401d7e..6f98d97 100644 --- a/Memola.xcodeproj/project.pbxproj +++ b/Memola.xcodeproj/project.pbxproj @@ -7,12 +7,18 @@ 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 */; }; + EC0D14282BF7BF20009BFE5F /* ContextMenuViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.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 */; }; EC35655A2BF060D900A4E0BF /* Quad.metal in Sources */ = {isa = PBXBuildFile; fileRef = EC3565592BF060D900A4E0BF /* Quad.metal */; }; EC35655C2BF0712A00A4E0BF /* Float++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC35655B2BF0712A00A4E0BF /* Float++.swift */; }; EC4538892BEBCAE000A86FEC /* Quad.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC4538882BEBCAE000A86FEC /* Quad.swift */; }; + EC5050072BF65CED00B4D86E /* PenDropDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC5050062BF65CED00B4D86E /* PenDropDelegate.swift */; }; + EC50500D2BF6674400B4D86E /* OnDragViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC50500C2BF6674400B4D86E /* OnDragViewModifier.swift */; }; EC7F6BEC2BE5E6E300A34A7B /* MemolaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7F6BEB2BE5E6E300A34A7B /* MemolaApp.swift */; }; EC7F6BF02BE5E6E400A34A7B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC7F6BEF2BE5E6E400A34A7B /* Assets.xcassets */; }; EC7F6BF32BE5E6E400A34A7B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC7F6BF22BE5E6E400A34A7B /* Preview Assets.xcassets */; }; @@ -68,23 +74,30 @@ 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 */; }; + ECA739082BE623F300A4542E /* PenDockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA739072BE623F300A4542E /* PenDockView.swift */; }; ECEC01A82BEE11BA006DA24C /* QuadShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECEC01A72BEE11BA006DA24C /* QuadShape.swift */; }; ECFA15202BEF21EF00455818 /* MemoObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA151F2BEF21EF00455818 /* MemoObject.swift */; }; ECFA15222BEF21F500455818 /* CanvasObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA15212BEF21F500455818 /* CanvasObject.swift */; }; ECFA15242BEF223300455818 /* GraphicContextObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA15232BEF223300455818 /* GraphicContextObject.swift */; }; ECFA15262BEF224900455818 /* StrokeObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA15252BEF224900455818 /* StrokeObject.swift */; }; ECFA15282BEF225000455818 /* QuadObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA15272BEF225000455818 /* QuadObject.swift */; }; + ECFC51272BF8885700D0D051 /* ColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFC51262BF8885700D0D051 /* ColorPicker.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + EC0D14202BF79C73009BFE5F /* ToolObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolObject.swift; sourceTree = ""; }; + EC0D14232BF79C98009BFE5F /* MemolaModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MemolaModel.xcdatamodel; sourceTree = ""; }; + EC0D14252BF7A8C9009BFE5F /* PenObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenObject.swift; sourceTree = ""; }; + EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuViewModifier.swift; sourceTree = ""; }; EC3565512BEFC65F00A4E0BF /* NSManagedObjectContext++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext++.swift"; sourceTree = ""; }; EC3565532BEFC6AD00A4E0BF /* View++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View++.swift"; sourceTree = ""; }; EC3565552BEFC7B300A4E0BF /* NSManagedObject++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObject++.swift"; sourceTree = ""; }; EC3565592BF060D900A4E0BF /* Quad.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Quad.metal; sourceTree = ""; }; EC35655B2BF0712A00A4E0BF /* Float++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Float++.swift"; sourceTree = ""; }; EC4538882BEBCAE000A86FEC /* Quad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Quad.swift; sourceTree = ""; }; + EC5050062BF65CED00B4D86E /* PenDropDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenDropDelegate.swift; sourceTree = ""; }; + EC50500C2BF6674400B4D86E /* OnDragViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnDragViewModifier.swift; sourceTree = ""; }; + EC50500E2BF670EA00B4D86E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; EC7F6BE82BE5E6E300A34A7B /* Memola.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Memola.app; sourceTree = BUILT_PRODUCTS_DIR; }; EC7F6BEB2BE5E6E300A34A7B /* MemolaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemolaApp.swift; sourceTree = ""; }; EC7F6BEF2BE5E6E400A34A7B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -141,14 +154,14 @@ ECA738F32BE612A000A4542E /* Array++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array++.swift"; sourceTree = ""; }; ECA738F52BE612B700A4542E /* MTLDevice++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MTLDevice++.swift"; sourceTree = ""; }; ECA738FB2BE61C5200A4542E /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; - ECA739002BE61D9C00A4542E /* MemolaModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MemolaModel.xcdatamodel; sourceTree = ""; }; - ECA739072BE623F300A4542E /* PenToolView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenToolView.swift; sourceTree = ""; }; + ECA739072BE623F300A4542E /* PenDockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenDockView.swift; sourceTree = ""; }; ECEC01A72BEE11BA006DA24C /* QuadShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuadShape.swift; sourceTree = ""; }; ECFA151F2BEF21EF00455818 /* MemoObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoObject.swift; sourceTree = ""; }; ECFA15212BEF21F500455818 /* CanvasObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CanvasObject.swift; sourceTree = ""; }; ECFA15232BEF223300455818 /* GraphicContextObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphicContextObject.swift; sourceTree = ""; }; ECFA15252BEF224900455818 /* StrokeObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StrokeObject.swift; sourceTree = ""; }; ECFA15272BEF225000455818 /* QuadObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuadObject.swift; sourceTree = ""; }; + ECFC51262BF8885700D0D051 /* ColorPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPicker.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -178,6 +191,73 @@ path = ViewController; sourceTree = ""; }; + EC1B783A2BF9C68C005A34E2 /* Views */ = { + isa = PBXGroup; + children = ( + ECFC51252BF8885000D0D051 /* ColorPicker */, + ); + path = Views; + sourceTree = ""; + }; + EC5050042BF65CBC00B4D86E /* Core */ = { + isa = PBXGroup; + children = ( + ECA738BB2BE60E0300A4542E /* Tool.swift */, + ); + path = Core; + sourceTree = ""; + }; + EC5050052BF65CCD00B4D86E /* PenDock */ = { + isa = PBXGroup; + children = ( + ECA739072BE623F300A4542E /* PenDockView.swift */, + EC5050062BF65CED00B4D86E /* PenDropDelegate.swift */, + ); + path = PenDock; + sourceTree = ""; + }; + EC5050082BF65D0500B4D86E /* Memo */ = { + isa = PBXGroup; + children = ( + ECA7387C2BE5EF4B00A4542E /* MemoView.swift */, + ); + path = Memo; + sourceTree = ""; + }; + EC5050092BF65D5700B4D86E /* Canvas */ = { + isa = PBXGroup; + children = ( + ECA738B22BE60D9E00A4542E /* CanvasView.swift */, + ); + path = Canvas; + sourceTree = ""; + }; + EC50500A2BF6672000B4D86E /* Components */ = { + isa = PBXGroup; + children = ( + EC1B783A2BF9C68C005A34E2 /* Views */, + EC50500B2BF6673300B4D86E /* ViewModifiers */, + ); + path = Components; + sourceTree = ""; + }; + EC50500B2BF6673300B4D86E /* ViewModifiers */ = { + isa = PBXGroup; + children = ( + EC50500C2BF6674400B4D86E /* OnDragViewModifier.swift */, + EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.swift */, + ); + path = ViewModifiers; + sourceTree = ""; + }; + EC5050102BF670EE00B4D86E /* Config */ = { + isa = PBXGroup; + children = ( + EC50500E2BF670EA00B4D86E /* Info.plist */, + ); + path = Config; + sourceTree = ""; + }; EC7F6BDF2BE5E6E300A34A7B = { isa = PBXGroup; children = ( @@ -199,6 +279,8 @@ children = ( ECA738762BE5EE4E00A4542E /* App */, ECA7387E2BE5FE4200A4542E /* Canvas */, + EC5050102BF670EE00B4D86E /* Config */, + EC50500A2BF6672000B4D86E /* Components */, ECA738A12BE601F700A4542E /* Extensions */, ECA738772BE5EEE800A4542E /* Features */, ECA738FA2BE61B1700A4542E /* Persistence */, @@ -244,8 +326,8 @@ ECA7387B2BE5EF3500A4542E /* Memo */ = { isa = PBXGroup; children = ( - ECA7387C2BE5EF4B00A4542E /* MemoView.swift */, - ECA739072BE623F300A4542E /* PenToolView.swift */, + EC5050082BF65D0500B4D86E /* Memo */, + EC5050052BF65CCD00B4D86E /* PenDock */, ); path = Memo; sourceTree = ""; @@ -371,8 +453,8 @@ ECA738AB2BE60CB500A4542E /* View */ = { isa = PBXGroup; children = ( + EC5050092BF65D5700B4D86E /* Canvas */, ECA738AE2BE60CEC00A4542E /* Bridge */, - ECA738B22BE60D9E00A4542E /* CanvasView.swift */, ); path = View; sourceTree = ""; @@ -389,8 +471,8 @@ ECA738B12BE60D8800A4542E /* Tool */ = { isa = PBXGroup; children = ( + EC5050042BF65CBC00B4D86E /* Core */, ECA738BD2BE60E2800A4542E /* Pen */, - ECA738BB2BE60E0300A4542E /* Tool.swift */, ); path = Tool; sourceTree = ""; @@ -492,7 +574,7 @@ ECA738FE2BE61D5700A4542E /* Models */ = { isa = PBXGroup; children = ( - ECA738FF2BE61D9C00A4542E /* MemolaModel.xcdatamodeld */, + EC0D14222BF79C98009BFE5F /* MemolaModel.xcdatamodeld */, ); path = Models; sourceTree = ""; @@ -522,10 +604,20 @@ ECFA15232BEF223300455818 /* GraphicContextObject.swift */, ECFA15252BEF224900455818 /* StrokeObject.swift */, ECFA15272BEF225000455818 /* QuadObject.swift */, + EC0D14202BF79C73009BFE5F /* ToolObject.swift */, + EC0D14252BF7A8C9009BFE5F /* PenObject.swift */, ); path = Objects; sourceTree = ""; }; + ECFC51252BF8885000D0D051 /* ColorPicker */ = { + isa = PBXGroup; + children = ( + ECFC51262BF8885700D0D051 /* ColorPicker.swift */, + ); + path = ColorPicker; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -627,13 +719,15 @@ ECA738862BE5FF2500A4542E /* Canvas.swift in Sources */, ECA738882BE5FF4400A4542E /* Renderer.swift in Sources */, ECA738D42BE60F9100A4542E /* StrokeGenerator.swift in Sources */, - ECA739082BE623F300A4542E /* PenToolView.swift in Sources */, + ECA739082BE623F300A4542E /* PenDockView.swift in Sources */, ECA738CB2BE60F1900A4542E /* ViewPortContext.swift in Sources */, ECA738EE2BE6125D00A4542E /* simd_float4x4++.swift in Sources */, ECA7388C2BE6009600A4542E /* Textures.swift in Sources */, + ECFC51272BF8885700D0D051 /* ColorPicker.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 */, @@ -646,6 +740,8 @@ ECA7388F2BE600DA00A4542E /* Grid.metal in Sources */, ECA738C92BE60EF700A4542E /* GraphicContext.swift in Sources */, ECA738F62BE612B700A4542E /* MTLDevice++.swift in Sources */, + EC0D14212BF79C73009BFE5F /* ToolObject.swift in Sources */, + EC50500D2BF6674400B4D86E /* OnDragViewModifier.swift in Sources */, ECA7389E2BE601CB00A4542E /* QuadVertex.swift in Sources */, ECA738B32BE60D9E00A4542E /* CanvasView.swift in Sources */, ECA738C42BE60E8800A4542E /* MarkerPenStyle.swift in Sources */, @@ -655,11 +751,13 @@ ECA738B62BE60DCD00A4542E /* History.swift in Sources */, ECA738D22BE60F7B00A4542E /* Stroke.swift in Sources */, ECA738F22BE6128F00A4542E /* Collection++.swift in Sources */, + EC5050072BF65CED00B4D86E /* PenDropDelegate.swift in Sources */, ECA738A32BE6020A00A4542E /* CGFloat++.swift in Sources */, ECA738C12BE60E5300A4542E /* PenStyle.swift in Sources */, ECA738DE2BE610A000A4542E /* ViewPortRenderPass.swift in Sources */, EC7F6BEC2BE5E6E300A34A7B /* MemolaApp.swift in Sources */, ECA738A02BE601E400A4542E /* ViewPortVertex.swift in Sources */, + EC0D14282BF7BF20009BFE5F /* ContextMenuViewModifier.swift in Sources */, ECA738BC2BE60E0300A4542E /* Tool.swift in Sources */, ECA738972BE6014200A4542E /* Graphic.metal in Sources */, ECA7388A2BE6006A00A4542E /* PipelineStates.swift in Sources */, @@ -798,7 +896,8 @@ DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\""; DEVELOPMENT_TEAM = 9TYSSFKV5U; ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = Memola/Config/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -830,7 +929,8 @@ DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\""; DEVELOPMENT_TEAM = 9TYSSFKV5U; ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = Memola/Config/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -876,13 +976,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 = ""; versionGroupType = wrapper.xcdatamodel; }; diff --git a/Memola/Canvas/Contexts/GraphicContext.swift b/Memola/Canvas/Contexts/GraphicContext.swift index cf6f565..fa6b910 100644 --- a/Memola/Canvas/Contexts/GraphicContext.swift +++ b/Memola/Canvas/Contexts/GraphicContext.swift @@ -117,7 +117,7 @@ extension GraphicContext { func beginStroke(at point: CGPoint, pen: Pen) -> Stroke { let stroke = Stroke( bounds: [point.x - pen.thickness, point.y - pen.thickness, point.x + pen.thickness, point.y + pen.thickness], - color: pen.color, + color: pen.rgba, style: pen.strokeStyle.rawValue, createdAt: .now, thickness: pen.thickness diff --git a/Memola/Canvas/RenderPasses/EraserRenderPass.swift b/Memola/Canvas/RenderPasses/EraserRenderPass.swift index ab4aab8..46bbe54 100644 --- a/Memola/Canvas/RenderPasses/EraserRenderPass.swift +++ b/Memola/Canvas/RenderPasses/EraserRenderPass.swift @@ -14,12 +14,14 @@ class EraserRenderPass: RenderPass { var descriptor: MTLRenderPassDescriptor? var eraserPipelineState: MTLRenderPipelineState? + var quadPipelineState: MTLComputePipelineState? var stroke: Stroke? weak var graphicTexture: MTLTexture? init(renderer: Renderer) { eraserPipelineState = PipelineStates.createEraserPipelineState(from: renderer) + quadPipelineState = PipelineStates.createQuadPipelineState(from: renderer) } func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { } @@ -27,6 +29,8 @@ class EraserRenderPass: RenderPass { func draw(on canvas: Canvas, with renderer: Renderer) { guard let descriptor else { return } + generateVertexBuffer(on: canvas, with: renderer) + guard let commandBuffer = renderer.commandQueue.makeCommandBuffer() else { return } commandBuffer.label = "Eraser Command Buffer" @@ -42,4 +46,30 @@ class EraserRenderPass: RenderPass { renderEncoder.endEncoding() commandBuffer.commit() } + + private func generateVertexBuffer(on canvas: Canvas, with renderer: Renderer) { + guard let stroke, !stroke.quads.isEmpty, let quadPipelineState else { return } + guard let quadCommandBuffer = renderer.commandQueue.makeCommandBuffer() else { return } + guard let computeEncoder = quadCommandBuffer.makeComputeCommandEncoder() else { return } + + computeEncoder.label = "Quad Render Pass" + + let quadCount = stroke.quads.endIndex + var quads = stroke.quads + let quadBuffer = renderer.device.makeBuffer(bytes: &quads, length: MemoryLayout.stride * quadCount, options: []) + let vertexBuffer = renderer.device.makeBuffer(length: MemoryLayout.stride * quadCount * 6, options: []) + + computeEncoder.setComputePipelineState(quadPipelineState) + computeEncoder.setBuffer(quadBuffer, offset: 0, index: 0) + computeEncoder.setBuffer(vertexBuffer, offset: 0, index: 1) + + stroke.vertexBuffer = vertexBuffer + + let threadsPerGroup = MTLSize(width: 1, height: 1, depth: 1) + let numThreadgroups = MTLSize(width: quadCount + 1, height: 1, depth: 1) + computeEncoder.dispatchThreadgroups(numThreadgroups, threadsPerThreadgroup: threadsPerGroup) + computeEncoder.endEncoding() + quadCommandBuffer.commit() + quadCommandBuffer.waitUntilCompleted() + } } diff --git a/Memola/Canvas/Tool/Core/Tool.swift b/Memola/Canvas/Tool/Core/Tool.swift new file mode 100644 index 0000000..5d05c80 --- /dev/null +++ b/Memola/Canvas/Tool/Core/Tool.swift @@ -0,0 +1,109 @@ +// +// Tool.swift +// Memola +// +// Created by Dscyre Scotti on 5/4/24. +// + +import Combine +import SwiftUI +import CoreData +import Foundation + +public class Tool: NSObject, ObservableObject { + let object: ToolObject + + @Published var pens: [Pen] = [] + @Published var selectedPen: Pen? + @Published var draggedPen: Pen? + + let scrollPublisher = PassthroughSubject() + var markers: [Pen] { + pens.filter { $0.strokeStyle == .marker } + } + + init(object: ToolObject) { + self.object = object + } + + 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) + scrollPublisher.send(selectedPen.id) + } + } + } + + func selectPen(_ pen: Pen) { + if let selectedPen { + unselectPen(selectedPen) + } + withAnimation { + selectedPen = pen + } + selectedPen?.isSelected = true + withPersistence(\.viewContext) { context in + try context.saveIfNeeded() + } + } + + func unselectPen(_ pen: Pen) { + pen.isSelected = false + withAnimation { + selectedPen = nil + } + withPersistence(\.viewContext) { context in + try context.saveIfNeeded() + } + } + + func duplicatePen(_ pen: Pen, of originalPen: Pen) { + guard let index = pens.firstIndex(where: { originalPen === $0 }) else { return } + withAnimation { + pens.insert(pen, at: index + 1) + } + selectPen(pen) + withPersistence(\.viewContext) { [pens] context in + for (index, pen) in pens.enumerated() { + pen.object?.orderIndex = Int16(index) + } + try context.saveIfNeeded() + } + } + + func addPen(_ pen: Pen) { + withAnimation { + pens.append(pen) + } + selectPen(pen) + if let _pen = pen.object { + object.pens.add(_pen) + } + scrollPublisher.send(pen.id) + withPersistence(\.viewContext) { context in + try context.saveIfNeeded() + } + } + + func removePen(_ pen: Pen) { + guard let index = pens.firstIndex(where: { $0 === pen }) else { return } + let deletedPen = withAnimation { + pens.remove(at: index) + } + unselectPen(deletedPen) + if let _pen = deletedPen.object { + _pen.tool = nil + object.pens.remove(_pen) + withPersistence(\.viewContext) { context in + context.delete(_pen) + try context.saveIfNeeded() + } + } + } +} diff --git a/Memola/Canvas/Tool/Pen/Core/Pen.swift b/Memola/Canvas/Tool/Pen/Core/Pen.swift index d2a5079..4b453cf 100644 --- a/Memola/Canvas/Tool/Pen/Core/Pen.swift +++ b/Memola/Canvas/Tool/Pen/Core/Pen.swift @@ -7,32 +7,50 @@ import SwiftUI import Foundation +import UniformTypeIdentifiers class Pen: NSObject, ObservableObject, Identifiable { - @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.style = style - self.color = color - self.thickness = thickness + let id: String + @Published var style: any PenStyle { + didSet { + object?.style = strokeStyle.rawValue + } + } + @Published var rgba: [CGFloat] { + didSet { + object?.color = rgba + } + } + @Published var thickness: CGFloat { + didSet { + object?.thickness = thickness + } + } + @Published var isSelected: Bool { + didSet { + object?.isSelected = isSelected + } + } + var color: Color { + get { Color.rgba(from: rgba) } + set { + rgba = newValue.components + } + } + + init(object: PenObject) { + self.object = object + self.id = object.objectID.uriRepresentation().absoluteString + self.style = (Stroke.Style(rawValue: object.style) ?? .marker).anyPenStyle + self.rgba = 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 } } diff --git a/Memola/Canvas/Tool/Pen/Core/PenStyle.swift b/Memola/Canvas/Tool/Pen/Core/PenStyle.swift index 618806c..85b1a05 100644 --- a/Memola/Canvas/Tool/Pen/Core/PenStyle.swift +++ b/Memola/Canvas/Tool/Pen/Core/PenStyle.swift @@ -11,7 +11,8 @@ import Foundation protocol PenStyle { var icon: (base: String, tip: String?) { get } var textureName: String { get } - var thinkness: (min: CGFloat, max: CGFloat) { get } + var thickness: (min: CGFloat, max: CGFloat) { get } + var thicknessSteps: [CGFloat] { get } var color: [CGFloat] { get } var stepRate: CGFloat { get } var generator: any StrokeGenerator { get } @@ -22,4 +23,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 + } + } } diff --git a/Memola/Canvas/Tool/Pen/PenStyles/EraserPenStyle.swift b/Memola/Canvas/Tool/Pen/PenStyles/EraserPenStyle.swift index a18d8df..930b1c7 100644 --- a/Memola/Canvas/Tool/Pen/PenStyles/EraserPenStyle.swift +++ b/Memola/Canvas/Tool/Pen/PenStyles/EraserPenStyle.swift @@ -12,7 +12,9 @@ struct EraserPenStyle: PenStyle { var textureName: String = "point-texture" - var thinkness: (min: CGFloat, max: CGFloat) = (0.5, 120) + var thickness: (min: CGFloat, max: CGFloat) = (0.5, 40) + + var thicknessSteps: [CGFloat] = [0.5, 1, 2, 5, 7.5, 10, 15, 20, 25, 30, 35, 40] var color: [CGFloat] = [1, 1, 1, 0] diff --git a/Memola/Canvas/Tool/Pen/PenStyles/MarkerPenStyle.swift b/Memola/Canvas/Tool/Pen/PenStyles/MarkerPenStyle.swift index ed82047..9c25a7b 100644 --- a/Memola/Canvas/Tool/Pen/PenStyles/MarkerPenStyle.swift +++ b/Memola/Canvas/Tool/Pen/PenStyles/MarkerPenStyle.swift @@ -12,7 +12,9 @@ struct MarkerPenStyle: PenStyle { var textureName: String = "point-texture" - var thinkness: (min: CGFloat, max: CGFloat) = (0.5, 120) + var thickness: (min: CGFloat, max: CGFloat) = (0.5, 40) + + var thicknessSteps: [CGFloat] = [0.5, 1, 2, 5, 7.5, 10, 15, 20, 25, 30, 35, 40] var color: [CGFloat] = [1, 0.38, 0.38, 1] diff --git a/Memola/Canvas/Tool/Tool.swift b/Memola/Canvas/Tool/Tool.swift deleted file mode 100644 index 9df2cbb..0000000 --- a/Memola/Canvas/Tool/Tool.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// Tool.swift -// Memola -// -// Created by Dscyre Scotti on 5/4/24. -// - -import SwiftUI -import Foundation - -class Tool: NSObject, ObservableObject { - @Published var pens: [Pen] - @Published var selectedPen: Pen? - - override init() { - pens = [ - Pen(for: .marker), - Pen(for: .eraser) - ] - super.init() - selectedPen = pens.first - } - - func changePen(_ pen: Pen) { - selectedPen = pen - } -} diff --git a/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift b/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift index 0a37e68..50cc89a 100644 --- a/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift +++ b/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift @@ -183,6 +183,7 @@ extension CanvasViewController { extension CanvasViewController { func loadMemo() { + tool.load() canvas.load() } diff --git a/Memola/Canvas/View/CanvasView.swift b/Memola/Canvas/View/Canvas/CanvasView.swift similarity index 100% rename from Memola/Canvas/View/CanvasView.swift rename to Memola/Canvas/View/Canvas/CanvasView.swift diff --git a/Memola/Components/ViewModifiers/ContextMenuViewModifier.swift b/Memola/Components/ViewModifiers/ContextMenuViewModifier.swift new file mode 100644 index 0000000..ee1a3f2 --- /dev/null +++ b/Memola/Components/ViewModifiers/ContextMenuViewModifier.swift @@ -0,0 +1,30 @@ +// +// ContextMenuViewModifier.swift +// Memola +// +// Created by Dscyre Scotti on 5/17/24. +// + +import SwiftUI +import Foundation + +struct ContextMenuViewModifier: ViewModifier { + let condition: Bool + let menuItems: () -> MenuContent + let preview: () -> Preview + + @ViewBuilder + func body(content: Content) -> some View { + if condition { + content.contextMenu(menuItems: menuItems, preview: preview) + } else { + content + } + } +} + +public extension View { + func contextMenu(if condition: Bool, @ViewBuilder menuItems: @escaping () -> MenuContent, @ViewBuilder preview: @escaping () -> Preview) -> some View { + modifier(ContextMenuViewModifier(condition: condition, menuItems: menuItems, preview: preview)) + } +} diff --git a/Memola/Components/ViewModifiers/OnDragViewModifier.swift b/Memola/Components/ViewModifiers/OnDragViewModifier.swift new file mode 100644 index 0000000..b60d540 --- /dev/null +++ b/Memola/Components/ViewModifiers/OnDragViewModifier.swift @@ -0,0 +1,30 @@ +// +// OnDragViewModifier.swift +// Memola +// +// Created by Dscyre Scotti on 5/16/24. +// + +import SwiftUI +import Foundation + +struct OnDragViewModifier: ViewModifier { + let condition: Bool + let data: () -> NSItemProvider + let preview: () -> Preview + + @ViewBuilder + func body(content: Content) -> some View { + if condition { + content.onDrag(data, preview: preview) + } else { + content + } + } +} + +public extension View { + func onDrag(if condition: Bool, data: @escaping () -> NSItemProvider, @ViewBuilder preview: @escaping () -> Preview) -> some View { + modifier(OnDragViewModifier(condition: condition, data: data, preview: preview)) + } +} diff --git a/Memola/Components/Views/ColorPicker/ColorPicker.swift b/Memola/Components/Views/ColorPicker/ColorPicker.swift new file mode 100644 index 0000000..fe29a9e --- /dev/null +++ b/Memola/Components/Views/ColorPicker/ColorPicker.swift @@ -0,0 +1,196 @@ +// +// ColorPicker.swift +// Memola +// +// Created by Dscyre Scotti on 5/18/24. +// + +import SwiftUI +import Foundation + +struct ColorPicker: View { + @State var hue: Double = 1 + @State var saturation: Double = 0 + @State var brightness: Double = 1 + @State var alpha: Double = 1 + + @Binding var color: Color + + let size: CGFloat = 20 + + var body: some View { + VStack(spacing: 10) { + colorPicker + .frame(width: 200, height: 200) + HStack(spacing: 10) { + hueSlider + alphaSlider + } + } + .padding(10) + .background { + Rectangle() + .fill(.regularMaterial) + .ignoresSafeArea(.all) + } + .onAppear { + let hsba = color.hsba + hue = hsba.hue + saturation = hsba.saturation + brightness = hsba.brightness + alpha = hsba.alpha * 1.43 - 0.43 + } + } + + @ViewBuilder + var colorPicker: some View { + GeometryReader { proxy in + ZStack { + Color(hue: hue, saturation: 1, brightness: 1) + LinearGradient( + colors: [.white, .clear], + startPoint: .leading, + endPoint: .trailing + ) + LinearGradient( + colors: [.black, .clear], + startPoint: .bottom, + endPoint: .top + ) + } + .cornerRadius(5) + .drawingGroup() + .overlay { + RoundedRectangle(cornerRadius: 5) + .stroke(Color.gray, lineWidth: 0.2) + } + .overlay(alignment: .bottomLeading) { + Color(hue: hue, saturation: saturation, brightness: brightness) + .frame(width: size, height: size) + .clipShape(Circle()) + .padding(1) + .overlay { + Circle() + .strokeBorder(.white, lineWidth: 2) + } + .overlay { + Circle() + .stroke(Color.gray, lineWidth: 0.2) + } + .offset(x: -size + 5, y: size - 5) + .offset(x: max(proxy.size.width * saturation, size - 10), y: min(proxy.size.height * -brightness, -size + 10)) + } + .gesture( + DragGesture(minimumDistance: 0) + .onChanged { value in + saturation = min(1, max(value.location.x / proxy.size.width, 0)) + brightness = 1 - min(1, max(value.location.y / proxy.size.height, 0)) + updateColor() + } + ) + } + } + + @ViewBuilder + var hueSlider: some View { + GeometryReader { proxy in + ZStack(alignment: .leading) { + LinearGradient( + colors: (0...10).map { Color(hue: Double($0) * 0.1, saturation: 1, brightness: 1) }, + startPoint: .leading, + endPoint: .trailing + ) + Color(hue: hue, saturation: 1, brightness: 1) + .frame(width: size, height: size) + .clipShape(Circle()) + .padding(1) + .overlay { + Circle() + .strokeBorder(.white, lineWidth: 2) + } + .overlay { + Circle() + .stroke(Color.gray, lineWidth: 0.2) + } + .offset(x: -size) + .offset(x: max(size, proxy.size.width * hue - 2)) + } + .frame(width: proxy.size.width, height: proxy.size.height) + .gesture( + DragGesture(minimumDistance: 0) + .onChanged { value in + hue = min(1, max(value.location.x / proxy.size.width, 0)) + updateColor() + } + .onEnded { value in + hue = min(1, max(value.location.x / proxy.size.width, 0)) + updateColor() + } + ) + .clipShape(Capsule()) + .overlay { + Capsule() + .stroke(Color.gray, lineWidth: 0.2) + } + .frame(height: proxy.size.height) + } + .frame(height: size) + } + + @ViewBuilder + var alphaSlider: some View { + GeometryReader { proxy in + let color = Color(hue: hue, saturation: saturation, brightness: brightness) + ZStack(alignment: .leading) { + LinearGradient( + colors: (3...10).map { color.opacity(0.1 * CGFloat($0)) }, + startPoint: .leading, + endPoint: .trailing + ) + .background { + Image("transparent-grid-rect") + .resizable() + .aspectRatio(contentMode: .fill) + .background(.white) + } + color + .frame(width: size, height: size) + .clipShape(Circle()) + .padding(1) + .overlay { + Circle() + .strokeBorder(.white, lineWidth: 2) + } + .overlay { + Circle() + .stroke(Color.gray, lineWidth: 0.2) + } + .offset(x: -size) + .offset(x: max(size, proxy.size.width * alpha - 2)) + } + .frame(width: proxy.size.width, height: proxy.size.height) + .gesture( + DragGesture(minimumDistance: 0) + .onChanged { value in + alpha = min(1, max(value.location.x / proxy.size.width, 0)) + updateColor() + } + .onEnded { value in + alpha = min(1, max(value.location.x / proxy.size.width, 0)) + updateColor() + } + ) + .clipShape(Capsule()) + .overlay { + Capsule() + .stroke(Color.gray, lineWidth: 0.2) + } + .frame(height: proxy.size.height) + } + .frame(height: size) + } + + func updateColor() { + color = Color(hue: hue, saturation: saturation, brightness: brightness).opacity(0.7 * alpha + 0.3) + } +} diff --git a/Memola/Config/Info.plist b/Memola/Config/Info.plist new file mode 100644 index 0000000..11d6956 --- /dev/null +++ b/Memola/Config/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleShortVersionString + $(MARKETING_VERSION) + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + UILaunchScreen + + UILaunchScreen + + + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleExecutable + $(EXECUTABLE_NAME) + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + + UIApplicationSupportsIndirectInputEvents + + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + UISupportedInterfaceOrientations~iphone + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CFBundleInfoDictionaryVersion + 6.0 + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleName + $(PRODUCT_NAME) + + diff --git a/Memola/Extensions/Color++.swift b/Memola/Extensions/Color++.swift index b0e9aa6..332db55 100644 --- a/Memola/Extensions/Color++.swift +++ b/Memola/Extensions/Color++.swift @@ -18,6 +18,18 @@ extension Color { } } +extension Color { + var hsba: (hue: Double, saturation: Double, brightness: Double, alpha: Double) { + let uiColor = UIColor(self) + var hue: CGFloat = 0 + var saturation: CGFloat = 0 + var brightness: CGFloat = 0 + var alpha: CGFloat = 0 + uiColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) + return (hue, saturation, brightness, alpha) + } +} + extension UIColor { var components: [CGFloat] { let uiColor: UIColor = self diff --git a/Memola/Features/Memo/MemoView.swift b/Memola/Features/Memo/Memo/MemoView.swift similarity index 85% rename from Memola/Features/Memo/MemoView.swift rename to Memola/Features/Memo/Memo/MemoView.swift index c6e2277..53c2591 100644 --- a/Memola/Features/Memo/MemoView.swift +++ b/Memola/Features/Memo/Memo/MemoView.swift @@ -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,25 +19,28 @@ 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)) } var body: some View { CanvasView() .ignoresSafeArea() - .overlay(alignment: .bottomTrailing) { - PenToolView() - .padding() - } .overlay(alignment: .topTrailing) { historyTool .padding() } + .overlay(alignment: .trailing) { + PenDockView() + .frame(maxHeight: .infinity) + .padding() + } .overlay(alignment: .topLeading) { Button { closeMemo() } label: { Image(systemName: "xmark") + .contentShape(.circle) .padding(15) .background(.regularMaterial) .clipShape(RoundedRectangle(cornerRadius: 20)) @@ -68,6 +70,7 @@ struct MemoView: View { history.historyPublisher.send(.undo) } label: { Image(systemName: "arrow.uturn.backward.circle") + .contentShape(.circle) } .hoverEffect(.lift) .disabled(history.undoDisabled) @@ -75,6 +78,7 @@ struct MemoView: View { history.historyPublisher.send(.redo) } label: { Image(systemName: "arrow.uturn.forward.circle") + .contentShape(.circle) } .hoverEffect(.lift) .disabled(history.redoDisabled) @@ -95,13 +99,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() } diff --git a/Memola/Features/Memo/PenDock/PenDockView.swift b/Memola/Features/Memo/PenDock/PenDockView.swift new file mode 100644 index 0000000..eeace76 --- /dev/null +++ b/Memola/Features/Memo/PenDock/PenDockView.swift @@ -0,0 +1,297 @@ +// +// PenDockView.swift +// Memola +// +// Created by Dscyre Scotti on 5/4/24. +// + +import SwiftUI + +struct PenDockView: View { + @EnvironmentObject var tool: Tool + + let width: CGFloat = 90 + let height: CGFloat = 30 + let factor: CGFloat = 0.95 + + @State var refreshScrollId: UUID = UUID() + @State var opensColorPicker: Bool = false + + var body: some View { + VStack(alignment: .trailing) { + if let pen = tool.selectedPen { + VStack(spacing: 5) { + penColorView(pen) + penThicknessView(pen) + } + .padding(10) + .frame(width: width * factor - 18) + .background { + RoundedRectangle(cornerRadius: 20) + .fill(.regularMaterial) + } + .transition(.move(edge: .trailing).combined(with: .opacity)) + } else { + Color.clear + .frame(width: width * factor - 18, height: 50) + } + penScrollView + } + .fixedSize() + } + + var penScrollView: some View { + ScrollViewReader { proxy in + ScrollView(.vertical, showsIndicators: false) { + LazyVStack(spacing: 0) { + ForEach(tool.pens) { pen in + penView(pen) + .id(pen.id) + .scrollTransition { content, phase in + content + .scaleEffect(phase.isIdentity ? 1 : 0.04, anchor: .trailing) + } + } + } + .padding(.vertical, 10) + .padding(.leading, 40) + .id(refreshScrollId) + } + .onReceive(tool.scrollPublisher) { id in + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + withAnimation { + proxy.scrollTo(id) + } + } + } + } + .frame(maxHeight:( (height * factor + 10) * 6) + 20) + .fixedSize() + .background(alignment: .trailing) { + RoundedRectangle(cornerRadius: 20) + .fill(.regularMaterial) + .frame(width: width * factor - 18) + } + .clipShape(.rect(cornerRadii: .init(bottomTrailing: 20, topTrailing: 20))) + .overlay(alignment: .bottomLeading) { + newPenButton + .offset(x: 60, y: 10) + } + } + + func penView(_ pen: Pen) -> some View { + ZStack { + penShadowView(pen) + if let tip = pen.style.icon.tip { + Image(tip) + .resizable() + .renderingMode(.template) + .foregroundStyle(Color.rgba(from: pen.rgba)) + } + Image(pen.style.icon.base) + .resizable() + } + .frame(width: width * factor, height: height * factor) + .padding(.vertical, 5) + .contentShape(.rect(cornerRadii: .init(topLeading: 10, bottomLeading: 10))) + .onTapGesture { + if tool.selectedPen === pen { + tool.unselectPen(pen) + } else { + tool.selectPen(pen) + } + } + .padding(.leading, 10) + .contextMenu(if: pen.strokeStyle != .eraser) { + ControlGroup { + Button { + tool.selectPen(pen) + } label: { + Label( + title: { Text("Select") }, + icon: { Image(systemName: "pencil.tip.crop.circle") } + ) + } + Button { + let originalPen = pen + let pen = PenObject.createObject(\.viewContext, penStyle: originalPen.style) + pen.color = originalPen.rgba + pen.thickness = originalPen.thickness + pen.isSelected = true + pen.tool = tool.object + let _pen = Pen(object: pen) + tool.duplicatePen(_pen, of: originalPen) + } label: { + Label( + title: { Text("Duplicate") }, + icon: { Image(systemName: "plus.square.on.square") } + ) + } + Button(role: .destructive) { + tool.removePen(pen) + } label: { + Label( + title: { Text("Remove") }, + icon: { Image(systemName: "trash") } + ) + } + .disabled(tool.markers.count <= 1) + } + .controlGroupStyle(.menu) + } preview: { + penPreviewView(pen) + .drawingGroup() + .contentShape(.contextMenuPreview, .rect(cornerRadius: 10)) + } + .onDrag(if: pen.strokeStyle != .eraser) { + tool.draggedPen = pen + return NSItemProvider(contentsOf: URL(string: pen.id)) ?? NSItemProvider() + } preview: { + penPreviewView(pen) + .contentShape(.dragPreview, .rect(cornerRadius: 10)) + } + .onDrop(of: [.item], delegate: PenDropDelegate(id: pen.id, tool: tool, action: { refreshScrollId = UUID() })) + .offset(x: tool.selectedPen === pen ? 0 : 25) + } + + func penColorView(_ pen: Pen) -> some View { + Button { + opensColorPicker = true + } label: { + let hsba = pen.color.hsba + let baseColor = Color(hue: hsba.hue, saturation: hsba.saturation, brightness: hsba.brightness) + GeometryReader { proxy in + HStack(spacing: 0) { + baseColor + .frame(width: proxy.size.width / 2) + Image("transparent-grid-square") + .resizable() + .scaleEffect(3) + .aspectRatio(contentMode: .fill) + .opacity(1 - hsba.alpha) + .frame(width: proxy.size.width / 2) + .clipped() + } + } + .background(baseColor) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .frame(height: 28) + .overlay { + RoundedRectangle(cornerRadius: 10) + .stroke(Color.gray, lineWidth: 0.4) + } + .padding(0.2) + .padding(.top, 4) + .drawingGroup() + } + .hoverEffect(.lift) + .popover(isPresented: $opensColorPicker) { + let color = Binding( + get: { pen.color }, + set: { + pen.color = $0 + tool.objectWillChange.send() + } + ) + ColorPicker(color: color) + .presentationCompactAdaptation(.popover) + } + } + + @ViewBuilder + func penThicknessView(_ pen: Pen) -> some View { + let minimum: CGFloat = pen.style.thickness.min + let maximum: CGFloat = pen.style.thickness.max + let start: CGFloat = 5 + let end: CGFloat = 15 + let selection = Binding( + get: { pen.thickness }, + set: { + pen.thickness = $0 + tool.objectWillChange.send() + } + ) + Picker("", selection: selection) { + ForEach(pen.style.thicknessSteps, id: \.self) { step in + let size = ((step - minimum) * (end - start) / (maximum - minimum)) + start - (1 / step) + if pen.thickness == step { + Circle() + .fill(.black) + .frame(width: size, height: size) + } else { + Circle() + .stroke(Color.black, lineWidth: 1) + .frame(width: size, height: size) + } + } + } + .pickerStyle(.wheel) + .frame(width: width * factor - 18, height: 40) + } + + var newPenButton: some View { + Button { + let pen = PenObject.createObject(\.viewContext, penStyle: .marker) + if let color = (tool.selectedPen ?? tool.pens.last)?.rgba { + pen.color = color + } + pen.isSelected = true + pen.tool = tool.object + pen.orderIndex = Int16(tool.pens.count) + let _pen = Pen(object: pen) + tool.addPen(_pen) + } label: { + Image(systemName: "plus.circle.fill") + .font(.title2) + .padding(1) + .contentShape(.circle) + .background { + Circle() + .fill(.white) + } + } + .foregroundStyle(.green) + .hoverEffect(.lift) + } + + func penPreviewView(_ pen: Pen) -> some View { + ZStack { + if let tip = pen.style.icon.tip { + Image(tip) + .resizable() + .renderingMode(.template) + .foregroundStyle(Color.rgba(from: pen.rgba)) + } + Image(pen.style.icon.base) + .resizable() + } + .frame(width: width * factor, height: height * factor) + .padding(.vertical, 5) + .padding(.leading, 10) + } + + func penShadowView(_ pen: Pen) -> some View { + ZStack { + Group { + if let tip = pen.style.icon.tip { + Image(tip) + .resizable() + .renderingMode(.template) + } + Image(pen.style.icon.base) + .resizable() + .renderingMode(.template) + } + .drawingGroup() + .foregroundStyle(.black.opacity(0.2)) + .blur(radius: 3) + if let tip = pen.style.icon.tip { + Image(tip) + .resizable() + .renderingMode(.template) + .foregroundStyle(Color(red: pen.rgba[0], green: pen.rgba[1], blue: pen.rgba[2])) + .blur(radius: 0.5) + } + } + } +} diff --git a/Memola/Features/Memo/PenDock/PenDropDelegate.swift b/Memola/Features/Memo/PenDock/PenDropDelegate.swift new file mode 100644 index 0000000..7d91630 --- /dev/null +++ b/Memola/Features/Memo/PenDock/PenDropDelegate.swift @@ -0,0 +1,40 @@ +// +// PenDropDelegate.swift +// Memola +// +// Created by Dscyre Scotti on 5/16/24. +// + +import SwiftUI +import Foundation + +struct PenDropDelegate: DropDelegate { + let id: String + @ObservedObject var tool: Tool + let action: () -> Void + + func performDrop(info: DropInfo) -> Bool { + tool.draggedPen = nil + action() + return true + } + + func dropEntered(info: DropInfo) { + guard let draggedPen = tool.draggedPen else { return } + if draggedPen.id != id { + let fromIndex = tool.pens.firstIndex(where: { $0.id == draggedPen.id })! + let toIndex = tool.pens.firstIndex(where: { $0.id == id })! + guard tool.pens[toIndex].strokeStyle != .eraser else { return } + withAnimation { + 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) + } + try context.saveIfNeeded() + } + } + } +} diff --git a/Memola/Features/Memo/PenToolView.swift b/Memola/Features/Memo/PenToolView.swift deleted file mode 100644 index 4bb1075..0000000 --- a/Memola/Features/Memo/PenToolView.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// PenToolView.swift -// Memola -// -// Created by Dscyre Scotti on 5/4/24. -// - -import SwiftUI - -struct PenToolView: View { - @EnvironmentObject var tool: Tool - - var body: some View { - VStack { - if let pen = tool.selectedPen { - let thicknessBounds = pen.style.thinkness - let thickness = Binding { - max(pen.thickness, pen.style.thinkness.min) - } set: { newValue in - tool.selectedPen?.thickness = newValue - } - let color = Binding { - Color.rgba(from: pen.color) - } set: { newValue in - tool.selectedPen?.color = newValue.components - tool.objectWillChange.send() - } - HStack { - ColorPicker("", selection: color) - .frame(width: 40, height: 40) - Slider(value: thickness, in: thicknessBounds.min...thicknessBounds.max) - .frame(width: 180, height: 40) - } - } - HStack { - ForEach(tool.pens) { pen in - penView(pen) - .overlay(alignment: .bottom) { - if tool.selectedPen === pen { - Circle() - .frame(width: 5, height: 5) - .offset(y: 7.5) - .foregroundStyle(Color.rgba(from: pen.color)) - } - } - } - } - .padding(15) - .background(.regularMaterial) - .clipShape(RoundedRectangle(cornerRadius: 20)) - } - } - - @ViewBuilder - func penView(_ pen: Pen) -> some View { - Button { - if tool.selectedPen === pen { - tool.selectedPen = nil - } else { - tool.changePen(pen) - } - } label: { - ZStack { - if let tip = pen.style.icon.tip { - Image(tip) - .resizable() - .renderingMode(.template) - .foregroundStyle(Color.rgba(from: pen.color)) - } - Image(pen.style.icon.base) - .resizable() - } - .frame(width: 30, height: 65) - .drawingGroup() - .hoverEffect(.lift) - } - .buttonStyle(.plain) - } -} - - - diff --git a/Memola/Features/Memos/MemosView.swift b/Memola/Features/Memos/MemosView.swift index 57c6395..4f66f0d 100644 --- a/Memola/Features/Memos/MemosView.swift +++ b/Memola/Features/Memos/MemosView.swift @@ -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) + } } } diff --git a/Memola/Persistence/Objects/MemoObject.swift b/Memola/Persistence/Objects/MemoObject.swift index d3a6b61..ad74aa9 100644 --- a/Memola/Persistence/Objects/MemoObject.swift +++ b/Memola/Persistence/Objects/MemoObject.swift @@ -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 } diff --git a/Memola/Persistence/Objects/PenObject.swift b/Memola/Persistence/Objects/PenObject.swift new file mode 100644 index 0000000..054edf4 --- /dev/null +++ b/Memola/Persistence/Objects/PenObject.swift @@ -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, 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.thickness.min + return object + } +} diff --git a/Memola/Persistence/Objects/ToolObject.swift b/Memola/Persistence/Objects/ToolObject.swift new file mode 100644 index 0000000..fb26c1a --- /dev/null +++ b/Memola/Persistence/Objects/ToolObject.swift @@ -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? +} diff --git a/Memola/Resources/Assets/Assets.xcassets/backgrounds/Contents.json b/Memola/Resources/Assets/Assets.xcassets/backgrounds/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Memola/Resources/Assets/Assets.xcassets/backgrounds/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/Contents.json b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/Contents.json new file mode 100644 index 0000000..a8d92bf --- /dev/null +++ b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "transparency_grid 1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "transparency_grid 1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "transparency_grid 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/transparency_grid 1.png b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/transparency_grid 1.png new file mode 100644 index 0000000..a3e4149 Binary files /dev/null and b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/transparency_grid 1.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/transparency_grid 1@2x.png b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/transparency_grid 1@2x.png new file mode 100644 index 0000000..af558fb Binary files /dev/null and b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/transparency_grid 1@2x.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/transparency_grid 1@3x.png b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/transparency_grid 1@3x.png new file mode 100644 index 0000000..948bb49 Binary files /dev/null and b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/transparency_grid 1@3x.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-square.imageset/Contents.json b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-square.imageset/Contents.json new file mode 100644 index 0000000..a8d92bf --- /dev/null +++ b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-square.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "transparency_grid 1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "transparency_grid 1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "transparency_grid 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-square.imageset/transparency_grid 1.png b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-square.imageset/transparency_grid 1.png new file mode 100644 index 0000000..b340c38 Binary files /dev/null and b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-square.imageset/transparency_grid 1.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-square.imageset/transparency_grid 1@2x.png b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-square.imageset/transparency_grid 1@2x.png new file mode 100644 index 0000000..c060271 Binary files /dev/null and b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-square.imageset/transparency_grid 1@2x.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-square.imageset/transparency_grid 1@3x.png b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-square.imageset/transparency_grid 1@3x.png new file mode 100644 index 0000000..27938df Binary files /dev/null and b/Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-square.imageset/transparency_grid 1@3x.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/eraser/eraser.imageset/eraser.png b/Memola/Resources/Assets/Assets.xcassets/graphics/eraser/eraser.imageset/eraser.png index efe860c..cfaade7 100644 Binary files a/Memola/Resources/Assets/Assets.xcassets/graphics/eraser/eraser.imageset/eraser.png and b/Memola/Resources/Assets/Assets.xcassets/graphics/eraser/eraser.imageset/eraser.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/eraser/eraser.imageset/eraser@2x.png b/Memola/Resources/Assets/Assets.xcassets/graphics/eraser/eraser.imageset/eraser@2x.png index 0f7d179..560f6ae 100644 Binary files a/Memola/Resources/Assets/Assets.xcassets/graphics/eraser/eraser.imageset/eraser@2x.png and b/Memola/Resources/Assets/Assets.xcassets/graphics/eraser/eraser.imageset/eraser@2x.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/eraser/eraser.imageset/eraser@3x.png b/Memola/Resources/Assets/Assets.xcassets/graphics/eraser/eraser.imageset/eraser@3x.png index 464763a..481f3ec 100644 Binary files a/Memola/Resources/Assets/Assets.xcassets/graphics/eraser/eraser.imageset/eraser@3x.png and b/Memola/Resources/Assets/Assets.xcassets/graphics/eraser/eraser.imageset/eraser@3x.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/Contents.json b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/Contents.json index 2734a89..d7ffdfb 100644 --- a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/Contents.json +++ b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/Contents.json @@ -1,17 +1,17 @@ { "images" : [ { - "filename" : "bullet-base.png", + "filename" : "marker-base.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "bullet-base@2x.png", + "filename" : "marker-base@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "bullet-base@3x.png", + "filename" : "marker-base@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/bullet-base.png b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/bullet-base.png deleted file mode 100644 index e14b91e..0000000 Binary files a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/bullet-base.png and /dev/null differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/bullet-base@2x.png b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/bullet-base@2x.png deleted file mode 100644 index c21a5a9..0000000 Binary files a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/bullet-base@2x.png and /dev/null differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/bullet-base@3x.png b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/bullet-base@3x.png deleted file mode 100644 index 2146f61..0000000 Binary files a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/bullet-base@3x.png and /dev/null differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/marker-base.png b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/marker-base.png new file mode 100644 index 0000000..1477b48 Binary files /dev/null and b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/marker-base.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/marker-base@2x.png b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/marker-base@2x.png new file mode 100644 index 0000000..3a7ee0d Binary files /dev/null and b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/marker-base@2x.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/marker-base@3x.png b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/marker-base@3x.png new file mode 100644 index 0000000..902a9b5 Binary files /dev/null and b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/marker-base@3x.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/Contents.json b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/Contents.json index 6c3c92a..69f40d1 100644 --- a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/Contents.json +++ b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/Contents.json @@ -1,17 +1,17 @@ { "images" : [ { - "filename" : "bullet-tip.png", + "filename" : "marker-tip.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "bullet-tip@2x.png", + "filename" : "marker-tip@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "bullet-tip@3x.png", + "filename" : "marker-tip@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/bullet-tip.png b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/bullet-tip.png deleted file mode 100644 index fefdcfc..0000000 Binary files a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/bullet-tip.png and /dev/null differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/bullet-tip@2x.png b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/bullet-tip@2x.png deleted file mode 100644 index 6175f55..0000000 Binary files a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/bullet-tip@2x.png and /dev/null differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/bullet-tip@3x.png b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/bullet-tip@3x.png deleted file mode 100644 index 6cf4a48..0000000 Binary files a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/bullet-tip@3x.png and /dev/null differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/marker-tip.png b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/marker-tip.png new file mode 100644 index 0000000..d0dbe86 Binary files /dev/null and b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/marker-tip.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/marker-tip@2x.png b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/marker-tip@2x.png new file mode 100644 index 0000000..ecfa7fb Binary files /dev/null and b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/marker-tip@2x.png differ diff --git a/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/marker-tip@3x.png b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/marker-tip@3x.png new file mode 100644 index 0000000..1132ea1 Binary files /dev/null and b/Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/marker-tip@3x.png differ diff --git a/Memola/Resources/Models/MemolaModel.xcdatamodeld/MemolaModel.xcdatamodel/contents b/Memola/Resources/Models/MemolaModel.xcdatamodeld/MemolaModel.xcdatamodel/contents index 1b277fa..798206c 100644 --- a/Memola/Resources/Models/MemolaModel.xcdatamodeld/MemolaModel.xcdatamodel/contents +++ b/Memola/Resources/Models/MemolaModel.xcdatamodeld/MemolaModel.xcdatamodel/contents @@ -15,6 +15,15 @@ + + + + + + + + + @@ -34,4 +43,8 @@ + + + + \ No newline at end of file