feat: fine tune memo canvas view for macos runtime

This commit is contained in:
dscyrescotti
2024-07-07 17:23:00 +07:00
parent 708a2b0758
commit ab7d24fb4e
7 changed files with 189 additions and 85 deletions

View File

@@ -1254,6 +1254,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Memola/Memola.entitlements; CODE_SIGN_ENTITLEMENTS = Memola/Memola.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\"";
@@ -1291,6 +1292,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Memola/Memola.entitlements; CODE_SIGN_ENTITLEMENTS = Memola/Memola.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\"";

View File

@@ -162,7 +162,13 @@ extension Canvas {
// MARK: - Zoom Scale // MARK: - Zoom Scale
extension Canvas { extension Canvas {
func setZoomScale(_ zoomScale: CGFloat) { func setZoomScale(_ zoomScale: CGFloat) {
#if os(macOS)
self.zoomScale = min(max(zoomScale, minimumZoomScale), maximumZoomScale) self.zoomScale = min(max(zoomScale, minimumZoomScale), maximumZoomScale)
#else
DispatchQueue.main.async { [unowned self] in
self.zoomScale = min(max(zoomScale, minimumZoomScale), maximumZoomScale)
}
#endif
} }
} }

View File

@@ -169,15 +169,15 @@ extension CanvasViewController {
let newFrame = CGRect(x: 0, y: 0, width: width, height: height) let newFrame = CGRect(x: 0, y: 0, width: width, height: height)
drawingView.frame = newFrame drawingView.frame = newFrame
#if os(macOS)
DispatchQueue.main.async { [unowned canvas] in DispatchQueue.main.async { [unowned canvas] in
canvas.setZoomScale(canvas.defaultZoomScale) canvas.setZoomScale(canvas.defaultZoomScale)
} }
#if os(macOS)
scrollView.contentView.setBoundsSize(newFrame.size) scrollView.contentView.setBoundsSize(newFrame.size)
let center = NSPoint(x: newFrame.midX, y: newFrame.midY) let center = NSPoint(x: newFrame.midX, y: newFrame.midY)
scrollView.setMagnification(canvas.defaultZoomScale, centeredAt: center) scrollView.setMagnification(canvas.defaultZoomScale, centeredAt: center)
#else #else
canvas.setZoomScale(canvas.defaultZoomScale)
scrollView.setZoomScale(canvas.defaultZoomScale, animated: true) scrollView.setZoomScale(canvas.defaultZoomScale, animated: true)
centerDocumentView(to: newSize) centerDocumentView(to: newSize)
#endif #endif
@@ -427,9 +427,6 @@ extension CanvasViewController {
} }
func draggingStarted() { func draggingStarted() {
#if os(macOS)
canvas.setZoomScale(scrollView.magnification)
#endif
guard !renderer.updatesViewPort else { return } guard !renderer.updatesViewPort else { return }
canvas.updateClipBounds(scrollView, on: drawingView) canvas.updateClipBounds(scrollView, on: drawingView)
drawingView.disableUserInteraction() drawingView.disableUserInteraction()
@@ -437,9 +434,6 @@ extension CanvasViewController {
} }
func draggingEnded() { func draggingEnded() {
#if os(macOS)
canvas.setZoomScale(scrollView.magnification)
#endif
renderer.setUpdatesViewPort(false) renderer.setUpdatesViewPort(false)
renderer.setRedrawsGraphicRender() renderer.setRedrawsGraphicRender()
renderView.draw() renderView.draw()

View File

@@ -23,6 +23,9 @@ struct ElementToolbar: View {
var body: some View { var body: some View {
Group { Group {
#if os(macOS)
regularToolbar
#else
if horizontalSizeClass == .regular { if horizontalSizeClass == .regular {
regularToolbar regularToolbar
} else { } else {
@@ -42,6 +45,7 @@ struct ElementToolbar: View {
} }
.padding(.bottom, 10) .padding(.bottom, 10)
} }
#endif
} }
#if os(iOS) #if os(iOS)
.fullScreenCover(isPresented: $opensCamera) { .fullScreenCover(isPresented: $opensCamera) {
@@ -94,9 +98,12 @@ struct ElementToolbar: View {
.frame(width: size, height: size) .frame(width: size, height: size)
.foregroundStyle(tool.selection == .hand ? Color.white : Color.accentColor) .foregroundStyle(tool.selection == .hand ? Color.white : Color.accentColor)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
} }
#if os(iOS) #if os(iOS)
.hoverEffect(.lift) .hoverEffect(.lift)
#else
.buttonStyle(.plain)
#endif #endif
.background { .background {
if tool.selection == .hand { if tool.selection == .hand {
@@ -116,9 +123,12 @@ struct ElementToolbar: View {
.frame(width: size, height: size) .frame(width: size, height: size)
.foregroundStyle(tool.selection == .pen ? Color.white : Color.accentColor) .foregroundStyle(tool.selection == .pen ? Color.white : Color.accentColor)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
} }
#if os(iOS) #if os(iOS)
.hoverEffect(.lift) .hoverEffect(.lift)
#else
.buttonStyle(.plain)
#endif #endif
.background { .background {
if tool.selection == .pen { if tool.selection == .pen {
@@ -138,9 +148,12 @@ struct ElementToolbar: View {
.frame(width: size, height: size) .frame(width: size, height: size)
.foregroundStyle(tool.selection == .photo ? Color.white : Color.accentColor) .foregroundStyle(tool.selection == .photo ? Color.white : Color.accentColor)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
} }
#if os(iOS) #if os(iOS)
.hoverEffect(.lift) .hoverEffect(.lift)
#else
.buttonStyle(.plain)
#endif #endif
.background { .background {
if tool.selection == .photo { if tool.selection == .photo {
@@ -159,13 +172,13 @@ struct ElementToolbar: View {
.transition(.blurReplace.animation(.easeIn(duration: 0.1))) .transition(.blurReplace.animation(.easeIn(duration: 0.1)))
} }
} }
.background { // .background {
if tool.selection == .photo { // if tool.selection == .photo {
RoundedRectangle(cornerRadius: 8) // RoundedRectangle(cornerRadius: 8)
.fill(Color.white.tertiary) // .fill(Color.white.tertiary)
.transition(.move(edge: .leading).combined(with: .opacity).animation(.easeIn(duration: 0.1))) // .transition(.move(edge: .leading).combined(with: .opacity).animation(.easeIn(duration: 0.1)))
} // }
} // }
} }
.background { .background {
RoundedRectangle(cornerRadius: 8) RoundedRectangle(cornerRadius: 8)
@@ -222,18 +235,24 @@ struct ElementToolbar: View {
.contentShape(.circle) .contentShape(.circle)
.frame(width: size, height: size) .frame(width: size, height: size)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
} }
#if os(iOS) #if os(iOS)
.hoverEffect(.lift) .hoverEffect(.lift)
#else
.buttonStyle(.plain)
#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) .contentShape(.circle)
.frame(width: size, height: size) .frame(width: size, height: size)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
} }
#if os(iOS) #if os(iOS)
.hoverEffect(.lift) .hoverEffect(.lift)
#else
.buttonStyle(.plain)
#endif #endif
} }
} }

View File

@@ -31,11 +31,15 @@ struct MemoView: View {
var body: some View { var body: some View {
Group { Group {
#if os(macOS)
canvasView
#else
if horizontalSizeClass == .regular { if horizontalSizeClass == .regular {
canvasView canvasView
} else { } else {
compactCanvasView compactCanvasView
} }
#endif
} }
.overlay(alignment: .top) { .overlay(alignment: .top) {
Toolbar(size: size, memo: memo, tool: tool, canvas: canvas, history: history) Toolbar(size: size, memo: memo, tool: tool, canvas: canvas, history: history)
@@ -66,7 +70,6 @@ struct MemoView: View {
switch tool.selection { switch tool.selection {
case .pen: case .pen:
PenDock(tool: tool, canvas: canvas, size: size) PenDock(tool: tool, canvas: canvas, size: size)
.transition(.move(edge: .trailing).combined(with: .blurReplace))
case .photo: case .photo:
if let photoItem = tool.selectedPhotoItem { if let photoItem = tool.selectedPhotoItem {
PhotoPreview(photoItem: photoItem, tool: tool) PhotoPreview(photoItem: photoItem, tool: tool)
@@ -106,7 +109,7 @@ struct MemoView: View {
} }
} }
.overlay(alignment: .bottom) { .overlay(alignment: .bottom) {
if tool.selection != .hand { if tool.selection != .hand && !canvas.locksCanvas {
Button { Button {
withAnimation { withAnimation {
tool.selectTool(.hand) tool.selectTool(.hand)
@@ -121,6 +124,7 @@ struct MemoView: View {
.contentShape(.capsule) .contentShape(.capsule)
} }
.offset(y: 5) .offset(y: 5)
.transition(.move(edge: .bottom).combined(with: .blurReplace))
} }
} }
} }
@@ -132,6 +136,40 @@ struct MemoView: View {
let zoomScale: CGFloat = (((canvas.zoomScale - canvas.minimumZoomScale) * (upperBound - lowerBound) / (canvas.maximumZoomScale - canvas.minimumZoomScale)) + lowerBound).rounded() 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] let zoomScales: [Int] = [400, 200, 100, 75, 50, 25, 10]
if !canvas.locksCanvas { if !canvas.locksCanvas {
#if os(macOS)
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)
.font(.subheadline)
.frame(height: size)
.clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
}
.menuIndicator(.hidden)
.frame(width: 50, height: size)
.padding(.leading, 12)
.background(.regularMaterial)
.clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
.menuStyle(.borderlessButton)
.transition(.move(edge: .bottom).combined(with: .blurReplace))
.padding(10)
#else
Menu { Menu {
ForEach(zoomScales, id: \.self) { scale in ForEach(zoomScales, id: \.self) { scale in
Button { Button {
@@ -156,12 +194,12 @@ struct MemoView: View {
.frame(height: size) .frame(height: size)
.background(.regularMaterial) .background(.regularMaterial)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
.padding(10) .padding(10)
} }
#if os(iOS)
.hoverEffect(.lift) .hoverEffect(.lift)
#endif
.transition(.move(edge: .bottom).combined(with: .blurReplace)) .transition(.move(edge: .bottom).combined(with: .blurReplace))
#endif
} }
} }

View File

@@ -23,8 +23,28 @@ struct PenDock: View {
@State var refreshScrollId: UUID = UUID() @State var refreshScrollId: UUID = UUID()
@State var opensColorPicker: Bool = false @State var opensColorPicker: Bool = false
#if os(macOS)
@State var showsThinknessPicker: Bool = false
#endif
var body: some View { var body: some View {
#if os(macOS)
ZStack(alignment: .bottomTrailing) {
if !canvas.locksCanvas {
VStack(alignment: .trailing) {
penPropertyTool
penItemList
}
.fixedSize()
.frame(maxHeight: .infinity)
.padding(10)
.transition(.move(edge: .trailing).combined(with: .blurReplace))
}
lockButton
.padding(10)
.transition(.move(edge: .trailing).combined(with: .blurReplace))
}
#else
if horizontalSizeClass == .regular { if horizontalSizeClass == .regular {
ZStack(alignment: .bottomTrailing) { ZStack(alignment: .bottomTrailing) {
if !canvas.locksCanvas { if !canvas.locksCanvas {
@@ -73,6 +93,7 @@ struct PenDock: View {
.transition(.move(edge: .trailing).combined(with: .blurReplace)) .transition(.move(edge: .trailing).combined(with: .blurReplace))
} }
} }
#endif
} }
@ViewBuilder @ViewBuilder
@@ -319,12 +340,12 @@ struct PenDock: View {
var compactPenPropertyTool: some View { var compactPenPropertyTool: some View {
if let pen = tool.selectedPen { if let pen = tool.selectedPen {
HStack(spacing: 10) { HStack(spacing: 10) {
compactPenThicknessPicker(pen) penThicknessPicker(pen)
.frame(width: width) .frame(width: size)
.rotationEffect(.degrees(-90)) .rotationEffect(.degrees(-90))
if pen.strokeStyle == .marker { if pen.strokeStyle == .marker {
penColorPicker(pen) penColorPicker(pen)
.frame(width: width) .frame(width: size)
.transition(.move(edge: .trailing).combined(with: .opacity)) .transition(.move(edge: .trailing).combined(with: .opacity))
} }
} }
@@ -353,7 +374,8 @@ struct PenDock: View {
} }
.background(baseColor) .background(baseColor)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.frame(height: horizontalSizeClass == .compact ? 30 : 25) .contentShape(.rect(cornerRadius: 8))
.frame(height: size)
.overlay { .overlay {
RoundedRectangle(cornerRadius: 8) RoundedRectangle(cornerRadius: 8)
.stroke(Color.gray, lineWidth: 0.4) .stroke(Color.gray, lineWidth: 0.4)
@@ -389,64 +411,50 @@ struct PenDock: View {
let maximum: CGFloat = pen.style.thickness.max let maximum: CGFloat = pen.style.thickness.max
let start: CGFloat = 4 let start: CGFloat = 4
let end: CGFloat = 10 let end: CGFloat = 10
let selection = Binding( let selection = Binding<CGFloat?>(
get: { pen.thickness }, get: { pen.thickness },
set: { set: {
pen.thickness = $0 pen.thickness = $0 ?? .zero
tool.objectWillChange.send() tool.objectWillChange.send()
} }
) )
Picker("", selection: selection) { #if os(macOS)
ForEach(pen.style.thicknessSteps, id: \.self) { step in let _width = width * factor - 38
let size = ((step - minimum) * (end - start) / (maximum - minimum)) + start - (0.5 / step) #else
Circle() let _width = horizontalSizeClass == .compact ? self.size : width * factor - 38
.fill(.black)
.frame(width: size, height: size)
.frame(width: size + 2, height: size + 2)
}
}
#if os(iOS)
.hoverEffect(.lift)
.pickerStyle(.wheel)
#endif #endif
.frame(width: width * factor - 18, height: 35) ScrollViewReader { proxy in
.onChange(of: pen.thickness) { _, _ in ScrollView(showsIndicators: false) {
withPersistence(\.viewContext) { context in LazyVStack(spacing: 0) {
try context.saveIfNeeded() ForEach(pen.style.thicknessSteps, id: \.self) { step in
let size = ((step - minimum) * (end - start) / (maximum - minimum)) + start - (0.5 / step)
Circle()
.foregroundStyle(.primary)
.frame(width: size, height: size)
.frame(width: _width, height: self.size)
.contentShape(.rect)
.id(step)
}
}
} }
} #if os(macOS)
} .frame(height: size)
#else
@ViewBuilder .frame(width: _width, height: size)
func compactPenThicknessPicker(_ pen: Pen) -> some View { #endif
let minimum: CGFloat = pen.style.thickness.min .background(.gray.quaternary)
let maximum: CGFloat = pen.style.thickness.max .clipShape(.rect(cornerRadius: 8))
let start: CGFloat = 4 .scrollPosition(id: selection, anchor: .center)
let end: CGFloat = 7 .scrollTargetLayout()
let selection = Binding( .scrollTargetBehavior(.viewAligned)
get: { pen.thickness }, .scrollIndicators(.hidden)
set: { .onAppear {
pen.thickness = $0 proxy.scrollTo(selection.wrappedValue)
tool.objectWillChange.send()
} }
) .onChange(of: pen.thickness) { _, _ in
Picker("", selection: selection) { withPersistence(\.viewContext) { context in
ForEach(pen.style.thicknessSteps, id: \.self) { step in try context.saveIfNeeded()
let size = ((step - minimum) * (end - start) / (maximum - minimum)) + start - (0.5 / step) }
Circle()
.fill(.black)
.frame(width: size, height: size)
.frame(width: size + 2, height: size + 2)
}
}
#if os(iOS)
.hoverEffect(.lift)
.pickerStyle(.wheel)
#endif
.frame(width: 50, height: 30)
.onChange(of: pen.thickness) { _, _ in
withPersistence(\.viewContext) { context in
try context.saveIfNeeded()
} }
} }
} }
@@ -467,6 +475,8 @@ struct PenDock: View {
.foregroundStyle(.green) .foregroundStyle(.green)
#if os(iOS) #if os(iOS)
.hoverEffect(.lift) .hoverEffect(.lift)
#else
.buttonStyle(.plain)
#endif #endif
} }
@@ -541,13 +551,15 @@ struct PenDock: View {
} }
} label: { } label: {
Image(systemName: canvas.locksCanvas ? "lock.fill" : "lock.open.fill") Image(systemName: canvas.locksCanvas ? "lock.fill" : "lock.open.fill")
.contentShape(.circle)
.frame(width: size, height: size) .frame(width: size, height: size)
.background(.regularMaterial) .background(.regularMaterial)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
} }
#if os(iOS) #if os(iOS)
.hoverEffect(.lift) .hoverEffect(.lift)
#else
.buttonStyle(.plain)
#endif #endif
.contentTransition(.symbolEffect(.replace)) .contentTransition(.symbolEffect(.replace))
} }

View File

@@ -41,9 +41,15 @@ struct Toolbar: View {
} }
} }
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
#if os(macOS)
if !canvas.locksCanvas {
ElementToolbar(size: size, tool: tool, canvas: canvas)
}
#else
if !canvas.locksCanvas, horizontalSizeClass == .regular { if !canvas.locksCanvas, horizontalSizeClass == .regular {
ElementToolbar(size: size, tool: tool, canvas: canvas) ElementToolbar(size: size, tool: tool, canvas: canvas)
} }
#endif
HStack(spacing: 5) { HStack(spacing: 5) {
if !canvas.locksCanvas { if !canvas.locksCanvas {
gridModeControl gridModeControl
@@ -61,13 +67,15 @@ struct Toolbar: View {
closeMemo() closeMemo()
} label: { } label: {
Image(systemName: "xmark") Image(systemName: "xmark")
.contentShape(.circle)
.frame(width: size, height: size) .frame(width: size, height: size)
.background(.regularMaterial) .background(.regularMaterial)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
} }
#if os(iOS) #if os(iOS)
.hoverEffect(.lift) .hoverEffect(.lift)
#else
.buttonStyle(.plain)
#endif #endif
.disabled(textFieldState) .disabled(textFieldState)
.transition(.move(edge: .top).combined(with: .blurReplace)) .transition(.move(edge: .top).combined(with: .blurReplace))
@@ -98,29 +106,34 @@ struct Toolbar: View {
} }
var historyControl: some View { var historyControl: some View {
HStack { HStack(spacing: 0) {
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) .frame(width: size, height: size)
.contentShape(.rect(cornerRadius: 8))
} }
#if os(iOS) #if os(iOS)
.hoverEffect(.lift) .hoverEffect(.lift)
#else
.buttonStyle(.plain)
#endif #endif
.disabled(history.undoDisabled) .disabled(history.undoDisabled)
Button { Button {
history.historyPublisher.send(.redo) history.historyPublisher.send(.redo)
} label: { } label: {
Image(systemName: "arrow.uturn.forward.circle") Image(systemName: "arrow.uturn.forward.circle")
.contentShape(.circle) .frame(width: size, height: size)
.contentShape(.rect(cornerRadius: 8))
} }
#if os(iOS) #if os(iOS)
.hoverEffect(.lift) .hoverEffect(.lift)
#else
.buttonStyle(.plain)
#endif #endif
.disabled(history.redoDisabled) .disabled(history.redoDisabled)
} }
.frame(width: size * 2, height: size)
.background(.regularMaterial) .background(.regularMaterial)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.disabled(textFieldState) .disabled(textFieldState)
@@ -128,6 +141,27 @@ struct Toolbar: View {
} }
var gridModeControl: some View { var gridModeControl: some View {
#if os(macOS)
Button {
switch canvas.gridMode {
case .none:
canvas.gridMode = .point
case .point:
canvas.gridMode = .line
case .line:
canvas.gridMode = .none
}
} label: {
Image(systemName: canvas.gridMode.icon)
.frame(width: size, height: size)
.background(.regularMaterial)
.clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
}
.buttonStyle(.plain)
.contentTransition(.symbolEffect(.replace))
.transition(.move(edge: .top).combined(with: .blurReplace))
#else
Menu { Menu {
ForEach(GridMode.all, id: \.self) { mode in ForEach(GridMode.all, id: \.self) { mode in
Button { Button {
@@ -143,16 +177,15 @@ struct Toolbar: View {
} }
} label: { } label: {
Image(systemName: canvas.gridMode.icon) Image(systemName: canvas.gridMode.icon)
.contentShape(.circle)
.frame(width: size, height: size) .frame(width: size, height: size)
.background(.regularMaterial) .background(.regularMaterial)
.clipShape(.rect(cornerRadius: 8)) .clipShape(.rect(cornerRadius: 8))
.contentShape(.rect(cornerRadius: 8))
} }
#if os(iOS)
.hoverEffect(.lift) .hoverEffect(.lift)
#endif
.contentTransition(.symbolEffect(.replace)) .contentTransition(.symbolEffect(.replace))
.transition(.move(edge: .top).combined(with: .blurReplace)) .transition(.move(edge: .top).combined(with: .blurReplace))
#endif
} }
func closeMemo() { func closeMemo() {