mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-07-10 06:42:43 +02:00
Merge pull request #29 from dscyrescotti/feature/memo-canvas-redesign
Redesign memo canvas view
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
EC0D14242BF79C98009BFE5F /* MemolaModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14222BF79C98009BFE5F /* MemolaModel.xcdatamodeld */; };
|
EC0D14242BF79C98009BFE5F /* MemolaModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14222BF79C98009BFE5F /* MemolaModel.xcdatamodeld */; };
|
||||||
EC0D14262BF7A8C9009BFE5F /* PenObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14252BF7A8C9009BFE5F /* PenObject.swift */; };
|
EC0D14262BF7A8C9009BFE5F /* PenObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14252BF7A8C9009BFE5F /* PenObject.swift */; };
|
||||||
EC0D14282BF7BF20009BFE5F /* ContextMenuViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.swift */; };
|
EC0D14282BF7BF20009BFE5F /* ContextMenuViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.swift */; };
|
||||||
|
EC1B783D2BFA0AC9005A34E2 /* Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1B783C2BFA0AC9005A34E2 /* Toolbar.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 */; };
|
||||||
@@ -74,7 +75,7 @@
|
|||||||
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 */; };
|
||||||
ECA739082BE623F300A4542E /* PenDockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA739072BE623F300A4542E /* PenDockView.swift */; };
|
ECA739082BE623F300A4542E /* PenDock.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA739072BE623F300A4542E /* PenDock.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 */; };
|
||||||
ECFA15222BEF21F500455818 /* CanvasObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA15212BEF21F500455818 /* CanvasObject.swift */; };
|
ECFA15222BEF21F500455818 /* CanvasObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA15212BEF21F500455818 /* CanvasObject.swift */; };
|
||||||
@@ -89,6 +90,7 @@
|
|||||||
EC0D14232BF79C98009BFE5F /* MemolaModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MemolaModel.xcdatamodel; 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>"; };
|
EC0D14252BF7A8C9009BFE5F /* PenObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenObject.swift; sourceTree = "<group>"; };
|
||||||
EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuViewModifier.swift; sourceTree = "<group>"; };
|
EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuViewModifier.swift; sourceTree = "<group>"; };
|
||||||
|
EC1B783C2BFA0AC9005A34E2 /* Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toolbar.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>"; };
|
||||||
@@ -154,7 +156,7 @@
|
|||||||
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>"; };
|
||||||
ECA739072BE623F300A4542E /* PenDockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenDockView.swift; sourceTree = "<group>"; };
|
ECA739072BE623F300A4542E /* PenDock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenDock.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>"; };
|
||||||
ECFA15212BEF21F500455818 /* CanvasObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CanvasObject.swift; sourceTree = "<group>"; };
|
ECFA15212BEF21F500455818 /* CanvasObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CanvasObject.swift; sourceTree = "<group>"; };
|
||||||
@@ -199,6 +201,14 @@
|
|||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
EC1B783B2BFA0AAC005A34E2 /* Toolbar */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EC1B783C2BFA0AC9005A34E2 /* Toolbar.swift */,
|
||||||
|
);
|
||||||
|
path = Toolbar;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
EC5050042BF65CBC00B4D86E /* Core */ = {
|
EC5050042BF65CBC00B4D86E /* Core */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -210,7 +220,7 @@
|
|||||||
EC5050052BF65CCD00B4D86E /* PenDock */ = {
|
EC5050052BF65CCD00B4D86E /* PenDock */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
ECA739072BE623F300A4542E /* PenDockView.swift */,
|
ECA739072BE623F300A4542E /* PenDock.swift */,
|
||||||
EC5050062BF65CED00B4D86E /* PenDropDelegate.swift */,
|
EC5050062BF65CED00B4D86E /* PenDropDelegate.swift */,
|
||||||
);
|
);
|
||||||
path = PenDock;
|
path = PenDock;
|
||||||
@@ -326,6 +336,7 @@
|
|||||||
ECA7387B2BE5EF3500A4542E /* Memo */ = {
|
ECA7387B2BE5EF3500A4542E /* Memo */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
EC1B783B2BFA0AAC005A34E2 /* Toolbar */,
|
||||||
EC5050082BF65D0500B4D86E /* Memo */,
|
EC5050082BF65D0500B4D86E /* Memo */,
|
||||||
EC5050052BF65CCD00B4D86E /* PenDock */,
|
EC5050052BF65CCD00B4D86E /* PenDock */,
|
||||||
);
|
);
|
||||||
@@ -634,6 +645,8 @@
|
|||||||
dependencies = (
|
dependencies = (
|
||||||
);
|
);
|
||||||
name = Memola;
|
name = Memola;
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
productName = Memola;
|
productName = Memola;
|
||||||
productReference = EC7F6BE82BE5E6E300A34A7B /* Memola.app */;
|
productReference = EC7F6BE82BE5E6E300A34A7B /* Memola.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
@@ -662,6 +675,8 @@
|
|||||||
Base,
|
Base,
|
||||||
);
|
);
|
||||||
mainGroup = EC7F6BDF2BE5E6E300A34A7B;
|
mainGroup = EC7F6BDF2BE5E6E300A34A7B;
|
||||||
|
packageReferences = (
|
||||||
|
);
|
||||||
productRefGroup = EC7F6BE92BE5E6E300A34A7B /* Products */;
|
productRefGroup = EC7F6BE92BE5E6E300A34A7B /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
@@ -691,6 +706,7 @@
|
|||||||
ECA738B02BE60D0B00A4542E /* CanvasViewController.swift in Sources */,
|
ECA738B02BE60D0B00A4542E /* CanvasViewController.swift in Sources */,
|
||||||
ECA738E42BE6110800A4542E /* Drawable.swift in Sources */,
|
ECA738E42BE6110800A4542E /* Drawable.swift in Sources */,
|
||||||
ECA738AD2BE60CC600A4542E /* DrawingView.swift in Sources */,
|
ECA738AD2BE60CC600A4542E /* DrawingView.swift in Sources */,
|
||||||
|
EC1B783D2BFA0AC9005A34E2 /* Toolbar.swift in Sources */,
|
||||||
ECA738E02BE610B900A4542E /* EraserRenderPass.swift in Sources */,
|
ECA738E02BE610B900A4542E /* EraserRenderPass.swift in Sources */,
|
||||||
EC35655A2BF060D900A4E0BF /* Quad.metal in Sources */,
|
EC35655A2BF060D900A4E0BF /* Quad.metal in Sources */,
|
||||||
ECA738912BE600F500A4542E /* Cache.metal in Sources */,
|
ECA738912BE600F500A4542E /* Cache.metal in Sources */,
|
||||||
@@ -719,7 +735,7 @@
|
|||||||
ECA738862BE5FF2500A4542E /* Canvas.swift in Sources */,
|
ECA738862BE5FF2500A4542E /* Canvas.swift in Sources */,
|
||||||
ECA738882BE5FF4400A4542E /* Renderer.swift in Sources */,
|
ECA738882BE5FF4400A4542E /* Renderer.swift in Sources */,
|
||||||
ECA738D42BE60F9100A4542E /* StrokeGenerator.swift in Sources */,
|
ECA738D42BE60F9100A4542E /* StrokeGenerator.swift in Sources */,
|
||||||
ECA739082BE623F300A4542E /* PenDockView.swift in Sources */,
|
ECA739082BE623F300A4542E /* PenDock.swift in Sources */,
|
||||||
ECA738CB2BE60F1900A4542E /* ViewPortContext.swift in Sources */,
|
ECA738CB2BE60F1900A4542E /* ViewPortContext.swift in Sources */,
|
||||||
ECA738EE2BE6125D00A4542E /* simd_float4x4++.swift in Sources */,
|
ECA738EE2BE6125D00A4542E /* simd_float4x4++.swift in Sources */,
|
||||||
ECA7388C2BE6009600A4542E /* Textures.swift in Sources */,
|
ECA7388C2BE6009600A4542E /* Textures.swift in Sources */,
|
||||||
|
|||||||
@@ -24,17 +24,20 @@ final class Canvas: ObservableObject, Identifiable, @unchecked Sendable {
|
|||||||
|
|
||||||
var transform: simd_float4x4 = .init()
|
var transform: simd_float4x4 = .init()
|
||||||
var clipBounds: CGRect = .zero
|
var clipBounds: CGRect = .zero
|
||||||
var zoomScale: CGFloat = .zero
|
|
||||||
var bounds: CGRect = .zero
|
var bounds: CGRect = .zero
|
||||||
var uniformsBuffer: MTLBuffer?
|
var uniformsBuffer: MTLBuffer?
|
||||||
|
|
||||||
|
@Published var state: State = .initial
|
||||||
|
@Published var zoomScale: CGFloat = .zero
|
||||||
|
@Published var locksCanvas: Bool = false
|
||||||
|
|
||||||
|
let zoomPublisher = PassthroughSubject<CGFloat, Never>()
|
||||||
|
|
||||||
init(size: CGSize, canvasID: NSManagedObjectID) {
|
init(size: CGSize, canvasID: NSManagedObjectID) {
|
||||||
self.size = size
|
self.size = size
|
||||||
self.canvasID = canvasID
|
self.canvasID = canvasID
|
||||||
}
|
}
|
||||||
|
|
||||||
@Published var state: State = .initial
|
|
||||||
|
|
||||||
var hasValidStroke: Bool {
|
var hasValidStroke: Bool {
|
||||||
if let currentStroke = graphicContext.currentStroke {
|
if let currentStroke = graphicContext.currentStroke {
|
||||||
return Date.now.timeIntervalSince(currentStroke.createdAt) * 1000 > 80
|
return Date.now.timeIntervalSince(currentStroke.createdAt) * 1000 > 80
|
||||||
@@ -92,7 +95,10 @@ extension Canvas {
|
|||||||
// MARK: - Zoom Scale
|
// MARK: - Zoom Scale
|
||||||
extension Canvas {
|
extension Canvas {
|
||||||
func setZoomScale(_ zoomScale: CGFloat) {
|
func setZoomScale(_ zoomScale: CGFloat) {
|
||||||
self.zoomScale = zoomScale
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
guard let self else { return }
|
||||||
|
self.zoomScale = min(max(zoomScale, minimumZoomScale), maximumZoomScale)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -162,6 +162,18 @@ extension CanvasViewController {
|
|||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
canvas.zoomPublisher
|
||||||
|
.sink { [weak self] zoomScale in
|
||||||
|
self?.zoomChanged(zoomScale)
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
canvas.$locksCanvas
|
||||||
|
.sink { [weak self] state in
|
||||||
|
self?.lockModeChanged(state)
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
tool.$selectedPen
|
tool.$selectedPen
|
||||||
.sink { [weak self] pen in
|
.sink { [weak self] pen in
|
||||||
self?.penChanged(to: pen)
|
self?.penChanged(to: pen)
|
||||||
@@ -298,6 +310,17 @@ extension CanvasViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension CanvasViewController {
|
||||||
|
func zoomChanged(_ zoomScale: CGFloat) {
|
||||||
|
scrollView.setZoomScale(zoomScale, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lockModeChanged(_ state: Bool) {
|
||||||
|
scrollView.isScrollEnabled = !state
|
||||||
|
scrollView.pinchGestureRecognizer?.isEnabled = !state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension CanvasViewController {
|
extension CanvasViewController {
|
||||||
func historyUndid() {
|
func historyUndid() {
|
||||||
guard history.undo() else { return }
|
guard history.undo() else { return }
|
||||||
|
|||||||
@@ -9,16 +9,19 @@ import SwiftUI
|
|||||||
import CoreData
|
import CoreData
|
||||||
|
|
||||||
struct MemoView: View {
|
struct MemoView: View {
|
||||||
@Environment(\.dismiss) var dismiss
|
|
||||||
|
|
||||||
@StateObject var tool: Tool
|
@StateObject var tool: Tool
|
||||||
@StateObject var canvas: Canvas
|
@StateObject var canvas: Canvas
|
||||||
@StateObject var history = History()
|
@StateObject var history = History()
|
||||||
|
|
||||||
let memo: MemoObject
|
@State var memo: MemoObject
|
||||||
|
@State var title: String
|
||||||
|
@FocusState var textFieldState: Bool
|
||||||
|
|
||||||
|
let size: CGFloat = 32
|
||||||
|
|
||||||
init(memo: MemoObject) {
|
init(memo: MemoObject) {
|
||||||
self.memo = memo
|
self.memo = memo
|
||||||
|
self.title = memo.title
|
||||||
self._tool = StateObject(wrappedValue: Tool(object: memo.tool))
|
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))
|
||||||
}
|
}
|
||||||
@@ -26,35 +29,23 @@ struct MemoView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
CanvasView()
|
CanvasView()
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
.overlay(alignment: .topTrailing) {
|
|
||||||
historyTool
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
.overlay(alignment: .trailing) {
|
.overlay(alignment: .trailing) {
|
||||||
PenDockView()
|
PenDock()
|
||||||
.frame(maxHeight: .infinity)
|
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
.overlay(alignment: .topLeading) {
|
.overlay(alignment: .bottomLeading) {
|
||||||
Button {
|
zoomControl
|
||||||
closeMemo()
|
}
|
||||||
} label: {
|
.disabled(textFieldState)
|
||||||
Image(systemName: "xmark")
|
.overlay(alignment: .top) {
|
||||||
.contentShape(.circle)
|
Toolbar(memo: memo, size: size)
|
||||||
.padding(15)
|
|
||||||
.background(.regularMaterial)
|
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
|
||||||
}
|
|
||||||
.hoverEffect(.lift)
|
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
.disabled(canvas.state == .loading || canvas.state == .closing)
|
.disabled(canvas.state == .loading || canvas.state == .closing)
|
||||||
.overlay {
|
.overlay {
|
||||||
switch canvas.state {
|
switch canvas.state {
|
||||||
case .loading:
|
case .loading:
|
||||||
progressView("Loading memo...")
|
loadingIndicator("Loading memo...")
|
||||||
case .closing:
|
case .closing:
|
||||||
progressView("Saving memo...")
|
loadingIndicator("Saving memo...")
|
||||||
default:
|
default:
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
@@ -64,31 +55,44 @@ struct MemoView: View {
|
|||||||
.environmentObject(history)
|
.environmentObject(history)
|
||||||
}
|
}
|
||||||
|
|
||||||
var historyTool: some View {
|
@ViewBuilder
|
||||||
HStack {
|
var zoomControl: some View {
|
||||||
Button {
|
let upperBound: CGFloat = 400
|
||||||
history.historyPublisher.send(.undo)
|
let lowerBound: CGFloat = 10
|
||||||
|
let zoomScale: CGFloat = (((canvas.zoomScale - canvas.minimumZoomScale) * (upperBound - lowerBound) / (canvas.maximumZoomScale - canvas.minimumZoomScale)) + lowerBound).rounded()
|
||||||
|
let zoomScales: [Int] = [400, 200, 100, 75, 50, 25, 10]
|
||||||
|
if !canvas.locksCanvas {
|
||||||
|
Menu {
|
||||||
|
ForEach(zoomScales, id: \.self) { scale in
|
||||||
|
Button {
|
||||||
|
let zoomScale = ((CGFloat(scale) - lowerBound) * (canvas.maximumZoomScale - canvas.minimumZoomScale) / (upperBound - lowerBound)) + canvas.minimumZoomScale
|
||||||
|
canvas.zoomPublisher.send(zoomScale)
|
||||||
|
} label: {
|
||||||
|
Label {
|
||||||
|
Text(scale, format: .percent)
|
||||||
|
} icon: {
|
||||||
|
if CGFloat(scale) == zoomScale {
|
||||||
|
Image(systemName: "checkmark")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.font(.headline)
|
||||||
|
}
|
||||||
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "arrow.uturn.backward.circle")
|
Text(zoomScale / 100, format: .percent)
|
||||||
.contentShape(.circle)
|
.frame(width: 45)
|
||||||
|
.font(.subheadline)
|
||||||
|
.padding(.horizontal, size / 2.5)
|
||||||
|
.frame(height: size)
|
||||||
|
.background(.regularMaterial)
|
||||||
|
.clipShape(.rect(cornerRadius: 8))
|
||||||
|
.padding(10)
|
||||||
}
|
}
|
||||||
.hoverEffect(.lift)
|
.transition(.move(edge: .bottom).combined(with: .blurReplace))
|
||||||
.disabled(history.undoDisabled)
|
|
||||||
Button {
|
|
||||||
history.historyPublisher.send(.redo)
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "arrow.uturn.forward.circle")
|
|
||||||
.contentShape(.circle)
|
|
||||||
}
|
|
||||||
.hoverEffect(.lift)
|
|
||||||
.disabled(history.redoDisabled)
|
|
||||||
}
|
}
|
||||||
.padding(15)
|
|
||||||
.background(.regularMaterial)
|
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func progressView(_ title: String) -> some View {
|
func loadingIndicator(_ title: String) -> some View {
|
||||||
ProgressView {
|
ProgressView {
|
||||||
Text(title)
|
Text(title)
|
||||||
}
|
}
|
||||||
@@ -97,11 +101,4 @@ struct MemoView: View {
|
|||||||
.background(.regularMaterial)
|
.background(.regularMaterial)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
.clipShape(RoundedRectangle(cornerRadius: 20))
|
||||||
}
|
}
|
||||||
|
|
||||||
func closeMemo() {
|
|
||||||
withPersistenceSync(\.viewContext) { context in
|
|
||||||
try context.saveIfNeeded()
|
|
||||||
}
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+68
-50
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// PenDockView.swift
|
// PenDock.swift
|
||||||
// Memola
|
// Memola
|
||||||
//
|
//
|
||||||
// Created by Dscyre Scotti on 5/4/24.
|
// Created by Dscyre Scotti on 5/4/24.
|
||||||
@@ -7,45 +7,36 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct PenDockView: View {
|
struct PenDock: View {
|
||||||
@EnvironmentObject var tool: Tool
|
@EnvironmentObject var tool: Tool
|
||||||
|
@EnvironmentObject var canvas: Canvas
|
||||||
|
|
||||||
let width: CGFloat = 90
|
let width: CGFloat = 90
|
||||||
let height: CGFloat = 30
|
let height: CGFloat = 30
|
||||||
let factor: CGFloat = 0.95
|
let factor: CGFloat = 0.9
|
||||||
|
|
||||||
@State var refreshScrollId: UUID = UUID()
|
@State var refreshScrollId: UUID = UUID()
|
||||||
@State var opensColorPicker: Bool = false
|
@State var opensColorPicker: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .trailing) {
|
if !canvas.locksCanvas {
|
||||||
if let pen = tool.selectedPen {
|
VStack(alignment: .trailing) {
|
||||||
VStack(spacing: 5) {
|
penPropertyTool
|
||||||
penColorView(pen)
|
penItemList
|
||||||
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()
|
||||||
|
.frame(maxHeight: .infinity)
|
||||||
|
.padding(10)
|
||||||
|
.transition(.move(edge: .trailing).combined(with: .blurReplace))
|
||||||
}
|
}
|
||||||
.fixedSize()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var penScrollView: some View {
|
var penItemList: some View {
|
||||||
ScrollViewReader { proxy in
|
ScrollViewReader { proxy in
|
||||||
ScrollView(.vertical, showsIndicators: false) {
|
ScrollView(.vertical, showsIndicators: false) {
|
||||||
LazyVStack(spacing: 0) {
|
LazyVStack(spacing: 0) {
|
||||||
ForEach(tool.pens) { pen in
|
ForEach(tool.pens) { pen in
|
||||||
penView(pen)
|
penItemRow(pen)
|
||||||
.id(pen.id)
|
.id(pen.id)
|
||||||
.scrollTransition { content, phase in
|
.scrollTransition { content, phase in
|
||||||
content
|
content
|
||||||
@@ -68,20 +59,20 @@ struct PenDockView: View {
|
|||||||
.frame(maxHeight:( (height * factor + 10) * 6) + 20)
|
.frame(maxHeight:( (height * factor + 10) * 6) + 20)
|
||||||
.fixedSize()
|
.fixedSize()
|
||||||
.background(alignment: .trailing) {
|
.background(alignment: .trailing) {
|
||||||
RoundedRectangle(cornerRadius: 20)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
.fill(.regularMaterial)
|
.fill(.regularMaterial)
|
||||||
.frame(width: width * factor - 18)
|
.frame(width: width * factor - 18)
|
||||||
}
|
}
|
||||||
.clipShape(.rect(cornerRadii: .init(bottomTrailing: 20, topTrailing: 20)))
|
.clipShape(.rect(cornerRadii: .init(bottomTrailing: 8, topTrailing: 8)))
|
||||||
.overlay(alignment: .bottomLeading) {
|
.overlay(alignment: .bottomLeading) {
|
||||||
newPenButton
|
newPenButton
|
||||||
.offset(x: 60, y: 10)
|
.offset(x: 60, y: 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func penView(_ pen: Pen) -> some View {
|
func penItemRow(_ pen: Pen) -> some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
penShadowView(pen)
|
penShadow(pen)
|
||||||
if let tip = pen.style.icon.tip {
|
if let tip = pen.style.icon.tip {
|
||||||
Image(tip)
|
Image(tip)
|
||||||
.resizable()
|
.resizable()
|
||||||
@@ -139,7 +130,7 @@ struct PenDockView: View {
|
|||||||
}
|
}
|
||||||
.controlGroupStyle(.menu)
|
.controlGroupStyle(.menu)
|
||||||
} preview: {
|
} preview: {
|
||||||
penPreviewView(pen)
|
penPreview(pen)
|
||||||
.drawingGroup()
|
.drawingGroup()
|
||||||
.contentShape(.contextMenuPreview, .rect(cornerRadius: 10))
|
.contentShape(.contextMenuPreview, .rect(cornerRadius: 10))
|
||||||
}
|
}
|
||||||
@@ -147,14 +138,36 @@ struct PenDockView: View {
|
|||||||
tool.draggedPen = pen
|
tool.draggedPen = pen
|
||||||
return NSItemProvider(contentsOf: URL(string: pen.id)) ?? NSItemProvider()
|
return NSItemProvider(contentsOf: URL(string: pen.id)) ?? NSItemProvider()
|
||||||
} preview: {
|
} preview: {
|
||||||
penPreviewView(pen)
|
penPreview(pen)
|
||||||
.contentShape(.dragPreview, .rect(cornerRadius: 10))
|
.contentShape(.dragPreview, .rect(cornerRadius: 10))
|
||||||
}
|
}
|
||||||
.onDrop(of: [.item], delegate: PenDropDelegate(id: pen.id, tool: tool, action: { refreshScrollId = UUID() }))
|
.onDrop(of: [.item], delegate: PenDropDelegate(id: pen.id, tool: tool, action: { refreshScrollId = UUID() }))
|
||||||
.offset(x: tool.selectedPen === pen ? 0 : 25)
|
.offset(x: tool.selectedPen === pen ? 0 : 25)
|
||||||
}
|
}
|
||||||
|
|
||||||
func penColorView(_ pen: Pen) -> some View {
|
@ViewBuilder
|
||||||
|
var penPropertyTool: some View {
|
||||||
|
if let pen = tool.selectedPen {
|
||||||
|
VStack(spacing: 5) {
|
||||||
|
if pen.strokeStyle == .marker {
|
||||||
|
penColorPicker(pen)
|
||||||
|
}
|
||||||
|
penThicknessPicker(pen)
|
||||||
|
}
|
||||||
|
.padding(10)
|
||||||
|
.frame(width: width * factor - 18)
|
||||||
|
.background {
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.fill(.regularMaterial)
|
||||||
|
}
|
||||||
|
.transition(.move(edge: .trailing).combined(with: .blurReplace))
|
||||||
|
} else {
|
||||||
|
Color.clear
|
||||||
|
.frame(width: width * factor - 18, height: 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func penColorPicker(_ pen: Pen) -> some View {
|
||||||
Button {
|
Button {
|
||||||
opensColorPicker = true
|
opensColorPicker = true
|
||||||
} label: {
|
} label: {
|
||||||
@@ -174,16 +187,16 @@ struct PenDockView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.background(baseColor)
|
.background(baseColor)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 10))
|
.clipShape(.rect(cornerRadius: 8))
|
||||||
.frame(height: 28)
|
.frame(height: 25)
|
||||||
.overlay {
|
.overlay {
|
||||||
RoundedRectangle(cornerRadius: 10)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
.stroke(Color.gray, lineWidth: 0.4)
|
.stroke(Color.gray, lineWidth: 0.4)
|
||||||
}
|
}
|
||||||
.padding(0.2)
|
.padding(0.2)
|
||||||
.padding(.top, 4)
|
|
||||||
.drawingGroup()
|
.drawingGroup()
|
||||||
}
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
.hoverEffect(.lift)
|
.hoverEffect(.lift)
|
||||||
.popover(isPresented: $opensColorPicker) {
|
.popover(isPresented: $opensColorPicker) {
|
||||||
let color = Binding(
|
let color = Binding(
|
||||||
@@ -195,15 +208,20 @@ struct PenDockView: View {
|
|||||||
)
|
)
|
||||||
ColorPicker(color: color)
|
ColorPicker(color: color)
|
||||||
.presentationCompactAdaptation(.popover)
|
.presentationCompactAdaptation(.popover)
|
||||||
|
.onDisappear {
|
||||||
|
withPersistence(\.viewContext) { context in
|
||||||
|
try context.saveIfNeeded()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func penThicknessView(_ pen: Pen) -> some View {
|
func penThicknessPicker(_ pen: Pen) -> some View {
|
||||||
let minimum: CGFloat = pen.style.thickness.min
|
let minimum: CGFloat = pen.style.thickness.min
|
||||||
let maximum: CGFloat = pen.style.thickness.max
|
let maximum: CGFloat = pen.style.thickness.max
|
||||||
let start: CGFloat = 5
|
let start: CGFloat = 4
|
||||||
let end: CGFloat = 15
|
let end: CGFloat = 10
|
||||||
let selection = Binding(
|
let selection = Binding(
|
||||||
get: { pen.thickness },
|
get: { pen.thickness },
|
||||||
set: {
|
set: {
|
||||||
@@ -213,20 +231,20 @@ struct PenDockView: View {
|
|||||||
)
|
)
|
||||||
Picker("", selection: selection) {
|
Picker("", selection: selection) {
|
||||||
ForEach(pen.style.thicknessSteps, id: \.self) { step in
|
ForEach(pen.style.thicknessSteps, id: \.self) { step in
|
||||||
let size = ((step - minimum) * (end - start) / (maximum - minimum)) + start - (1 / step)
|
let size = ((step - minimum) * (end - start) / (maximum - minimum)) + start - (0.5 / step)
|
||||||
if pen.thickness == step {
|
Circle()
|
||||||
Circle()
|
.fill(.black)
|
||||||
.fill(.black)
|
.frame(width: size, height: size)
|
||||||
.frame(width: size, height: size)
|
.frame(width: size + 2, height: size + 2)
|
||||||
} else {
|
|
||||||
Circle()
|
|
||||||
.stroke(Color.black, lineWidth: 1)
|
|
||||||
.frame(width: size, height: size)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.pickerStyle(.wheel)
|
.pickerStyle(.wheel)
|
||||||
.frame(width: width * factor - 18, height: 40)
|
.frame(width: width * factor - 18, height: 35)
|
||||||
|
.onChange(of: pen.thickness) { _, _ in
|
||||||
|
withPersistence(\.viewContext) { context in
|
||||||
|
try context.saveIfNeeded()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var newPenButton: some View {
|
var newPenButton: some View {
|
||||||
@@ -254,7 +272,7 @@ struct PenDockView: View {
|
|||||||
.hoverEffect(.lift)
|
.hoverEffect(.lift)
|
||||||
}
|
}
|
||||||
|
|
||||||
func penPreviewView(_ pen: Pen) -> some View {
|
func penPreview(_ pen: Pen) -> some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
if let tip = pen.style.icon.tip {
|
if let tip = pen.style.icon.tip {
|
||||||
Image(tip)
|
Image(tip)
|
||||||
@@ -270,7 +288,7 @@ struct PenDockView: View {
|
|||||||
.padding(.leading, 10)
|
.padding(.leading, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
func penShadowView(_ pen: Pen) -> some View {
|
func penShadow(_ pen: Pen) -> some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
Group {
|
Group {
|
||||||
if let tip = pen.style.icon.tip {
|
if let tip = pen.style.icon.tip {
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
//
|
||||||
|
// Toolbar.swift
|
||||||
|
// Memola
|
||||||
|
//
|
||||||
|
// Created by Dscyre Scotti on 5/19/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct Toolbar: View {
|
||||||
|
@Environment(\.dismiss) var dismiss
|
||||||
|
|
||||||
|
@EnvironmentObject var history: History
|
||||||
|
@EnvironmentObject var canvas: Canvas
|
||||||
|
|
||||||
|
@State var memo: MemoObject
|
||||||
|
@State var title: String
|
||||||
|
@FocusState var textFieldState: Bool
|
||||||
|
|
||||||
|
let size: CGFloat
|
||||||
|
|
||||||
|
init(memo: MemoObject, size: CGFloat) {
|
||||||
|
self.memo = memo
|
||||||
|
self.size = size
|
||||||
|
self.title = memo.title
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack(spacing: 5) {
|
||||||
|
if !canvas.locksCanvas {
|
||||||
|
closeButton
|
||||||
|
titleField
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
if !canvas.locksCanvas {
|
||||||
|
historyControl
|
||||||
|
}
|
||||||
|
lockButton
|
||||||
|
}
|
||||||
|
.font(.subheadline)
|
||||||
|
.padding(10)
|
||||||
|
}
|
||||||
|
|
||||||
|
var closeButton: some View {
|
||||||
|
Button {
|
||||||
|
closeMemo()
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "xmark")
|
||||||
|
.contentShape(.circle)
|
||||||
|
.frame(width: size, height: size)
|
||||||
|
.background(.regularMaterial)
|
||||||
|
.clipShape(.rect(cornerRadius: 8))
|
||||||
|
}
|
||||||
|
.hoverEffect(.lift)
|
||||||
|
.disabled(textFieldState)
|
||||||
|
.transition(.move(edge: .top).combined(with: .blurReplace))
|
||||||
|
}
|
||||||
|
|
||||||
|
var titleField: some View {
|
||||||
|
TextField("", text: $title)
|
||||||
|
.focused($textFieldState)
|
||||||
|
.textFieldStyle(.plain)
|
||||||
|
.padding(.horizontal, size / 2.5)
|
||||||
|
.frame(width: 140, height: size)
|
||||||
|
.background(.regularMaterial)
|
||||||
|
.clipShape(.rect(cornerRadius: 8))
|
||||||
|
.onChange(of: textFieldState) { oldValue, newValue in
|
||||||
|
if !newValue {
|
||||||
|
if !title.isEmpty {
|
||||||
|
memo.title = title
|
||||||
|
} else {
|
||||||
|
title = memo.title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.transition(.move(edge: .top).combined(with: .blurReplace))
|
||||||
|
}
|
||||||
|
|
||||||
|
var historyControl: some View {
|
||||||
|
HStack {
|
||||||
|
Button {
|
||||||
|
history.historyPublisher.send(.undo)
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "arrow.uturn.backward.circle")
|
||||||
|
|
||||||
|
.contentShape(.circle)
|
||||||
|
}
|
||||||
|
.hoverEffect(.lift)
|
||||||
|
.disabled(history.undoDisabled)
|
||||||
|
Button {
|
||||||
|
history.historyPublisher.send(.redo)
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "arrow.uturn.forward.circle")
|
||||||
|
.contentShape(.circle)
|
||||||
|
}
|
||||||
|
.hoverEffect(.lift)
|
||||||
|
.disabled(history.redoDisabled)
|
||||||
|
}
|
||||||
|
.frame(width: size * 2, height: size)
|
||||||
|
.background(.regularMaterial)
|
||||||
|
.clipShape(.rect(cornerRadius: 8))
|
||||||
|
.disabled(textFieldState)
|
||||||
|
.transition(.move(edge: .top).combined(with: .blurReplace))
|
||||||
|
}
|
||||||
|
|
||||||
|
var lockButton: some View {
|
||||||
|
Button {
|
||||||
|
withAnimation {
|
||||||
|
canvas.locksCanvas.toggle()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
ZStack {
|
||||||
|
if canvas.locksCanvas {
|
||||||
|
Image(systemName: "lock.open")
|
||||||
|
.transition(.move(edge: .trailing).combined(with: .blurReplace))
|
||||||
|
} else {
|
||||||
|
Image(systemName: "lock")
|
||||||
|
.transition(.move(edge: .leading).combined(with: .blurReplace))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.contentShape(.circle)
|
||||||
|
.frame(width: size, height: size)
|
||||||
|
.background(.regularMaterial)
|
||||||
|
.clipShape(.rect(cornerRadius: 8))
|
||||||
|
}
|
||||||
|
.hoverEffect(.lift)
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeMemo() {
|
||||||
|
withPersistenceSync(\.viewContext) { context in
|
||||||
|
try context.saveIfNeeded()
|
||||||
|
}
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -76,8 +76,12 @@ struct MemosView: View {
|
|||||||
|
|
||||||
let eraserPenObject = PenObject.createObject(\.viewContext, penStyle: .eraser)
|
let eraserPenObject = PenObject.createObject(\.viewContext, penStyle: .eraser)
|
||||||
eraserPenObject.orderIndex = 0
|
eraserPenObject.orderIndex = 0
|
||||||
let markerPenObject = PenObject.createObject(\.viewContext, penStyle: .marker)
|
let markerPenObjects = [Color.red, Color.blue, Color.yellow, Color.black].enumerated().map { (index, color) in
|
||||||
markerPenObject.orderIndex = 1
|
let penObject = PenObject.createObject(\.viewContext, penStyle: .marker)
|
||||||
|
penObject.orderIndex = Int16(index) + 1
|
||||||
|
penObject.color = color.components
|
||||||
|
return penObject
|
||||||
|
}
|
||||||
|
|
||||||
let graphicContextObject = GraphicContextObject(\.viewContext)
|
let graphicContextObject = GraphicContextObject(\.viewContext)
|
||||||
graphicContextObject.strokes = []
|
graphicContextObject.strokes = []
|
||||||
@@ -89,10 +93,10 @@ struct MemosView: View {
|
|||||||
canvasObject.graphicContext = graphicContextObject
|
canvasObject.graphicContext = graphicContextObject
|
||||||
|
|
||||||
toolObject.memo = memoObject
|
toolObject.memo = memoObject
|
||||||
toolObject.pens = [eraserPenObject, markerPenObject]
|
toolObject.pens = .init(array: [eraserPenObject] + markerPenObjects)
|
||||||
|
|
||||||
eraserPenObject.tool = toolObject
|
eraserPenObject.tool = toolObject
|
||||||
markerPenObject.tool = toolObject
|
markerPenObjects.forEach { $0.tool = toolObject }
|
||||||
|
|
||||||
graphicContextObject.canvas = canvasObject
|
graphicContextObject.canvas = canvasObject
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user