mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-07-09 14:25:18 +02:00
feat: add duplicate and remove button
This commit is contained in:
@@ -16,9 +16,6 @@ public class Tool: NSObject, ObservableObject {
|
||||
@Published var pens: [Pen] = []
|
||||
@Published var selectedPen: Pen?
|
||||
@Published var draggedPen: Pen?
|
||||
@Published var isReordering: Bool = false
|
||||
@Published var isShaking: Bool = false
|
||||
@Published var shakingId: UUID = UUID()
|
||||
|
||||
let scrollPublisher = PassthroughSubject<String, Never>()
|
||||
|
||||
@@ -47,6 +44,9 @@ public class Tool: NSObject, ObservableObject {
|
||||
selectedPen = pen
|
||||
}
|
||||
selectedPen?.isSelected = true
|
||||
withPersistence(\.viewContext) { context in
|
||||
try context.saveIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
func unselectPen(_ pen: Pen) {
|
||||
@@ -54,6 +54,23 @@ public class Tool: NSObject, ObservableObject {
|
||||
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) {
|
||||
@@ -65,6 +82,9 @@ public class Tool: NSObject, ObservableObject {
|
||||
object.pens.add(_pen)
|
||||
}
|
||||
scrollPublisher.send(pen.id)
|
||||
withPersistence(\.viewContext) { context in
|
||||
try context.saveIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
func removePen(_ pen: Pen) {
|
||||
@@ -76,5 +96,8 @@ public class Tool: NSObject, ObservableObject {
|
||||
_pen.tool = nil
|
||||
object.pens.remove(_pen)
|
||||
}
|
||||
withPersistence(\.viewContext) { context in
|
||||
try context.saveIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// ContextMenuableViewModifier.swift
|
||||
// ContextMenuViewModifier.swift
|
||||
// Memola
|
||||
//
|
||||
// Created by Dscyre Scotti on 5/17/24.
|
||||
@@ -8,7 +8,7 @@
|
||||
import SwiftUI
|
||||
import Foundation
|
||||
|
||||
struct ContextMenuableViewModifier<MenuContent: View>: ViewModifier {
|
||||
struct ContextMenuViewModifier<MenuContent: View>: ViewModifier {
|
||||
let condition: Bool
|
||||
let menuItems: () -> MenuContent
|
||||
|
||||
@@ -24,6 +24,6 @@ struct ContextMenuableViewModifier<MenuContent: View>: ViewModifier {
|
||||
|
||||
public extension View {
|
||||
func contextMenu<MenuContent: View>(if condition: Bool, @ViewBuilder menuItems: @escaping () -> MenuContent) -> some View {
|
||||
modifier(ContextMenuableViewModifier(condition: condition, menuItems: menuItems))
|
||||
modifier(ContextMenuViewModifier(condition: condition, menuItems: menuItems))
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// DraggableViewModifier.swift
|
||||
// OnDragViewModifier.swift
|
||||
// Memola
|
||||
//
|
||||
// Created by Dscyre Scotti on 5/16/24.
|
||||
@@ -8,7 +8,7 @@
|
||||
import SwiftUI
|
||||
import Foundation
|
||||
|
||||
struct DraggableViewModifier<Preview: View>: ViewModifier {
|
||||
struct OnDragViewModifier<Preview: View>: ViewModifier {
|
||||
let condition: Bool
|
||||
let data: () -> NSItemProvider
|
||||
let preview: () -> Preview
|
||||
@@ -25,6 +25,6 @@ struct DraggableViewModifier<Preview: View>: ViewModifier {
|
||||
|
||||
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))
|
||||
modifier(OnDragViewModifier(condition: condition, data: data, preview: preview))
|
||||
}
|
||||
}
|
||||
@@ -27,11 +27,11 @@ struct PenDropDelegate: DropDelegate {
|
||||
tool.pens.move(fromOffsets: IndexSet(integer: fromIndex), toOffset: toIndex > fromIndex ? toIndex + 1 : toIndex)
|
||||
tool.objectWillChange.send()
|
||||
}
|
||||
tool.shakingId = UUID()
|
||||
withPersistence(\.viewContext) { context in
|
||||
for (index, pen) in tool.pens.enumerated() {
|
||||
pen.object?.orderIndex = Int16(index)
|
||||
}
|
||||
try context.saveIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,37 +17,14 @@ struct PenToolView: View {
|
||||
var body: some View {
|
||||
ScrollViewReader { proxy in
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
if tool.isReordering {
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(tool.pens) { pen in
|
||||
if pen.strokeStyle == .marker {
|
||||
penView(pen)
|
||||
.offset(y: tool.isShaking ? 1.5 : -1.5)
|
||||
.id(pen.id)
|
||||
} else {
|
||||
penView(pen)
|
||||
.id(pen.id)
|
||||
}
|
||||
}
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(tool.pens) { pen in
|
||||
penView(pen)
|
||||
.id(pen.id)
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
.padding(.leading, 40)
|
||||
.onAppear {
|
||||
withAnimation(.easeInOut.repeatForever().speed(5)) {
|
||||
tool.isShaking.toggle()
|
||||
}
|
||||
}
|
||||
.id(tool.shakingId)
|
||||
} else {
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(tool.pens) { pen in
|
||||
penView(pen)
|
||||
.id(pen.id)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
.padding(.leading, 40)
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
.padding(.leading, 40)
|
||||
}
|
||||
.onReceive(tool.scrollPublisher) { id in
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
@@ -68,14 +45,8 @@ struct PenToolView: View {
|
||||
}
|
||||
.clipShape(.rect(cornerRadii: .init(bottomTrailing: 20, topTrailing: 20)))
|
||||
.overlay(alignment: .bottomLeading) {
|
||||
Group {
|
||||
if tool.isReordering {
|
||||
doneButton
|
||||
} else {
|
||||
newPenButton
|
||||
}
|
||||
}
|
||||
.offset(x: 60, y: 10)
|
||||
newPenButton
|
||||
.offset(x: 60, y: 10)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,20 +73,34 @@ struct PenToolView: View {
|
||||
tool.selectPen(pen)
|
||||
}
|
||||
}
|
||||
.disabled(tool.isReordering)
|
||||
.contextMenu(if: pen.strokeStyle != .eraser && !tool.isReordering) {
|
||||
Button {
|
||||
tool.isReordering = true
|
||||
} label: {
|
||||
Label("Rearrange", systemImage: "arrow.up.arrow.down.circle")
|
||||
}
|
||||
Button(role: .destructive) {
|
||||
tool.removePen(pen)
|
||||
} label: {
|
||||
Label("Delete", systemImage: "trash")
|
||||
.contextMenu(if: pen.strokeStyle != .eraser) {
|
||||
ControlGroup {
|
||||
Button {
|
||||
let originalPen = pen
|
||||
let pen = PenObject.createObject(\.viewContext, penStyle: originalPen.style)
|
||||
pen.color = originalPen.color
|
||||
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") }
|
||||
)
|
||||
}
|
||||
}
|
||||
.controlGroupStyle(.menu)
|
||||
}
|
||||
.onDrag(if: pen.strokeStyle != .eraser && tool.isReordering) {
|
||||
.onDrag(if: pen.strokeStyle != .eraser) {
|
||||
tool.draggedPen = pen
|
||||
return NSItemProvider(contentsOf: URL(string: pen.id)) ?? NSItemProvider()
|
||||
} preview: {
|
||||
@@ -149,22 +134,6 @@ struct PenToolView: View {
|
||||
.hoverEffect(.lift)
|
||||
}
|
||||
|
||||
var doneButton: some View {
|
||||
Button {
|
||||
tool.isReordering = false
|
||||
} label: {
|
||||
Image(systemName: "xmark.circle")
|
||||
.font(.title2)
|
||||
.padding(1)
|
||||
.contentShape(.circle)
|
||||
.background {
|
||||
Circle()
|
||||
.fill(.white)
|
||||
}
|
||||
}
|
||||
.hoverEffect(.lift)
|
||||
}
|
||||
|
||||
func penPreview(_ pen: Pen) -> some View {
|
||||
ZStack {
|
||||
if let tip = pen.style.icon.tip {
|
||||
|
||||
Reference in New Issue
Block a user