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