diff --git a/Memola.xcodeproj/project.pbxproj b/Memola.xcodeproj/project.pbxproj index 3dd6735..c5886b2 100644 --- a/Memola.xcodeproj/project.pbxproj +++ b/Memola.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ EC7F6BEC2BE5E6E300A34A7B /* MemolaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7F6BEB2BE5E6E300A34A7B /* MemolaApp.swift */; }; EC7F6BF02BE5E6E400A34A7B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC7F6BEF2BE5E6E400A34A7B /* Assets.xcassets */; }; EC7F6BF32BE5E6E400A34A7B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC7F6BF22BE5E6E400A34A7B /* Preview Assets.xcassets */; }; + EC8C9DCE2C39882500A8F3C4 /* NSSyncScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8C9DCD2C39882500A8F3C4 /* NSSyncScrollView.swift */; }; EC8F54AC2C2ACDA8001C7C74 /* GridMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8F54AB2C2ACDA8001C7C74 /* GridMode.swift */; }; EC8F54AE2C2AF5A4001C7C74 /* LineGridVertex.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8F54AD2C2AF5A4001C7C74 /* LineGridVertex.swift */; }; EC8F54B02C2AF5E9001C7C74 /* LineGridContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8F54AF2C2AF5E9001C7C74 /* LineGridContext.swift */; }; @@ -119,7 +120,7 @@ ECF7B2E02C39169C004D2C57 /* Image++.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF7B2CD2C39169C004D2C57 /* Image++.swift */; }; ECF7B2E12C39169C004D2C57 /* View++.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF7B2CE2C39169C004D2C57 /* View++.swift */; }; ECF7B2E42C39174D004D2C57 /* Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF7B2E32C39174D004D2C57 /* Platform.swift */; }; - ECF7B2E72C39544E004D2C57 /* CenterClipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF7B2E62C39544E004D2C57 /* CenterClipView.swift */; }; + ECF7B2E72C39544E004D2C57 /* NSCenterClipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF7B2E62C39544E004D2C57 /* NSCenterClipView.swift */; }; ECFA15202BEF21EF00455818 /* MemoObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA151F2BEF21EF00455818 /* MemoObject.swift */; }; ECFA15222BEF21F500455818 /* CanvasObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA15212BEF21F500455818 /* CanvasObject.swift */; }; ECFA15242BEF223300455818 /* GraphicContextObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA15232BEF223300455818 /* GraphicContextObject.swift */; }; @@ -161,6 +162,7 @@ EC7F6BEB2BE5E6E300A34A7B /* MemolaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemolaApp.swift; sourceTree = ""; }; EC7F6BEF2BE5E6E400A34A7B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; EC7F6BF22BE5E6E400A34A7B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + EC8C9DCD2C39882500A8F3C4 /* NSSyncScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSSyncScrollView.swift; sourceTree = ""; }; EC8F54AB2C2ACDA8001C7C74 /* GridMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridMode.swift; sourceTree = ""; }; EC8F54AD2C2AF5A4001C7C74 /* LineGridVertex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineGridVertex.swift; sourceTree = ""; }; EC8F54AF2C2AF5E9001C7C74 /* LineGridContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineGridContext.swift; sourceTree = ""; }; @@ -243,7 +245,7 @@ ECF7B2CD2C39169C004D2C57 /* Image++.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Image++.swift"; sourceTree = ""; }; ECF7B2CE2C39169C004D2C57 /* View++.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View++.swift"; sourceTree = ""; }; ECF7B2E32C39174D004D2C57 /* Platform.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Platform.swift; sourceTree = ""; }; - ECF7B2E62C39544E004D2C57 /* CenterClipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenterClipView.swift; sourceTree = ""; }; + ECF7B2E62C39544E004D2C57 /* NSCenterClipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSCenterClipView.swift; sourceTree = ""; }; ECF7B2E82C395A8E004D2C57 /* Memola.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Memola.entitlements; sourceTree = ""; }; ECFA151F2BEF21EF00455818 /* MemoObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoObject.swift; sourceTree = ""; }; ECFA15212BEF21F500455818 /* CanvasObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CanvasObject.swift; sourceTree = ""; }; @@ -322,8 +324,8 @@ EC1437B42BE748E60022C903 /* Views */ = { isa = PBXGroup; children = ( + EC8C9DCC2C3987FD00A8F3C4 /* AppKit */, ECA738AC2BE60CC600A4542E /* DrawingView.swift */, - ECF7B2E62C39544E004D2C57 /* CenterClipView.swift */, ); path = Views; sourceTree = ""; @@ -481,6 +483,15 @@ path = "Preview Content"; sourceTree = ""; }; + EC8C9DCC2C3987FD00A8F3C4 /* AppKit */ = { + isa = PBXGroup; + children = ( + ECF7B2E62C39544E004D2C57 /* NSCenterClipView.swift */, + EC8C9DCD2C39882500A8F3C4 /* NSSyncScrollView.swift */, + ); + path = AppKit; + sourceTree = ""; + }; EC8F54AA2C2ACD9D001C7C74 /* Grid */ = { isa = PBXGroup; children = ( @@ -1038,6 +1049,7 @@ ECA738862BE5FF2500A4542E /* Canvas.swift in Sources */, ECF7B2DE2C39169C004D2C57 /* NSManagedObjectContext++.swift in Sources */, ECA738882BE5FF4400A4542E /* Renderer.swift in Sources */, + EC8C9DCE2C39882500A8F3C4 /* NSSyncScrollView.swift in Sources */, ECF7B2D72C39169C004D2C57 /* Color++.swift in Sources */, EC01512C2C306BEF008A115E /* MemoCard.swift in Sources */, ECA738D42BE60F9100A4542E /* StrokeGenerator.swift in Sources */, @@ -1058,7 +1070,7 @@ ECA738E22BE610D000A4542E /* GraphicRenderPass.swift in Sources */, ECE883BF2C00AB440045C53D /* Stroke.swift in Sources */, ECA738DC2BE6108D00A4542E /* StrokeRenderPass.swift in Sources */, - ECF7B2E72C39544E004D2C57 /* CenterClipView.swift in Sources */, + ECF7B2E72C39544E004D2C57 /* NSCenterClipView.swift in Sources */, ECF7B2D22C39169C004D2C57 /* CGFloat++.swift in Sources */, EC4538892BEBCAE000A86FEC /* Quad.swift in Sources */, ECE883BD2C00AA170045C53D /* EraserStroke.swift in Sources */, diff --git a/Memola/Canvas/Buffers/Vertices/PointGridVertex.swift b/Memola/Canvas/Buffers/Vertices/PointGridVertex.swift index ccae5af..4861397 100644 --- a/Memola/Canvas/Buffers/Vertices/PointGridVertex.swift +++ b/Memola/Canvas/Buffers/Vertices/PointGridVertex.swift @@ -10,11 +10,7 @@ import Foundation struct PointGridVertex { var position: vector_float4 - #if os(macOS) - var pointSize: Float = 256 - #else var pointSize: Float = 10 - #endif } extension PointGridVertex { diff --git a/Memola/Canvas/Core/Canvas.swift b/Memola/Canvas/Core/Canvas.swift index 8ab5e9f..2c58dbf 100644 --- a/Memola/Canvas/Core/Canvas.swift +++ b/Memola/Canvas/Core/Canvas.swift @@ -110,8 +110,9 @@ extension Canvas { let bounds = CGRect(origin: .zero, size: size) let renderView = drawingView.renderView #if os(macOS) - let drawingViewBounds = CGRect(origin: .zero, size: drawingView.bounds.size.multiply(by: zoomScale)) - let targetRect = drawingView.convert(drawingViewBounds, to: renderView) + let drawingViewBounds = drawingView.bounds + var targetRect = drawingView.convert(drawingViewBounds, to: renderView) + targetRect.origin.y = renderView.bounds.height - targetRect.maxY #else let targetRect = drawingView.convert(drawingView.bounds, to: renderView) #endif @@ -145,19 +146,23 @@ extension Canvas { } func updateClipBounds(_ scrollView: Platform.ScrollView, on drawingView: DrawingView) { + #if os(macOS) + let ratio = drawingView.ratio + var bounds = scrollView.convert(scrollView.bounds, to: drawingView) + bounds.origin.y = drawingView.bounds.height - (bounds.origin.y + bounds.height) + clipBounds = CGRect(origin: bounds.origin.muliply(by: ratio), size: bounds.size.multiply(by: ratio)) + #else let ratio = drawingView.ratio let bounds = scrollView.convert(scrollView.bounds, to: drawingView) clipBounds = CGRect(origin: bounds.origin.muliply(by: ratio), size: bounds.size.multiply(by: ratio)) + #endif } } // MARK: - Zoom Scale extension Canvas { func setZoomScale(_ zoomScale: CGFloat) { - DispatchQueue.main.async { [weak self] in - guard let self else { return } - self.zoomScale = min(max(zoomScale, minimumZoomScale), maximumZoomScale) - } + self.zoomScale = min(max(zoomScale, minimumZoomScale), maximumZoomScale) } } diff --git a/Memola/Canvas/Core/Renderer.swift b/Memola/Canvas/Core/Renderer.swift index b1434bb..3409ade 100644 --- a/Memola/Canvas/Core/Renderer.swift +++ b/Memola/Canvas/Core/Renderer.swift @@ -112,7 +112,7 @@ final class Renderer { func drawPreview(on canvas: Canvas) -> Platform.Image? { guard let commandBuffer = commandQueue.makeCommandBuffer() else { - NSLog("[Memola] - Unable to create command buffer") + NSLog("[Memola] - Unable to create command buffer for preview") return nil } strokeRenderPass.eraserRenderPass = eraserRenderPass diff --git a/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift b/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift index 81e482f..074536e 100644 --- a/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift +++ b/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift @@ -48,14 +48,11 @@ class CanvasViewController: Platform.ViewController { } #if os(macOS) - override func viewWillLayout() { - super.viewWillLayout() - resizeDocumentView() - updateDocumentBounds() - } override func viewWillAppear() { super.viewWillAppear() + resizeDocumentView() + updateDocumentBounds() loadMemo() } @@ -132,9 +129,9 @@ extension CanvasViewController { scrollView.isScrollEnabled = true scrollView.showsVerticalScrollIndicator = true scrollView.showsHorizontalScrollIndicator = true - scrollView.delegate = self scrollView.backgroundColor = .clear #endif + scrollView.delegate = self scrollView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(scrollView) @@ -146,7 +143,7 @@ extension CanvasViewController { ]) #if os(macOS) - scrollView.contentView = CenterClipView() + scrollView.contentView = NSCenterClipView() scrollView.contentView.drawsBackground = false scrollView.documentView = drawingView #else @@ -171,10 +168,14 @@ extension CanvasViewController { let height = size.height * scale let newFrame = CGRect(x: 0, y: 0, width: width, height: height) drawingView.frame = newFrame + + DispatchQueue.main.async { [unowned canvas] in + canvas.setZoomScale(canvas.defaultZoomScale) + } #if os(macOS) - let rect = scrollView.documentVisibleRect - let center = NSPoint(x: rect.midX, y: rect.midY) + scrollView.contentView.setBoundsSize(newFrame.size) + let center = NSPoint(x: newFrame.midX, y: newFrame.midY) scrollView.setMagnification(canvas.defaultZoomScale, centeredAt: center) #else scrollView.setZoomScale(canvas.defaultZoomScale, animated: true) @@ -189,7 +190,6 @@ extension CanvasViewController { scrollView.setContentOffset(point, animated: true) #endif drawingView.updateDrawableSize(with: view.frame.size) - NSLog("[Memola] - drawingView: \(drawingView.frame.size.multiply(by: scrollView.magnification)), scrollView: \(scrollView.frame), renderView: \(renderView.frame)") } #if os(iOS) @@ -204,7 +204,10 @@ extension CanvasViewController { func updateDocumentBounds() { #if os(macOS) - var bounds = scrollView.bounds.muliply(by: drawingView.ratio / scrollView.magnification) + let ratio = drawingView.ratio + var bounds = scrollView.convert(scrollView.bounds, to: drawingView) + bounds.origin.y = drawingView.bounds.height - (bounds.origin.y + bounds.height) + bounds = CGRect(origin: bounds.origin.muliply(by: ratio), size: bounds.size.multiply(by: ratio)) #else var bounds = scrollView.bounds.muliply(by: drawingView.ratio / scrollView.zoomScale) #endif @@ -226,6 +229,7 @@ extension CanvasViewController { #if os(macOS) NotificationCenter.default.publisher(for: NSScrollView.didEndLiveMagnifyNotification, object: scrollView) .sink { [weak self] _ in + self?.updateDocumentBounds() self?.magnificationEnded() } .store(in: &cancellables) @@ -241,6 +245,7 @@ extension CanvasViewController { .store(in: &cancellables) NotificationCenter.default.publisher(for: NSScrollView.didEndLiveScrollNotification, object: scrollView) .sink { [weak self] _ in + self?.updateDocumentBounds() self?.draggingEnded() } .store(in: &cancellables) @@ -334,7 +339,12 @@ extension CanvasViewController { withAnimation { tool.selectedPhotoItem = nil } + #if os(macOS) + let pointInLeftBottomOrigin = gesture.location(in: drawingView) + let point = CGPoint(x: pointInLeftBottomOrigin.x, y: drawingView.bounds.height - pointInLeftBottomOrigin.y) + #else let point = gesture.location(in: drawingView) + #endif let photo = canvas.insertPhoto(at: point.muliply(by: drawingView.ratio), photoItem: photoItem) history.addUndo(.photo(photo)) drawingView.draw() @@ -342,6 +352,16 @@ extension CanvasViewController { } #if os(macOS) +extension CanvasViewController: NSSyncScrollViewDelegate { + func scrollViewDidZoom(_ scrollView: NSSyncScrollView) { + canvas.setZoomScale(scrollView.magnification) + renderView.draw() + } + + func scrollViewDidScroll(_ scrollView: NSSyncScrollView) { + renderView.draw() + } +} #else extension CanvasViewController: UIScrollViewDelegate { func viewForZooming(in scrollView: UIScrollView) -> UIView? { @@ -392,9 +412,6 @@ extension CanvasViewController: UIScrollViewDelegate { extension CanvasViewController { func magnificationStarted() { - #if os(macOS) - canvas.setZoomScale(scrollView.magnification) - #endif guard !renderer.updatesViewPort else { return } drawingView.touchCancelled() canvas.updateClipBounds(scrollView, on: drawingView) @@ -403,9 +420,6 @@ extension CanvasViewController { } func magnificationEnded() { - #if os(macOS) - canvas.setZoomScale(scrollView.magnification) - #endif renderer.setUpdatesViewPort(false) renderer.setRedrawsGraphicRender() renderView.draw() diff --git a/Memola/Canvas/View/Bridge/Views/CenterClipView.swift b/Memola/Canvas/View/Bridge/Views/AppKit/NSCenterClipView.swift similarity index 91% rename from Memola/Canvas/View/Bridge/Views/CenterClipView.swift rename to Memola/Canvas/View/Bridge/Views/AppKit/NSCenterClipView.swift index 1a4ba12..a9c48dd 100644 --- a/Memola/Canvas/View/Bridge/Views/CenterClipView.swift +++ b/Memola/Canvas/View/Bridge/Views/AppKit/NSCenterClipView.swift @@ -1,5 +1,5 @@ // -// CenterClipView.swift +// NSCenterClipView.swift // Memola // // Created by Dscyre Scotti on 7/6/24. @@ -8,7 +8,7 @@ #if canImport(AppKit) import AppKit -class CenterClipView: NSClipView { +class NSCenterClipView: NSClipView { override func constrainBoundsRect(_ proposedBounds: NSRect) -> NSRect { var rect = super.constrainBoundsRect(proposedBounds) if let containerView = self.documentView { diff --git a/Memola/Canvas/View/Bridge/Views/AppKit/NSSyncScrollView.swift b/Memola/Canvas/View/Bridge/Views/AppKit/NSSyncScrollView.swift new file mode 100644 index 0000000..280cfc4 --- /dev/null +++ b/Memola/Canvas/View/Bridge/Views/AppKit/NSSyncScrollView.swift @@ -0,0 +1,29 @@ +// +// NSSyncScrollView.swift +// Memola +// +// Created by Dscyre Scotti on 7/6/24. +// + +#if canImport(AppKit) +import AppKit + +protocol NSSyncScrollViewDelegate: AnyObject { + func scrollViewDidZoom(_ scrollView: NSSyncScrollView) + func scrollViewDidScroll(_ scrollView: NSSyncScrollView) +} + +class NSSyncScrollView: NSScrollView { + weak var delegate: NSSyncScrollViewDelegate? + + override func magnify(with event: NSEvent) { + super.magnify(with: event) + delegate?.scrollViewDidZoom(self) + } + + override func scrollWheel(with event: NSEvent) { + super.scrollWheel(with: event) + delegate?.scrollViewDidScroll(self) + } +} +#endif diff --git a/Memola/Utilies/Platform/Platform.swift b/Memola/Utilies/Platform/Platform.swift index db900e3..0b8c939 100644 --- a/Memola/Utilies/Platform/Platform.swift +++ b/Memola/Utilies/Platform/Platform.swift @@ -12,7 +12,7 @@ enum Platform { typealias View = NSView typealias Color = NSColor typealias Image = NSImage - typealias ScrollView = NSScrollView + typealias ScrollView = NSSyncScrollView typealias Application = NSApplication typealias ViewController = NSViewController typealias TapGestureRecognizer = NSClickGestureRecognizer