mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-04-24 09:38:33 +02:00
feat: revamp element tool bar
This commit is contained in:
@@ -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() {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user