feat: refine canvas tool bar

This commit is contained in:
dscyrescotti
2024-07-11 01:09:34 +07:00
parent f36cc3e20c
commit 742b327200
4 changed files with 68 additions and 75 deletions
@@ -11,7 +11,7 @@ import AVFoundation
struct ElementToolbar: View { struct ElementToolbar: View {
@Environment(\.horizontalSizeClass) var horizontalSizeClass @Environment(\.horizontalSizeClass) var horizontalSizeClass
let size: CGFloat let size: CGFloat = 40
@ObservedObject var tool: Tool @ObservedObject var tool: Tool
@ObservedObject var canvas: Canvas @ObservedObject var canvas: Canvas
@@ -32,6 +32,9 @@ struct ElementToolbar: View {
ZStack(alignment: .bottom) { ZStack(alignment: .bottom) {
if tool.selection == .photo { if tool.selection == .photo {
photoOption photoOption
.padding(.bottom, 10)
.frame(maxWidth: .infinity)
.transition(.move(edge: .bottom).combined(with: .blurReplace))
} else { } else {
compactToolbar compactToolbar
} }
@@ -263,9 +266,6 @@ struct ElementToolbar: View {
RoundedRectangle(cornerRadius: 8) RoundedRectangle(cornerRadius: 8)
.fill(.regularMaterial) .fill(.regularMaterial)
} }
.padding(.bottom, 10)
.frame(maxWidth: .infinity)
.transition(.move(edge: .bottom).combined(with: .blurReplace))
} }
func openCamera() { func openCamera() {
+5 -5
View File
@@ -19,7 +19,7 @@ struct MemoView: View {
@FocusState var textFieldState: Bool @FocusState var textFieldState: Bool
let memo: MemoObject let memo: MemoObject
let size: CGFloat = 32 let size: CGFloat = 40
init(memo: MemoObject) { init(memo: MemoObject) {
self.memo = memo self.memo = memo
@@ -42,7 +42,7 @@ struct MemoView: View {
#endif #endif
} }
.overlay(alignment: .top) { .overlay(alignment: .top) {
Toolbar(size: size, memo: memo, tool: tool, canvas: canvas, history: history) Toolbar(memo: memo, tool: tool, canvas: canvas, history: history)
} }
.disabled(textFieldState || tool.isLoadingPhoto) .disabled(textFieldState || tool.isLoadingPhoto)
.disabled(canvas.state == .loading || canvas.state == .closing) .disabled(canvas.state == .loading || canvas.state == .closing)
@@ -69,7 +69,7 @@ struct MemoView: View {
.overlay(alignment: .bottomTrailing) { .overlay(alignment: .bottomTrailing) {
switch tool.selection { switch tool.selection {
case .pen: case .pen:
PenDock(tool: tool, canvas: canvas, size: size) PenDock(tool: tool, canvas: canvas)
case .photo: case .photo:
if let photoItem = tool.selectedPhotoItem { if let photoItem = tool.selectedPhotoItem {
PhotoPreview(photoItem: photoItem, tool: tool) PhotoPreview(photoItem: photoItem, tool: tool)
@@ -90,7 +90,7 @@ struct MemoView: View {
.overlay(alignment: .bottom) { .overlay(alignment: .bottom) {
switch tool.selection { switch tool.selection {
case .pen: case .pen:
PenDock(tool: tool, canvas: canvas, size: size) PenDock(tool: tool, canvas: canvas)
.transition(.move(edge: .bottom).combined(with: .blurReplace)) .transition(.move(edge: .bottom).combined(with: .blurReplace))
case .photo: case .photo:
if let photoItem = tool.selectedPhotoItem { if let photoItem = tool.selectedPhotoItem {
@@ -104,7 +104,7 @@ struct MemoView: View {
} }
.overlay(alignment: .bottom) { .overlay(alignment: .bottom) {
if tool.selection != .pen { if tool.selection != .pen {
ElementToolbar(size: size, tool: tool, canvas: canvas) ElementToolbar(tool: tool, canvas: canvas)
.transition(.move(edge: .bottom).combined(with: .blurReplace)) .transition(.move(edge: .bottom).combined(with: .blurReplace))
} }
} }
+55 -61
View File
@@ -12,14 +12,14 @@ struct PenDock: View {
@ObservedObject var tool: Tool @ObservedObject var tool: Tool
@ObservedObject var canvas: Canvas @ObservedObject var canvas: Canvas
let size: CGFloat let size: CGFloat = 40
let penPropertySize: CGFloat = 32
var width: CGFloat { var width: CGFloat {
horizontalSizeClass == .compact ? 25 : 90 horizontalSizeClass == .compact ? size / 2 : size
} }
var height: CGFloat { var height: CGFloat {
horizontalSizeClass == .compact ? 75 : 30 horizontalSizeClass == .compact ? size : size / 2
} }
var factor: CGFloat = 0.9
@State var refreshScrollId: UUID = UUID() @State var refreshScrollId: UUID = UUID()
@State var opensColorPicker: Bool = false @State var opensColorPicker: Bool = false
@@ -29,38 +29,45 @@ struct PenDock: View {
var body: some View { var body: some View {
#if os(macOS) #if os(macOS)
VStack(alignment: .trailing) { GeometryReader { proxy in
penPropertyTool VStack(alignment: .trailing, spacing: 5) {
penItemList penPropertyTool
penItemList
.frame(maxWidth: proxy.size.width * 0.4)
}
.fixedSize()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .trailing)
} }
.fixedSize()
.frame(maxHeight: .infinity)
.padding(10) .padding(10)
.transition(.move(edge: .trailing).combined(with: .blurReplace)) .transition(.move(edge: .trailing).combined(with: .blurReplace))
#else #else
if horizontalSizeClass == .regular { if horizontalSizeClass == .regular {
VStack(alignment: .trailing) { GeometryReader { proxy in
penPropertyTool VStack(alignment: .trailing, spacing: 5) {
penItemList penPropertyTool
penItemList
.frame(maxHeight: proxy.size.height * 0.4)
}
.fixedSize()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .trailing)
} }
.fixedSize()
.frame(maxHeight: .infinity)
.padding(10) .padding(10)
.transition(.move(edge: .trailing).combined(with: .blurReplace)) .transition(.move(edge: .trailing).combined(with: .blurReplace))
} else { } else {
GeometryReader { proxy in GeometryReader { proxy in
HStack(alignment: .bottom, spacing: 10) { HStack(alignment: .bottom, spacing: 10) {
newPenButton newPenButton
.frame(height: height * factor - 18) .padding(.leading, 10)
.frame(height: height)
compactPenItemList compactPenItemList
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
compactPenPropertyTool HStack(spacing: 0) {
HStack(spacing: 5) { compactPenPropertyTool
Divider() Divider()
.padding(.vertical, 4) .padding(.vertical, 4)
.frame(height: size) .frame(height: size)
.foregroundStyle(Color.accentColor) .foregroundStyle(Color.accentColor)
.frame(height: height * factor - 18) .padding(.leading, 8)
Button { Button {
withAnimation { withAnimation {
tool.selectTool(.hand) tool.selectTool(.hand)
@@ -76,15 +83,13 @@ struct PenDock: View {
#else #else
.buttonStyle(.plain) .buttonStyle(.plain)
#endif #endif
.frame(height: height * factor - 18)
} }
} }
.padding(.horizontal, 10)
.clipped() .clipped()
.background(alignment: .bottom) { .background(alignment: .bottom) {
RoundedRectangle(cornerRadius: 8) RoundedRectangle(cornerRadius: 8)
.fill(.regularMaterial) .fill(.regularMaterial)
.frame(height: height * factor - 18) .frame(height: height)
} }
.padding([.horizontal, .bottom], 10) .padding([.horizontal, .bottom], 10)
.frame(maxWidth: min(proxy.size.height, proxy.size.width), maxHeight: .infinity, alignment: .bottom) .frame(maxWidth: min(proxy.size.height, proxy.size.width), maxHeight: .infinity, alignment: .bottom)
@@ -100,7 +105,7 @@ struct PenDock: View {
VStack(alignment: .trailing, spacing: 0) { VStack(alignment: .trailing, spacing: 0) {
ScrollViewReader { proxy in ScrollViewReader { proxy in
ScrollView(.vertical, showsIndicators: false) { ScrollView(.vertical, showsIndicators: false) {
LazyVStack(spacing: 0) { LazyVStack(spacing: 5) {
ForEach(tool.pens) { pen in ForEach(tool.pens) { pen in
penItem(pen) penItem(pen)
.id(pen.id) .id(pen.id)
@@ -110,7 +115,7 @@ struct PenDock: View {
} }
} }
} }
.padding(.vertical, 10) .padding(.vertical, 5)
.id(refreshScrollId) .id(refreshScrollId)
} }
.onReceive(tool.scrollPublisher) { id in .onReceive(tool.scrollPublisher) { id in
@@ -122,17 +127,14 @@ struct PenDock: View {
} }
} }
newPenButton newPenButton
.padding(.vertical, 10) .padding(.vertical, 5)
.frame(width: width * factor - 18) .frame(width: width)
} }
.frame(maxHeight: ((height * factor + 10) * 7) + 20)
.fixedSize()
.background(alignment: .trailing) { .background(alignment: .trailing) {
RoundedRectangle(cornerRadius: 8) RoundedRectangle(cornerRadius: 8)
.fill(.regularMaterial) .fill(.regularMaterial)
.frame(width: width * factor - 18) .frame(width: width)
} }
.clipShape(.rect(cornerRadii: .init(bottomTrailing: 8, topTrailing: 8)))
} }
@ViewBuilder @ViewBuilder
@@ -174,15 +176,10 @@ struct PenDock: View {
Image(pen.style.icon.base) Image(pen.style.icon.base)
.resizable() .resizable()
} }
.frame(width: width * factor, height: height * factor) .frame(width: width * 1.2, height: height * 0.9)
.padding(.vertical, 5) .padding(.vertical, 5)
.contentShape(.rect(cornerRadii: .init(topLeading: 10, bottomLeading: 10)))
.onTapGesture {
if tool.selectedPen !== pen {
tool.selectPen(pen)
}
}
.padding(.leading, 10) .padding(.leading, 10)
.contentShape(.rect)
.contextMenu(if: pen.strokeStyle != .eraser) { .contextMenu(if: pen.strokeStyle != .eraser) {
ControlGroup { ControlGroup {
Button { Button {
@@ -235,7 +232,13 @@ struct PenDock: View {
.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 : 16)
.contentShape(.rect)
.onTapGesture {
if tool.selectedPen !== pen {
tool.selectPen(pen)
}
}
} }
func compactPenItem(_ pen: Pen) -> some View { func compactPenItem(_ pen: Pen) -> some View {
@@ -250,7 +253,7 @@ struct PenDock: View {
Image(pen.style.compactIcon.base) Image(pen.style.compactIcon.base)
.resizable() .resizable()
} }
.frame(width: width * factor, height: height * factor) .frame(width: width * 0.9, height: height * 1.2)
.padding(.top, 5) .padding(.top, 5)
.contentShape(.rect(cornerRadii: .init(topLeading: 10, bottomLeading: 10))) .contentShape(.rect(cornerRadii: .init(topLeading: 10, bottomLeading: 10)))
.onTapGesture { .onTapGesture {
@@ -258,7 +261,7 @@ struct PenDock: View {
tool.selectPen(pen) tool.selectPen(pen)
} }
} }
.padding(.horizontal, 5) .padding(.horizontal, 6)
.contextMenu(if: pen.strokeStyle != .eraser) { .contextMenu(if: pen.strokeStyle != .eraser) {
ControlGroup { ControlGroup {
Button { Button {
@@ -311,7 +314,7 @@ struct PenDock: View {
.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(y: tool.selectedPen === pen ? 0 : 25) .offset(y: tool.selectedPen === pen ? 0 : 16)
} }
@ViewBuilder @ViewBuilder
@@ -323,32 +326,32 @@ struct PenDock: View {
} }
penThicknessPicker(pen) penThicknessPicker(pen)
} }
.padding(10) .padding(.vertical, 5)
.frame(width: width * factor - 18) .frame(width: width)
.background { .background {
RoundedRectangle(cornerRadius: 8) RoundedRectangle(cornerRadius: 8)
.fill(.regularMaterial) .fill(.regularMaterial)
} }
} else { } else {
Color.clear Color.clear
.frame(width: width * factor - 18, height: 50) .frame(width: width, height: 50)
} }
} }
@ViewBuilder @ViewBuilder
var compactPenPropertyTool: some View { var compactPenPropertyTool: some View {
if let pen = tool.selectedPen { if let pen = tool.selectedPen {
HStack(spacing: 10) { HStack(spacing: 8) {
penThicknessPicker(pen) penThicknessPicker(pen)
.frame(width: size) .frame(width: penPropertySize)
.rotationEffect(.degrees(-90)) .rotationEffect(.degrees(-90))
if pen.strokeStyle == .marker { if pen.strokeStyle == .marker {
penColorPicker(pen) penColorPicker(pen)
.frame(width: size) .frame(width: penPropertySize)
.transition(.move(edge: .trailing).combined(with: .opacity)) .transition(.move(edge: .trailing).combined(with: .opacity))
} }
} }
.frame(height: height * factor - 18) .frame(height: height)
} }
} }
@@ -374,7 +377,7 @@ struct PenDock: View {
.background(baseColor) .background(baseColor)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8)) .contentShape(.rect(cornerRadius: 8))
.frame(height: size) .frame(width: penPropertySize, height: penPropertySize)
.overlay { .overlay {
RoundedRectangle(cornerRadius: 8) RoundedRectangle(cornerRadius: 8)
.stroke(Color.gray, lineWidth: 0.4) .stroke(Color.gray, lineWidth: 0.4)
@@ -417,11 +420,6 @@ struct PenDock: View {
tool.objectWillChange.send() tool.objectWillChange.send()
} }
) )
#if os(macOS)
let _width = width * factor - 38
#else
let _width = horizontalSizeClass == .compact ? self.size : width * factor - 38
#endif
ScrollViewReader { proxy in ScrollViewReader { proxy in
ScrollView(showsIndicators: false) { ScrollView(showsIndicators: false) {
LazyVStack(spacing: 0) { LazyVStack(spacing: 0) {
@@ -430,17 +428,13 @@ struct PenDock: View {
Circle() Circle()
.foregroundStyle(.primary) .foregroundStyle(.primary)
.frame(width: size, height: size) .frame(width: size, height: size)
.frame(width: _width, height: self.size) .frame(width: penPropertySize, height: penPropertySize)
.contentShape(.rect) .contentShape(.rect)
.id(step) .id(step)
} }
} }
} }
#if os(macOS) .frame(width: penPropertySize, height: penPropertySize)
.frame(height: size)
#else
.frame(width: _width, height: size)
#endif
.background(.gray.quaternary) .background(.gray.quaternary)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.scrollPosition(id: selection, anchor: .center) .scrollPosition(id: selection, anchor: .center)
@@ -490,7 +484,7 @@ struct PenDock: View {
Image(pen.style.icon.base) Image(pen.style.icon.base)
.resizable() .resizable()
} }
.frame(width: width * factor, height: height * factor) .frame(width: width * 1.2, height: height * 0.9)
.padding(.vertical, 5) .padding(.vertical, 5)
.padding(.leading, 10) .padding(.leading, 10)
} }
@@ -506,7 +500,7 @@ struct PenDock: View {
Image(pen.style.compactIcon.base) Image(pen.style.compactIcon.base)
.resizable() .resizable()
} }
.frame(width: width * factor, height: height * factor) .frame(width: width * 0.9, height: height * 1.2)
.padding(.top, 5) .padding(.top, 5)
.padding(.horizontal, 5) .padding(.horizontal, 5)
} }
+4 -5
View File
@@ -20,11 +20,10 @@ struct Toolbar: View {
@FocusState var textFieldState: Bool @FocusState var textFieldState: Bool
let size: CGFloat let size: CGFloat = 40
let memo: MemoObject let memo: MemoObject
init(size: CGFloat, memo: MemoObject, tool: Tool, canvas: Canvas, history: History) { init(memo: MemoObject, tool: Tool, canvas: Canvas, history: History) {
self.size = size
self.memo = memo self.memo = memo
self.tool = tool self.tool = tool
self.canvas = canvas self.canvas = canvas
@@ -40,10 +39,10 @@ struct Toolbar: View {
} }
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
#if os(macOS) #if os(macOS)
ElementToolbar(size: size, tool: tool, canvas: canvas) ElementToolbar(tool: tool, canvas: canvas)
#else #else
if horizontalSizeClass == .regular { if horizontalSizeClass == .regular {
ElementToolbar(size: size, tool: tool, canvas: canvas) ElementToolbar(tool: tool, canvas: canvas)
} }
#endif #endif
HStack(spacing: 5) { HStack(spacing: 5) {