feat: redesign pen tool dock
@@ -13,6 +13,8 @@
|
|||||||
EC35655A2BF060D900A4E0BF /* Quad.metal in Sources */ = {isa = PBXBuildFile; fileRef = EC3565592BF060D900A4E0BF /* Quad.metal */; };
|
EC35655A2BF060D900A4E0BF /* Quad.metal in Sources */ = {isa = PBXBuildFile; fileRef = EC3565592BF060D900A4E0BF /* Quad.metal */; };
|
||||||
EC35655C2BF0712A00A4E0BF /* Float++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC35655B2BF0712A00A4E0BF /* Float++.swift */; };
|
EC35655C2BF0712A00A4E0BF /* Float++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC35655B2BF0712A00A4E0BF /* Float++.swift */; };
|
||||||
EC4538892BEBCAE000A86FEC /* Quad.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC4538882BEBCAE000A86FEC /* Quad.swift */; };
|
EC4538892BEBCAE000A86FEC /* Quad.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC4538882BEBCAE000A86FEC /* Quad.swift */; };
|
||||||
|
EC5050072BF65CED00B4D86E /* PenDropDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC5050062BF65CED00B4D86E /* PenDropDelegate.swift */; };
|
||||||
|
EC50500D2BF6674400B4D86E /* DraggableViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC50500C2BF6674400B4D86E /* DraggableViewModifier.swift */; };
|
||||||
EC7F6BEC2BE5E6E300A34A7B /* MemolaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7F6BEB2BE5E6E300A34A7B /* MemolaApp.swift */; };
|
EC7F6BEC2BE5E6E300A34A7B /* MemolaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7F6BEB2BE5E6E300A34A7B /* MemolaApp.swift */; };
|
||||||
EC7F6BF02BE5E6E400A34A7B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC7F6BEF2BE5E6E400A34A7B /* Assets.xcassets */; };
|
EC7F6BF02BE5E6E400A34A7B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC7F6BEF2BE5E6E400A34A7B /* Assets.xcassets */; };
|
||||||
EC7F6BF32BE5E6E400A34A7B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC7F6BF22BE5E6E400A34A7B /* Preview Assets.xcassets */; };
|
EC7F6BF32BE5E6E400A34A7B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC7F6BF22BE5E6E400A34A7B /* Preview Assets.xcassets */; };
|
||||||
@@ -85,6 +87,9 @@
|
|||||||
EC3565592BF060D900A4E0BF /* Quad.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Quad.metal; sourceTree = "<group>"; };
|
EC3565592BF060D900A4E0BF /* Quad.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Quad.metal; sourceTree = "<group>"; };
|
||||||
EC35655B2BF0712A00A4E0BF /* Float++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Float++.swift"; sourceTree = "<group>"; };
|
EC35655B2BF0712A00A4E0BF /* Float++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Float++.swift"; sourceTree = "<group>"; };
|
||||||
EC4538882BEBCAE000A86FEC /* Quad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Quad.swift; sourceTree = "<group>"; };
|
EC4538882BEBCAE000A86FEC /* Quad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Quad.swift; sourceTree = "<group>"; };
|
||||||
|
EC5050062BF65CED00B4D86E /* PenDropDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenDropDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
EC50500C2BF6674400B4D86E /* DraggableViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableViewModifier.swift; sourceTree = "<group>"; };
|
||||||
|
EC50500E2BF670EA00B4D86E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
EC7F6BE82BE5E6E300A34A7B /* Memola.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Memola.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
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 = "<group>"; };
|
EC7F6BEB2BE5E6E300A34A7B /* MemolaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemolaApp.swift; sourceTree = "<group>"; };
|
||||||
EC7F6BEF2BE5E6E400A34A7B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
EC7F6BEF2BE5E6E400A34A7B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
@@ -178,6 +183,63 @@
|
|||||||
path = ViewController;
|
path = ViewController;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
EC5050042BF65CBC00B4D86E /* Core */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
ECA738BB2BE60E0300A4542E /* Tool.swift */,
|
||||||
|
);
|
||||||
|
path = Core;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EC5050052BF65CCD00B4D86E /* PenTool */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
ECA739072BE623F300A4542E /* PenToolView.swift */,
|
||||||
|
EC5050062BF65CED00B4D86E /* PenDropDelegate.swift */,
|
||||||
|
);
|
||||||
|
path = PenTool;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EC5050082BF65D0500B4D86E /* Memo */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
ECA7387C2BE5EF4B00A4542E /* MemoView.swift */,
|
||||||
|
);
|
||||||
|
path = Memo;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EC5050092BF65D5700B4D86E /* Canvas */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
ECA738B22BE60D9E00A4542E /* CanvasView.swift */,
|
||||||
|
);
|
||||||
|
path = Canvas;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EC50500A2BF6672000B4D86E /* Components */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EC50500B2BF6673300B4D86E /* ViewModifiers */,
|
||||||
|
);
|
||||||
|
path = Components;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EC50500B2BF6673300B4D86E /* ViewModifiers */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EC50500C2BF6674400B4D86E /* DraggableViewModifier.swift */,
|
||||||
|
);
|
||||||
|
path = ViewModifiers;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EC5050102BF670EE00B4D86E /* Config */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EC50500E2BF670EA00B4D86E /* Info.plist */,
|
||||||
|
);
|
||||||
|
path = Config;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
EC7F6BDF2BE5E6E300A34A7B = {
|
EC7F6BDF2BE5E6E300A34A7B = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -199,6 +261,8 @@
|
|||||||
children = (
|
children = (
|
||||||
ECA738762BE5EE4E00A4542E /* App */,
|
ECA738762BE5EE4E00A4542E /* App */,
|
||||||
ECA7387E2BE5FE4200A4542E /* Canvas */,
|
ECA7387E2BE5FE4200A4542E /* Canvas */,
|
||||||
|
EC5050102BF670EE00B4D86E /* Config */,
|
||||||
|
EC50500A2BF6672000B4D86E /* Components */,
|
||||||
ECA738A12BE601F700A4542E /* Extensions */,
|
ECA738A12BE601F700A4542E /* Extensions */,
|
||||||
ECA738772BE5EEE800A4542E /* Features */,
|
ECA738772BE5EEE800A4542E /* Features */,
|
||||||
ECA738FA2BE61B1700A4542E /* Persistence */,
|
ECA738FA2BE61B1700A4542E /* Persistence */,
|
||||||
@@ -244,8 +308,8 @@
|
|||||||
ECA7387B2BE5EF3500A4542E /* Memo */ = {
|
ECA7387B2BE5EF3500A4542E /* Memo */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
ECA7387C2BE5EF4B00A4542E /* MemoView.swift */,
|
EC5050082BF65D0500B4D86E /* Memo */,
|
||||||
ECA739072BE623F300A4542E /* PenToolView.swift */,
|
EC5050052BF65CCD00B4D86E /* PenTool */,
|
||||||
);
|
);
|
||||||
path = Memo;
|
path = Memo;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -371,8 +435,8 @@
|
|||||||
ECA738AB2BE60CB500A4542E /* View */ = {
|
ECA738AB2BE60CB500A4542E /* View */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
EC5050092BF65D5700B4D86E /* Canvas */,
|
||||||
ECA738AE2BE60CEC00A4542E /* Bridge */,
|
ECA738AE2BE60CEC00A4542E /* Bridge */,
|
||||||
ECA738B22BE60D9E00A4542E /* CanvasView.swift */,
|
|
||||||
);
|
);
|
||||||
path = View;
|
path = View;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -389,8 +453,8 @@
|
|||||||
ECA738B12BE60D8800A4542E /* Tool */ = {
|
ECA738B12BE60D8800A4542E /* Tool */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
EC5050042BF65CBC00B4D86E /* Core */,
|
||||||
ECA738BD2BE60E2800A4542E /* Pen */,
|
ECA738BD2BE60E2800A4542E /* Pen */,
|
||||||
ECA738BB2BE60E0300A4542E /* Tool.swift */,
|
|
||||||
);
|
);
|
||||||
path = Tool;
|
path = Tool;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -646,6 +710,7 @@
|
|||||||
ECA7388F2BE600DA00A4542E /* Grid.metal in Sources */,
|
ECA7388F2BE600DA00A4542E /* Grid.metal in Sources */,
|
||||||
ECA738C92BE60EF700A4542E /* GraphicContext.swift in Sources */,
|
ECA738C92BE60EF700A4542E /* GraphicContext.swift in Sources */,
|
||||||
ECA738F62BE612B700A4542E /* MTLDevice++.swift in Sources */,
|
ECA738F62BE612B700A4542E /* MTLDevice++.swift in Sources */,
|
||||||
|
EC50500D2BF6674400B4D86E /* DraggableViewModifier.swift in Sources */,
|
||||||
ECA7389E2BE601CB00A4542E /* QuadVertex.swift in Sources */,
|
ECA7389E2BE601CB00A4542E /* QuadVertex.swift in Sources */,
|
||||||
ECA738B32BE60D9E00A4542E /* CanvasView.swift in Sources */,
|
ECA738B32BE60D9E00A4542E /* CanvasView.swift in Sources */,
|
||||||
ECA738C42BE60E8800A4542E /* MarkerPenStyle.swift in Sources */,
|
ECA738C42BE60E8800A4542E /* MarkerPenStyle.swift in Sources */,
|
||||||
@@ -655,6 +720,7 @@
|
|||||||
ECA738B62BE60DCD00A4542E /* History.swift in Sources */,
|
ECA738B62BE60DCD00A4542E /* History.swift in Sources */,
|
||||||
ECA738D22BE60F7B00A4542E /* Stroke.swift in Sources */,
|
ECA738D22BE60F7B00A4542E /* Stroke.swift in Sources */,
|
||||||
ECA738F22BE6128F00A4542E /* Collection++.swift in Sources */,
|
ECA738F22BE6128F00A4542E /* Collection++.swift in Sources */,
|
||||||
|
EC5050072BF65CED00B4D86E /* PenDropDelegate.swift in Sources */,
|
||||||
ECA738A32BE6020A00A4542E /* CGFloat++.swift in Sources */,
|
ECA738A32BE6020A00A4542E /* CGFloat++.swift in Sources */,
|
||||||
ECA738C12BE60E5300A4542E /* PenStyle.swift in Sources */,
|
ECA738C12BE60E5300A4542E /* PenStyle.swift in Sources */,
|
||||||
ECA738DE2BE610A000A4542E /* ViewPortRenderPass.swift in Sources */,
|
ECA738DE2BE610A000A4542E /* ViewPortRenderPass.swift in Sources */,
|
||||||
@@ -798,7 +864,8 @@
|
|||||||
DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = 9TYSSFKV5U;
|
DEVELOPMENT_TEAM = 9TYSSFKV5U;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
|
INFOPLIST_FILE = Memola/Config/Info.plist;
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
@@ -830,7 +897,8 @@
|
|||||||
DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = 9TYSSFKV5U;
|
DEVELOPMENT_TEAM = 9TYSSFKV5U;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
|
INFOPLIST_FILE = Memola/Config/Info.plist;
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
|||||||
@@ -11,17 +11,22 @@ import Foundation
|
|||||||
class Tool: NSObject, ObservableObject {
|
class Tool: NSObject, ObservableObject {
|
||||||
@Published var pens: [Pen]
|
@Published var pens: [Pen]
|
||||||
@Published var selectedPen: Pen?
|
@Published var selectedPen: Pen?
|
||||||
|
@Published var draggedPen: Pen?
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
pens = [
|
pens = [
|
||||||
Pen(for: .marker),
|
Pen(for: .eraser),
|
||||||
Pen(for: .eraser)
|
Pen(for: .marker)
|
||||||
]
|
]
|
||||||
super.init()
|
super.init()
|
||||||
selectedPen = pens.first
|
selectedPen = pens[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func changePen(_ pen: Pen) {
|
func changePen(_ pen: Pen) {
|
||||||
selectedPen = pen
|
selectedPen = pen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addPen(_ pen: Pen) {
|
||||||
|
pens.append(pen)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -7,13 +7,16 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
class Pen: NSObject, ObservableObject, Identifiable {
|
class Pen: NSObject, ObservableObject, Identifiable {
|
||||||
|
let id: String
|
||||||
@Published var style: any PenStyle
|
@Published var style: any PenStyle
|
||||||
@Published var color: [CGFloat]
|
@Published var color: [CGFloat]
|
||||||
@Published var thickness: CGFloat
|
@Published var thickness: CGFloat
|
||||||
|
|
||||||
init(style: any PenStyle, color: [CGFloat], thickness: CGFloat) {
|
init(style: any PenStyle, color: [CGFloat], thickness: CGFloat) {
|
||||||
|
self.id = UUID().uuidString
|
||||||
self.style = style
|
self.style = style
|
||||||
self.color = color
|
self.color = color
|
||||||
self.thickness = thickness
|
self.thickness = thickness
|
||||||
|
|||||||
30
Memola/Components/ViewModifiers/DraggableViewModifier.swift
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// DraggableViewModifier.swift
|
||||||
|
// Memola
|
||||||
|
//
|
||||||
|
// Created by Dscyre Scotti on 5/16/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct DraggableViewModifier<Preview: View>: 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<Preview: View>(if condition: Bool, data: @escaping () -> NSItemProvider, @ViewBuilder preview: @escaping () -> Preview) -> some View {
|
||||||
|
modifier(DraggableViewModifier(condition: condition, data: data, preview: preview))
|
||||||
|
}
|
||||||
|
}
|
||||||
51
Memola/Config/Info.plist
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(MARKETING_VERSION)</string>
|
||||||
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>UILaunchScreen</key>
|
||||||
|
<dict>
|
||||||
|
<key>UILaunchScreen</key>
|
||||||
|
<dict/>
|
||||||
|
</dict>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>UIApplicationSceneManifest</key>
|
||||||
|
<dict>
|
||||||
|
<key>UIApplicationSupportsMultipleScenes</key>
|
||||||
|
<true/>
|
||||||
|
<key>UISceneConfigurations</key>
|
||||||
|
<dict/>
|
||||||
|
</dict>
|
||||||
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
|
<true/>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>UISupportedInterfaceOrientations~iphone</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -26,19 +26,19 @@ struct MemoView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
CanvasView()
|
CanvasView()
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
.overlay(alignment: .bottomTrailing) {
|
|
||||||
PenToolView()
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
.overlay(alignment: .topTrailing) {
|
.overlay(alignment: .topTrailing) {
|
||||||
historyTool
|
VStack(alignment: .trailing, spacing: 20) {
|
||||||
.padding()
|
historyTool
|
||||||
|
PenToolView()
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
}
|
}
|
||||||
.overlay(alignment: .topLeading) {
|
.overlay(alignment: .topLeading) {
|
||||||
Button {
|
Button {
|
||||||
closeMemo()
|
closeMemo()
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "xmark")
|
Image(systemName: "xmark")
|
||||||
|
.contentShape(.circle)
|
||||||
.padding(15)
|
.padding(15)
|
||||||
.background(.regularMaterial)
|
.background(.regularMaterial)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
.clipShape(RoundedRectangle(cornerRadius: 20))
|
||||||
@@ -68,6 +68,7 @@ struct MemoView: View {
|
|||||||
history.historyPublisher.send(.undo)
|
history.historyPublisher.send(.undo)
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "arrow.uturn.backward.circle")
|
Image(systemName: "arrow.uturn.backward.circle")
|
||||||
|
.contentShape(.circle)
|
||||||
}
|
}
|
||||||
.hoverEffect(.lift)
|
.hoverEffect(.lift)
|
||||||
.disabled(history.undoDisabled)
|
.disabled(history.undoDisabled)
|
||||||
@@ -75,6 +76,7 @@ struct MemoView: View {
|
|||||||
history.historyPublisher.send(.redo)
|
history.historyPublisher.send(.redo)
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "arrow.uturn.forward.circle")
|
Image(systemName: "arrow.uturn.forward.circle")
|
||||||
|
.contentShape(.circle)
|
||||||
}
|
}
|
||||||
.hoverEffect(.lift)
|
.hoverEffect(.lift)
|
||||||
.disabled(history.redoDisabled)
|
.disabled(history.redoDisabled)
|
||||||
32
Memola/Features/Memo/PenTool/PenDropDelegate.swift
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
||||||
|
func performDrop(info: DropInfo) -> Bool {
|
||||||
|
tool.draggedPen = nil
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
109
Memola/Features/Memo/PenTool/PenToolView.swift
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
//
|
||||||
|
// PenToolView.swift
|
||||||
|
// Memola
|
||||||
|
//
|
||||||
|
// Created by Dscyre Scotti on 5/4/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PenToolView: View {
|
||||||
|
@EnvironmentObject var tool: Tool
|
||||||
|
|
||||||
|
let width: CGFloat = 80
|
||||||
|
let height: CGFloat = 30
|
||||||
|
let factor: CGFloat = 1.22
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .trailing, spacing: 0) {
|
||||||
|
ScrollView(.vertical, showsIndicators: false) {
|
||||||
|
LazyVStack(spacing: 0) {
|
||||||
|
ForEach(tool.pens) { pen in
|
||||||
|
penView(pen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.vertical, 5)
|
||||||
|
.padding(.leading, 40)
|
||||||
|
}
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
Divider()
|
||||||
|
newPenButton
|
||||||
|
}
|
||||||
|
.frame(width: width * factor - 20)
|
||||||
|
}
|
||||||
|
.frame(maxHeight: (height * factor + 10) * 8)
|
||||||
|
.fixedSize()
|
||||||
|
.background {
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
Spacer(minLength: 70)
|
||||||
|
RoundedRectangle(cornerRadius: 20)
|
||||||
|
.fill(.regularMaterial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.clipShape(.rect(cornerRadii: .init(bottomTrailing: 20, topTrailing: 20)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func penView(_ pen: Pen) -> some View {
|
||||||
|
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: width * factor, height: height * factor)
|
||||||
|
.padding(.vertical, 5)
|
||||||
|
.padding(.leading, 10)
|
||||||
|
.clipShape(.rect(cornerRadii: .init(topLeading: 10, bottomLeading: 10)))
|
||||||
|
.contentShape(.rect(cornerRadii: .init(topLeading: 10, bottomLeading: 10)))
|
||||||
|
.onDrag(if: pen.strokeStyle != .eraser) {
|
||||||
|
tool.draggedPen = pen
|
||||||
|
return NSItemProvider(contentsOf: URL(string: pen.id)) ?? NSItemProvider()
|
||||||
|
} preview: {
|
||||||
|
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: width * factor, height: height * factor)
|
||||||
|
.padding([.vertical, .leading], 10)
|
||||||
|
.contentShape(.dragPreview, .rect(cornerRadius: 10))
|
||||||
|
}
|
||||||
|
.onDrop(of: [.item], delegate: PenDropDelegate(id: pen.id, tool: tool))
|
||||||
|
.onTapGesture {
|
||||||
|
if tool.selectedPen === pen {
|
||||||
|
withAnimation {
|
||||||
|
tool.selectedPen = nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
withAnimation {
|
||||||
|
tool.changePen(pen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.offset(x: tool.selectedPen === pen ? 0 : 28)
|
||||||
|
}
|
||||||
|
|
||||||
|
var newPenButton: some View {
|
||||||
|
Button(action: {
|
||||||
|
let pen = Pen(for: .marker)
|
||||||
|
pen.color = [Color.red, Color.blue, Color.green, Color.black, Color.orange].randomElement()!.components
|
||||||
|
tool.addPen(pen)
|
||||||
|
}) {
|
||||||
|
Image(systemName: "plus")
|
||||||
|
.font(.title3)
|
||||||
|
.contentShape(.circle)
|
||||||
|
}
|
||||||
|
.hoverEffect(.lift)
|
||||||
|
.padding(10)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 18 KiB |
@@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "bullet-base.png",
|
"filename" : "marker-base.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "bullet-base@2x.png",
|
"filename" : "marker-base@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "bullet-base@3x.png",
|
"filename" : "marker-base@3x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 20 KiB |
BIN
Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/marker-base.png
vendored
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/marker-base@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-base.imageset/marker-base@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 23 KiB |
@@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "bullet-tip.png",
|
"filename" : "marker-tip.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "bullet-tip@2x.png",
|
"filename" : "marker-tip@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "bullet-tip@3x.png",
|
"filename" : "marker-tip@3x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 427 B |
|
Before Width: | Height: | Size: 859 B |
|
Before Width: | Height: | Size: 1.3 KiB |
BIN
Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/marker-tip.png
vendored
Normal file
|
After Width: | Height: | Size: 381 B |
BIN
Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/marker-tip@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 770 B |
BIN
Memola/Resources/Assets/Assets.xcassets/graphics/pens/marker-tip.imageset/marker-tip@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |