mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-07-07 13:25:09 +02:00
feat: add canvas lock button
This commit is contained in:
@@ -29,6 +29,7 @@ final class Canvas: ObservableObject, Identifiable, @unchecked Sendable {
|
|||||||
|
|
||||||
@Published var state: State = .initial
|
@Published var state: State = .initial
|
||||||
@Published var zoomScale: CGFloat = .zero
|
@Published var zoomScale: CGFloat = .zero
|
||||||
|
@Published var locksCanvas: Bool = false
|
||||||
|
|
||||||
let zoomPublisher = PassthroughSubject<CGFloat, Never>()
|
let zoomPublisher = PassthroughSubject<CGFloat, Never>()
|
||||||
|
|
||||||
|
|||||||
@@ -168,6 +168,12 @@ extension CanvasViewController {
|
|||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.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)
|
||||||
@@ -308,6 +314,11 @@ extension CanvasViewController {
|
|||||||
func zoomChanged(_ zoomScale: CGFloat) {
|
func zoomChanged(_ zoomScale: CGFloat) {
|
||||||
scrollView.setZoomScale(zoomScale, animated: true)
|
scrollView.setZoomScale(zoomScale, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lockModeChanged(_ state: Bool) {
|
||||||
|
scrollView.isScrollEnabled = !state
|
||||||
|
scrollView.pinchGestureRecognizer?.isEnabled = !state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CanvasViewController {
|
extension CanvasViewController {
|
||||||
|
|||||||
@@ -61,31 +61,34 @@ struct MemoView: View {
|
|||||||
let lowerBound: CGFloat = 10
|
let lowerBound: CGFloat = 10
|
||||||
let zoomScale: CGFloat = (((canvas.zoomScale - canvas.minimumZoomScale) * (upperBound - lowerBound) / (canvas.maximumZoomScale - canvas.minimumZoomScale)) + lowerBound).rounded()
|
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]
|
let zoomScales: [Int] = [400, 200, 100, 75, 50, 25, 10]
|
||||||
Menu {
|
if !canvas.locksCanvas {
|
||||||
ForEach(zoomScales, id: \.self) { scale in
|
Menu {
|
||||||
Button {
|
ForEach(zoomScales, id: \.self) { scale in
|
||||||
let zoomScale = ((CGFloat(scale) - lowerBound) * (canvas.maximumZoomScale - canvas.minimumZoomScale) / (upperBound - lowerBound)) + canvas.minimumZoomScale
|
Button {
|
||||||
canvas.zoomPublisher.send(zoomScale)
|
let zoomScale = ((CGFloat(scale) - lowerBound) * (canvas.maximumZoomScale - canvas.minimumZoomScale) / (upperBound - lowerBound)) + canvas.minimumZoomScale
|
||||||
} label: {
|
canvas.zoomPublisher.send(zoomScale)
|
||||||
Label {
|
} label: {
|
||||||
Text(scale, format: .percent)
|
Label {
|
||||||
} icon: {
|
Text(scale, format: .percent)
|
||||||
if CGFloat(scale) == zoomScale {
|
} icon: {
|
||||||
Image(systemName: "checkmark")
|
if CGFloat(scale) == zoomScale {
|
||||||
|
Image(systemName: "checkmark")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
.font(.headline)
|
||||||
}
|
}
|
||||||
.font(.headline)
|
|
||||||
}
|
}
|
||||||
|
} label: {
|
||||||
|
Text(zoomScale / 100, format: .percent)
|
||||||
|
.frame(width: 45)
|
||||||
|
.font(.subheadline)
|
||||||
|
.padding(.horizontal, size / 2.5)
|
||||||
|
.frame(height: size)
|
||||||
|
.background(.regularMaterial)
|
||||||
|
.clipShape(.rect(cornerRadius: 8))
|
||||||
|
.padding(10)
|
||||||
}
|
}
|
||||||
} label: {
|
.transition(.move(edge: .bottom).combined(with: .blurReplace))
|
||||||
Text(zoomScale / 100, format: .percent)
|
|
||||||
.frame(width: 45)
|
|
||||||
.font(.subheadline)
|
|
||||||
.padding(.horizontal, size / 2.5)
|
|
||||||
.frame(height: size)
|
|
||||||
.background(.regularMaterial)
|
|
||||||
.clipShape(.rect(cornerRadius: 8))
|
|
||||||
.padding(10)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import SwiftUI
|
|||||||
|
|
||||||
struct PenDock: 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
|
||||||
@@ -18,13 +19,16 @@ struct PenDock: View {
|
|||||||
@State var opensColorPicker: Bool = false
|
@State var opensColorPicker: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .trailing) {
|
if !canvas.locksCanvas {
|
||||||
penPropertyTool
|
VStack(alignment: .trailing) {
|
||||||
penItemList
|
penPropertyTool
|
||||||
|
penItemList
|
||||||
|
}
|
||||||
|
.fixedSize()
|
||||||
|
.frame(maxHeight: .infinity)
|
||||||
|
.padding(10)
|
||||||
|
.transition(.move(edge: .trailing).combined(with: .blurReplace))
|
||||||
}
|
}
|
||||||
.fixedSize()
|
|
||||||
.frame(maxHeight: .infinity)
|
|
||||||
.padding(10)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var penItemList: some View {
|
var penItemList: some View {
|
||||||
@@ -156,7 +160,7 @@ struct PenDock: View {
|
|||||||
RoundedRectangle(cornerRadius: 8)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
.fill(.regularMaterial)
|
.fill(.regularMaterial)
|
||||||
}
|
}
|
||||||
.transition(.move(edge: .trailing).combined(with: .opacity))
|
.transition(.move(edge: .trailing).combined(with: .blurReplace))
|
||||||
} else {
|
} else {
|
||||||
Color.clear
|
Color.clear
|
||||||
.frame(width: width * factor - 18, height: 50)
|
.frame(width: width * factor - 18, height: 50)
|
||||||
|
|||||||
@@ -28,10 +28,15 @@ struct Toolbar: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 5) {
|
HStack(spacing: 5) {
|
||||||
closeButton
|
if !canvas.locksCanvas {
|
||||||
titleField
|
closeButton
|
||||||
|
titleField
|
||||||
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
historyControl
|
if !canvas.locksCanvas {
|
||||||
|
historyControl
|
||||||
|
}
|
||||||
|
lockButton
|
||||||
}
|
}
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.padding(10)
|
.padding(10)
|
||||||
@@ -49,6 +54,7 @@ struct Toolbar: View {
|
|||||||
}
|
}
|
||||||
.hoverEffect(.lift)
|
.hoverEffect(.lift)
|
||||||
.disabled(textFieldState)
|
.disabled(textFieldState)
|
||||||
|
.transition(.move(edge: .top).combined(with: .blurReplace))
|
||||||
}
|
}
|
||||||
|
|
||||||
var titleField: some View {
|
var titleField: some View {
|
||||||
@@ -68,6 +74,7 @@ struct Toolbar: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.transition(.move(edge: .top).combined(with: .blurReplace))
|
||||||
}
|
}
|
||||||
|
|
||||||
var historyControl: some View {
|
var historyControl: some View {
|
||||||
@@ -94,6 +101,30 @@ struct Toolbar: View {
|
|||||||
.background(.regularMaterial)
|
.background(.regularMaterial)
|
||||||
.clipShape(.rect(cornerRadius: 8))
|
.clipShape(.rect(cornerRadius: 8))
|
||||||
.disabled(textFieldState)
|
.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() {
|
func closeMemo() {
|
||||||
|
|||||||
Reference in New Issue
Block a user