feat: revamp element tool bar

This commit is contained in:
dscyrescotti
2024-07-09 22:08:42 +07:00
parent a5ef50eb24
commit f36cc3e20c
3 changed files with 119 additions and 62 deletions

View File

@@ -32,18 +32,10 @@ struct ElementToolbar: View {
ZStack(alignment: .bottom) { ZStack(alignment: .bottom) {
if tool.selection == .photo { if tool.selection == .photo {
photoOption photoOption
.background {
RoundedRectangle(cornerRadius: 8)
.fill(.regularMaterial)
}
.padding(.bottom, 10)
.frame(maxWidth: .infinity)
.transition(.move(edge: .bottom).combined(with: .blurReplace))
} else { } else {
compactToolbar compactToolbar
} }
} }
.padding(.bottom, 10)
} }
#endif #endif
} }
@@ -228,7 +220,6 @@ struct ElementToolbar: View {
openCamera() openCamera()
} label: { } label: {
Image(systemName: "camera.fill") Image(systemName: "camera.fill")
.contentShape(.circle)
.frame(width: size, height: size) .frame(width: size, height: size)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8)) .contentShape(.rect(cornerRadius: 8))
@@ -237,7 +228,6 @@ struct ElementToolbar: View {
#endif #endif
PhotosPicker(selection: $photosPickerItem, matching: .images, preferredItemEncoding: .compatible) { PhotosPicker(selection: $photosPickerItem, matching: .images, preferredItemEncoding: .compatible) {
Image(systemName: "photo.fill.on.rectangle.fill") Image(systemName: "photo.fill.on.rectangle.fill")
.contentShape(.circle)
.frame(width: size, height: size) .frame(width: size, height: size)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8)) .contentShape(.rect(cornerRadius: 8))
@@ -247,7 +237,35 @@ struct ElementToolbar: View {
#else #else
.buttonStyle(.plain) .buttonStyle(.plain)
#endif #endif
if horizontalSizeClass == .compact {
Divider()
.padding(.vertical, 4)
.frame(height: size)
.foregroundStyle(Color.accentColor)
Button {
withAnimation {
tool.selectTool(.hand)
}
} label: {
Image(systemName: "xmark")
.frame(width: size, height: size)
.clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
}
#if os(iOS)
.hoverEffect(.lift)
#else
.buttonStyle(.plain)
#endif
}
} }
.background {
RoundedRectangle(cornerRadius: 8)
.fill(.regularMaterial)
}
.padding(.bottom, 10)
.frame(maxWidth: .infinity)
.transition(.move(edge: .bottom).combined(with: .blurReplace))
} }
func openCamera() { func openCamera() {

View File

@@ -108,25 +108,25 @@ struct MemoView: View {
.transition(.move(edge: .bottom).combined(with: .blurReplace)) .transition(.move(edge: .bottom).combined(with: .blurReplace))
} }
} }
.overlay(alignment: .bottom) { // .overlay(alignment: .bottom) {
if tool.selection != .hand { // if tool.selection != .hand {
Button { // Button {
withAnimation { // withAnimation {
tool.selectTool(.hand) // tool.selectTool(.hand)
} // }
} label: { // } label: {
Image(systemName: "chevron.compact.down") // Image(systemName: "chevron.compact.down")
.font(.headline) // .font(.headline)
.frame(width: 80) // .frame(width: 80)
.padding(5) // .padding(5)
.background(.regularMaterial) // .background(.regularMaterial)
.clipShape(.capsule) // .clipShape(.capsule)
.contentShape(.capsule) // .contentShape(.capsule)
} // }
.offset(y: 5) // .offset(y: 5)
.transition(.move(edge: .bottom).combined(with: .blurReplace)) // .transition(.move(edge: .bottom).combined(with: .blurReplace))
} // }
} // }
} }
@ViewBuilder @ViewBuilder

View File

@@ -14,10 +14,10 @@ struct PenDock: View {
let size: CGFloat let size: CGFloat
var width: CGFloat { var width: CGFloat {
horizontalSizeClass == .compact ? 30 : 90 horizontalSizeClass == .compact ? 25 : 90
} }
var height: CGFloat { var height: CGFloat {
horizontalSizeClass == .compact ? 90 : 30 horizontalSizeClass == .compact ? 75 : 30
} }
var factor: CGFloat = 0.9 var factor: CGFloat = 0.9
@@ -55,6 +55,29 @@ struct PenDock: View {
compactPenItemList compactPenItemList
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
compactPenPropertyTool compactPenPropertyTool
HStack(spacing: 5) {
Divider()
.padding(.vertical, 4)
.frame(height: size)
.foregroundStyle(Color.accentColor)
.frame(height: height * factor - 18)
Button {
withAnimation {
tool.selectTool(.hand)
}
} label: {
Image(systemName: "xmark")
.frame(width: size, height: size)
.clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
}
#if os(iOS)
.hoverEffect(.lift)
#else
.buttonStyle(.plain)
#endif
.frame(height: height * factor - 18)
}
} }
.padding(.horizontal, 10) .padding(.horizontal, 10)
.clipped() .clipped()
@@ -63,8 +86,7 @@ struct PenDock: View {
.fill(.regularMaterial) .fill(.regularMaterial)
.frame(height: height * factor - 18) .frame(height: height * factor - 18)
} }
.padding(.horizontal, 10) .padding([.horizontal, .bottom], 10)
.padding(.bottom, 20)
.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)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
} }
@@ -75,30 +97,35 @@ struct PenDock: View {
@ViewBuilder @ViewBuilder
var penItemList: some View { var penItemList: some View {
ScrollViewReader { proxy in VStack(alignment: .trailing, spacing: 0) {
ScrollView(.vertical, showsIndicators: false) { ScrollViewReader { proxy in
LazyVStack(spacing: 0) { ScrollView(.vertical, showsIndicators: false) {
ForEach(tool.pens) { pen in LazyVStack(spacing: 0) {
penItem(pen) ForEach(tool.pens) { pen in
.id(pen.id) penItem(pen)
.scrollTransition { content, phase in .id(pen.id)
content .scrollTransition { content, phase in
.scaleEffect(phase.isIdentity ? 1 : 0.04, anchor: .trailing) content
} .scaleEffect(phase.isIdentity ? 1 : 0.04, anchor: .trailing)
}
}
}
.padding(.vertical, 10)
.id(refreshScrollId)
}
.onReceive(tool.scrollPublisher) { id in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
withAnimation {
proxy.scrollTo(id)
}
} }
} }
}
newPenButton
.padding(.vertical, 10) .padding(.vertical, 10)
.id(refreshScrollId) .frame(width: width * factor - 18)
}
.onReceive(tool.scrollPublisher) { id in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
withAnimation {
proxy.scrollTo(id)
}
}
}
} }
.frame(maxHeight: ((height * factor + 10) * 6) + 20) .frame(maxHeight: ((height * factor + 10) * 7) + 20)
.fixedSize() .fixedSize()
.background(alignment: .trailing) { .background(alignment: .trailing) {
RoundedRectangle(cornerRadius: 8) RoundedRectangle(cornerRadius: 8)
@@ -106,10 +133,6 @@ struct PenDock: View {
.frame(width: width * factor - 18) .frame(width: width * factor - 18)
} }
.clipShape(.rect(cornerRadii: .init(bottomTrailing: 8, topTrailing: 8))) .clipShape(.rect(cornerRadii: .init(bottomTrailing: 8, topTrailing: 8)))
.overlay(alignment: .bottomLeading) {
newPenButton
.offset(x: 15, y: 10)
}
} }
@ViewBuilder @ViewBuilder
@@ -126,7 +149,7 @@ struct PenDock: View {
} }
} }
} }
.padding(.horizontal, 10) .padding(.horizontal, 5)
.id(refreshScrollId) .id(refreshScrollId)
} }
.onReceive(tool.scrollPublisher) { id in .onReceive(tool.scrollPublisher) { id in
@@ -235,7 +258,7 @@ struct PenDock: View {
tool.selectPen(pen) tool.selectPen(pen)
} }
} }
.padding(.horizontal, 10) .padding(.horizontal, 5)
.contextMenu(if: pen.strokeStyle != .eraser) { .contextMenu(if: pen.strokeStyle != .eraser) {
ControlGroup { ControlGroup {
Button { Button {
@@ -273,7 +296,7 @@ struct PenDock: View {
} }
.controlGroupStyle(.menu) .controlGroupStyle(.menu)
} preview: { } preview: {
penPreview(pen) compactPenPreview(pen)
.drawingGroup() .drawingGroup()
#if os(iOS) #if os(iOS)
.contentShape(.contextMenuPreview, .rect(cornerRadius: 10)) .contentShape(.contextMenuPreview, .rect(cornerRadius: 10))
@@ -284,7 +307,7 @@ struct PenDock: View {
tool.draggedPen = pen tool.draggedPen = pen
return NSItemProvider(contentsOf: URL(string: pen.id)) ?? NSItemProvider() return NSItemProvider(contentsOf: URL(string: pen.id)) ?? NSItemProvider()
} preview: { } preview: {
penPreview(pen) compactPenPreview(pen)
.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() }))
@@ -440,7 +463,7 @@ struct PenDock: View {
createNewPen() createNewPen()
} label: { } label: {
Image(systemName: "plus.circle.fill") Image(systemName: "plus.circle.fill")
.font(.title2) .font(.headline)
.padding(1) .padding(1)
.contentShape(.circle) .contentShape(.circle)
.background { .background {
@@ -472,6 +495,22 @@ struct PenDock: View {
.padding(.leading, 10) .padding(.leading, 10)
} }
func compactPenPreview(_ pen: Pen) -> some View {
ZStack {
if let tip = pen.style.compactIcon.tip {
Image(tip)
.resizable()
.renderingMode(.template)
.foregroundStyle(Color.rgba(from: pen.rgba))
}
Image(pen.style.compactIcon.base)
.resizable()
}
.frame(width: width * factor, height: height * factor)
.padding(.top, 5)
.padding(.horizontal, 5)
}
func penShadow(_ pen: Pen) -> some View { func penShadow(_ pen: Pen) -> some View {
ZStack { ZStack {
Group { Group {