feat: add zoom scale options

This commit is contained in:
dscyrescotti
2024-05-19 23:50:21 +07:00
parent 095e5fa515
commit 819c7dc321
4 changed files with 74 additions and 14 deletions

View File

@@ -24,17 +24,19 @@ 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
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 +94,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)
}
} }
} }

View File

@@ -162,6 +162,12 @@ extension CanvasViewController {
} }
.store(in: &cancellables) .store(in: &cancellables)
canvas.zoomPublisher
.sink { [weak self] zoomScale in
self?.zoomChanged(zoomScale)
}
.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 +304,12 @@ extension CanvasViewController {
} }
} }
extension CanvasViewController {
func zoomChanged(_ zoomScale: CGFloat) {
scrollView.setZoomScale(zoomScale, animated: true)
}
}
extension CanvasViewController { extension CanvasViewController {
func historyUndid() { func historyUndid() {
guard history.undo() else { return } guard history.undo() else { return }

View File

@@ -17,6 +17,8 @@ struct MemoView: View {
@State var title: String @State var title: String
@FocusState var textFieldState: Bool @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.title = memo.title
@@ -30,9 +32,12 @@ struct MemoView: View {
.overlay(alignment: .trailing) { .overlay(alignment: .trailing) {
PenDock() PenDock()
} }
.overlay(alignment: .bottomLeading) {
zoomControl
}
.disabled(textFieldState) .disabled(textFieldState)
.overlay(alignment: .top) { .overlay(alignment: .top) {
Toolbar(memo: memo) Toolbar(memo: memo, size: size)
} }
.disabled(canvas.state == .loading || canvas.state == .closing) .disabled(canvas.state == .loading || canvas.state == .closing)
.overlay { .overlay {
@@ -50,6 +55,40 @@ struct MemoView: View {
.environmentObject(history) .environmentObject(history)
} }
@ViewBuilder
var zoomControl: some View {
let upperBound: CGFloat = 400
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]
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: {
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)
}
}
func loadingIndicator(_ title: String) -> some View { func loadingIndicator(_ title: String) -> some View {
ProgressView { ProgressView {
Text(title) Text(title)

View File

@@ -12,13 +12,17 @@ struct Toolbar: View {
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
@EnvironmentObject var history: History @EnvironmentObject var history: History
@EnvironmentObject var canvas: Canvas
@State var memo: MemoObject @State var memo: MemoObject
@State var title: String @State var title: String
@FocusState var textFieldState: Bool @FocusState var textFieldState: Bool
init(memo: MemoObject) { let size: CGFloat
init(memo: MemoObject, size: CGFloat) {
self.memo = memo self.memo = memo
self.size = size
self.title = memo.title self.title = memo.title
} }
@@ -27,7 +31,7 @@ struct Toolbar: View {
closeButton closeButton
titleField titleField
Spacer() Spacer()
historyTool historyControl
} }
.font(.subheadline) .font(.subheadline)
.padding(10) .padding(10)
@@ -39,7 +43,7 @@ struct Toolbar: View {
} label: { } label: {
Image(systemName: "xmark") Image(systemName: "xmark")
.contentShape(.circle) .contentShape(.circle)
.padding(10) .frame(width: size, height: size)
.background(.regularMaterial) .background(.regularMaterial)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
} }
@@ -51,9 +55,8 @@ struct Toolbar: View {
TextField("", text: $title) TextField("", text: $title)
.focused($textFieldState) .focused($textFieldState)
.textFieldStyle(.plain) .textFieldStyle(.plain)
.padding(.vertical, 5) .padding(.horizontal, size / 2.5)
.padding(.horizontal, 10) .frame(width: 140, height: size)
.frame(width: 120)
.background(.regularMaterial) .background(.regularMaterial)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.onChange(of: textFieldState) { oldValue, newValue in .onChange(of: textFieldState) { oldValue, newValue in
@@ -67,12 +70,13 @@ struct Toolbar: View {
} }
} }
var historyTool: some View { var historyControl: some View {
HStack { HStack {
Button { Button {
history.historyPublisher.send(.undo) history.historyPublisher.send(.undo)
} label: { } label: {
Image(systemName: "arrow.uturn.backward.circle") Image(systemName: "arrow.uturn.backward.circle")
.contentShape(.circle) .contentShape(.circle)
} }
.hoverEffect(.lift) .hoverEffect(.lift)
@@ -86,7 +90,7 @@ struct Toolbar: View {
.hoverEffect(.lift) .hoverEffect(.lift)
.disabled(history.redoDisabled) .disabled(history.redoDisabled)
} }
.padding(10) .frame(width: size * 2, height: size)
.background(.regularMaterial) .background(.regularMaterial)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.disabled(textFieldState) .disabled(textFieldState)