mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-03-20 00:24:12 +01:00
wip: fix grid line
This commit is contained in:
@@ -119,6 +119,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 */; };
|
||||
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 */; };
|
||||
@@ -242,6 +243,8 @@
|
||||
ECF7B2CD2C39169C004D2C57 /* Image++.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Image++.swift"; sourceTree = "<group>"; };
|
||||
ECF7B2CE2C39169C004D2C57 /* View++.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View++.swift"; sourceTree = "<group>"; };
|
||||
ECF7B2E32C39174D004D2C57 /* Platform.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Platform.swift; sourceTree = "<group>"; };
|
||||
ECF7B2E62C39544E004D2C57 /* CenterClipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenterClipView.swift; sourceTree = "<group>"; };
|
||||
ECF7B2E82C395A8E004D2C57 /* Memola.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Memola.entitlements; sourceTree = "<group>"; };
|
||||
ECFA151F2BEF21EF00455818 /* MemoObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoObject.swift; sourceTree = "<group>"; };
|
||||
ECFA15212BEF21F500455818 /* CanvasObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CanvasObject.swift; sourceTree = "<group>"; };
|
||||
ECFA15232BEF223300455818 /* GraphicContextObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphicContextObject.swift; sourceTree = "<group>"; };
|
||||
@@ -320,6 +323,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ECA738AC2BE60CC600A4542E /* DrawingView.swift */,
|
||||
ECF7B2E62C39544E004D2C57 /* CenterClipView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
@@ -455,6 +459,7 @@
|
||||
EC7F6BEA2BE5E6E300A34A7B /* Memola */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ECF7B2E82C395A8E004D2C57 /* Memola.entitlements */,
|
||||
ECA738762BE5EE4E00A4542E /* App */,
|
||||
ECA7387E2BE5FE4200A4542E /* Canvas */,
|
||||
EC50500A2BF6672000B4D86E /* Components */,
|
||||
@@ -1053,6 +1058,7 @@
|
||||
ECA738E22BE610D000A4542E /* GraphicRenderPass.swift in Sources */,
|
||||
ECE883BF2C00AB440045C53D /* Stroke.swift in Sources */,
|
||||
ECA738DC2BE6108D00A4542E /* StrokeRenderPass.swift in Sources */,
|
||||
ECF7B2E72C39544E004D2C57 /* CenterClipView.swift in Sources */,
|
||||
ECF7B2D22C39169C004D2C57 /* CGFloat++.swift in Sources */,
|
||||
EC4538892BEBCAE000A86FEC /* Quad.swift in Sources */,
|
||||
ECE883BD2C00AA170045C53D /* EraserStroke.swift in Sources */,
|
||||
@@ -1235,6 +1241,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Memola/Memola.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\"";
|
||||
@@ -1271,6 +1278,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Memola/Memola.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\"";
|
||||
|
||||
@@ -10,7 +10,11 @@ import Foundation
|
||||
|
||||
struct PointGridVertex {
|
||||
var position: vector_float4
|
||||
#if os(macOS)
|
||||
var pointSize: Float = 256
|
||||
#else
|
||||
var pointSize: Float = 10
|
||||
#endif
|
||||
}
|
||||
|
||||
extension PointGridVertex {
|
||||
|
||||
@@ -86,7 +86,7 @@ extension Canvas {
|
||||
state = .closing
|
||||
let previewImage = renderer?.drawPreview(on: self)
|
||||
#if os(macOS)
|
||||
#warning("TODO: implement for macos")
|
||||
memoObject.preview = previewImage?.tiffRepresentation
|
||||
#else
|
||||
memoObject.preview = previewImage?.jpegData(compressionQuality: 0.8)
|
||||
#endif
|
||||
@@ -109,7 +109,12 @@ extension Canvas {
|
||||
func updateTransform(on drawingView: DrawingView) {
|
||||
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)
|
||||
#else
|
||||
let targetRect = drawingView.convert(drawingView.bounds, to: renderView)
|
||||
#endif
|
||||
let transform1 = bounds.transform(to: targetRect)
|
||||
let transform2 = renderView.bounds.transform(to: CGRect(x: -1.0, y: -1.0, width: 2.0, height: 2.0))
|
||||
let transform3 = CGAffineTransform.identity.translatedBy(x: 0, y: 1).scaledBy(x: 1, y: -1).translatedBy(x: 0, y: 1)
|
||||
|
||||
@@ -125,8 +125,7 @@ final class Renderer {
|
||||
return nil
|
||||
}
|
||||
#if os(macOS)
|
||||
#warning("TODO: implement here")
|
||||
return nil
|
||||
return NSImage(cgImage: cgImage, size: .init(width: CGFloat(cgImage.width), height: CGFloat(cgImage.height))).flipped(flipVertically: true)
|
||||
#else
|
||||
return UIImage(cgImage: cgImage, scale: 1.0, orientation: .downMirrored)
|
||||
#endif
|
||||
|
||||
@@ -148,7 +148,12 @@ public class Tool: NSObject, ObservableObject {
|
||||
let rect = CGRect(origin: .zero, size: targetSize)
|
||||
|
||||
#if os(macOS)
|
||||
return (image, dimension)
|
||||
let newImage = NSImage(size: rect.size, flipped: false) { destRect in
|
||||
NSGraphicsContext.current?.imageInterpolation = .high
|
||||
image.draw(in: destRect, from: NSZeroRect, operation: .copy, fraction: 1)
|
||||
return true
|
||||
}
|
||||
return (newImage, dimension)
|
||||
#else
|
||||
UIGraphicsBeginImageContextWithOptions(targetSize, true, 1.0)
|
||||
image.draw(in: rect)
|
||||
@@ -163,8 +168,7 @@ public class Tool: NSObject, ObservableObject {
|
||||
|
||||
private func bookmarkPhoto(of image: Platform.Image, and previewImage: Platform.Image, in dimension: CGSize, with canvasID: NSManagedObjectID) -> PhotoItem? {
|
||||
#if os(macOS)
|
||||
#warning("TODO: implement for macos")
|
||||
let data = Data()
|
||||
guard let data = image.tiffRepresentation else { return nil }
|
||||
#else
|
||||
guard let data = image.jpegData(compressionQuality: 1) else { return nil }
|
||||
#endif
|
||||
|
||||
@@ -48,10 +48,14 @@ class CanvasViewController: Platform.ViewController {
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
override func viewWillAppear() {
|
||||
super.viewWillAppear()
|
||||
override func viewWillLayout() {
|
||||
super.viewWillLayout()
|
||||
resizeDocumentView()
|
||||
updateDocumentBounds()
|
||||
}
|
||||
|
||||
override func viewWillAppear() {
|
||||
super.viewWillAppear()
|
||||
loadMemo()
|
||||
}
|
||||
|
||||
@@ -95,9 +99,11 @@ class CanvasViewController: Platform.ViewController {
|
||||
extension CanvasViewController {
|
||||
func configureViews() {
|
||||
#if os(macOS)
|
||||
#warning("TODO: implement for macos")
|
||||
view.wantsLayer = true
|
||||
view.layer?.backgroundColor = NSColor.white.cgColor
|
||||
#else
|
||||
view.backgroundColor = .white
|
||||
#endif
|
||||
|
||||
renderView.autoResizeDrawable = false
|
||||
renderView.enableSetNeedsDisplay = true
|
||||
@@ -111,6 +117,15 @@ extension CanvasViewController {
|
||||
renderView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
|
||||
])
|
||||
|
||||
#if os(macOS)
|
||||
scrollView.maxMagnification = canvas.maximumZoomScale
|
||||
scrollView.minMagnification = canvas.minimumZoomScale
|
||||
scrollView.hasVerticalScroller = true
|
||||
scrollView.hasHorizontalScroller = true
|
||||
scrollView.allowsMagnification = true
|
||||
scrollView.drawsBackground = false
|
||||
scrollView.scrollerKnobStyle = .dark
|
||||
#else
|
||||
scrollView.maximumZoomScale = canvas.maximumZoomScale
|
||||
scrollView.minimumZoomScale = canvas.minimumZoomScale
|
||||
scrollView.contentInsetAdjustmentBehavior = .never
|
||||
@@ -119,6 +134,7 @@ extension CanvasViewController {
|
||||
scrollView.showsHorizontalScrollIndicator = true
|
||||
scrollView.delegate = self
|
||||
scrollView.backgroundColor = .clear
|
||||
#endif
|
||||
|
||||
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(scrollView)
|
||||
@@ -129,6 +145,11 @@ extension CanvasViewController {
|
||||
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
|
||||
])
|
||||
|
||||
#if os(macOS)
|
||||
scrollView.contentView = CenterClipView()
|
||||
scrollView.contentView.drawsBackground = false
|
||||
scrollView.documentView = drawingView
|
||||
#else
|
||||
scrollView.addSubview(drawingView)
|
||||
drawingView.backgroundColor = .clear
|
||||
drawingView.isUserInteractionEnabled = false
|
||||
@@ -137,10 +158,10 @@ extension CanvasViewController {
|
||||
|
||||
func resizeDocumentView(to newSize: CGSize? = nil) {
|
||||
#if os(macOS)
|
||||
#warning("TODO: implement for macos")
|
||||
scrollView.layoutSubtreeIfNeeded()
|
||||
#else
|
||||
scrollView.layoutIfNeeded()
|
||||
|
||||
#endif
|
||||
let size = canvas.size
|
||||
let widthScale = (newSize?.width ?? view.frame.width) / size.width
|
||||
let heightScale = (newSize?.height ?? view.frame.height) / size.height
|
||||
@@ -151,36 +172,42 @@ extension CanvasViewController {
|
||||
let newFrame = CGRect(x: 0, y: 0, width: width, height: height)
|
||||
drawingView.frame = newFrame
|
||||
|
||||
#if os(macOS)
|
||||
let rect = scrollView.documentVisibleRect
|
||||
let center = NSPoint(x: rect.midX, y: rect.midY)
|
||||
scrollView.setMagnification(canvas.defaultZoomScale, centeredAt: center)
|
||||
#else
|
||||
scrollView.setZoomScale(canvas.defaultZoomScale, animated: true)
|
||||
centerDocumentView(to: newSize)
|
||||
#endif
|
||||
|
||||
#if os(iOS)
|
||||
let offsetX = (newFrame.width * canvas.defaultZoomScale - view.frame.width) / 2
|
||||
let offsetY = (newFrame.height * canvas.defaultZoomScale - view.frame.height) / 2
|
||||
|
||||
let point = CGPoint(x: offsetX, y: offsetY)
|
||||
scrollView.setContentOffset(point, animated: true)
|
||||
|
||||
drawingView.updateDrawableSize(with: view.frame.size)
|
||||
#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)
|
||||
func centerDocumentView(to newSize: CGSize? = nil) {
|
||||
#if os(macOS)
|
||||
#warning("TODO: implement for macos")
|
||||
#else
|
||||
let documentViewSize = drawingView.frame.size
|
||||
let scrollViewSize = newSize ?? view.frame.size
|
||||
let verticalPadding = documentViewSize.height < scrollViewSize.height ? (scrollViewSize.height - documentViewSize.height) / 2 : 0
|
||||
let horizontalPadding = documentViewSize.width < scrollViewSize.width ? (scrollViewSize.width - documentViewSize.width) / 2 : 0
|
||||
self.scrollView.contentInset = UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding)
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
func updateDocumentBounds() {
|
||||
#if os(macOS)
|
||||
#warning("TODO: implement for macos")
|
||||
var bounds = scrollView.bounds.muliply(by: drawingView.ratio / scrollView.magnification)
|
||||
#else
|
||||
var bounds = scrollView.bounds.muliply(by: drawingView.ratio / scrollView.zoomScale)
|
||||
#endif
|
||||
let xDelta = bounds.minX * 0.0
|
||||
let yDelta = bounds.minY * 0.0
|
||||
bounds.origin.x -= xDelta
|
||||
@@ -191,12 +218,33 @@ extension CanvasViewController {
|
||||
if canvas.state == .loaded {
|
||||
canvas.loadStrokes(bounds)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
extension CanvasViewController {
|
||||
func configureListeners() {
|
||||
#if os(macOS)
|
||||
NotificationCenter.default.publisher(for: NSScrollView.didEndLiveMagnifyNotification, object: scrollView)
|
||||
.sink { [weak self] _ in
|
||||
self?.magnificationEnded()
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
NotificationCenter.default.publisher(for: NSScrollView.willStartLiveMagnifyNotification, object: scrollView)
|
||||
.sink { [weak self] _ in
|
||||
self?.magnificationStarted()
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
NotificationCenter.default.publisher(for: NSScrollView.willStartLiveScrollNotification, object: scrollView)
|
||||
.sink { [weak self] _ in
|
||||
self?.draggingStarted()
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
NotificationCenter.default.publisher(for: NSScrollView.didEndLiveScrollNotification, object: scrollView)
|
||||
.sink { [weak self] _ in
|
||||
self?.draggingEnded()
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
#endif
|
||||
canvas.$state
|
||||
.sink { [weak self] state in
|
||||
self?.canvasStateChanged(state)
|
||||
@@ -344,6 +392,9 @@ 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)
|
||||
@@ -352,6 +403,9 @@ extension CanvasViewController {
|
||||
}
|
||||
|
||||
func magnificationEnded() {
|
||||
#if os(macOS)
|
||||
canvas.setZoomScale(scrollView.magnification)
|
||||
#endif
|
||||
renderer.setUpdatesViewPort(false)
|
||||
renderer.setRedrawsGraphicRender()
|
||||
renderView.draw()
|
||||
@@ -359,6 +413,9 @@ extension CanvasViewController {
|
||||
}
|
||||
|
||||
func draggingStarted() {
|
||||
#if os(macOS)
|
||||
canvas.setZoomScale(scrollView.magnification)
|
||||
#endif
|
||||
guard !renderer.updatesViewPort else { return }
|
||||
canvas.updateClipBounds(scrollView, on: drawingView)
|
||||
drawingView.disableUserInteraction()
|
||||
@@ -366,6 +423,9 @@ extension CanvasViewController {
|
||||
}
|
||||
|
||||
func draggingEnded() {
|
||||
#if os(macOS)
|
||||
canvas.setZoomScale(scrollView.magnification)
|
||||
#endif
|
||||
renderer.setUpdatesViewPort(false)
|
||||
renderer.setRedrawsGraphicRender()
|
||||
renderView.draw()
|
||||
@@ -413,7 +473,8 @@ extension CanvasViewController {
|
||||
extension CanvasViewController {
|
||||
func zoomChanged(_ zoomScale: CGFloat) {
|
||||
#if os(macOS)
|
||||
#warning("TODO: implement for macos")
|
||||
let rect = scrollView.documentVisibleRect
|
||||
scrollView.setMagnification(zoomScale, centeredAt: CGPoint(x: rect.midX, y: rect.midY))
|
||||
#else
|
||||
scrollView.setZoomScale(zoomScale, animated: true)
|
||||
#endif
|
||||
|
||||
25
Memola/Canvas/View/Bridge/Views/CenterClipView.swift
Normal file
25
Memola/Canvas/View/Bridge/Views/CenterClipView.swift
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// CenterClipView.swift
|
||||
// Memola
|
||||
//
|
||||
// Created by Dscyre Scotti on 7/6/24.
|
||||
//
|
||||
|
||||
#if canImport(AppKit)
|
||||
import AppKit
|
||||
|
||||
class CenterClipView: NSClipView {
|
||||
override func constrainBoundsRect(_ proposedBounds: NSRect) -> NSRect {
|
||||
var rect = super.constrainBoundsRect(proposedBounds)
|
||||
if let containerView = self.documentView {
|
||||
if (rect.size.width > containerView.frame.size.width) {
|
||||
rect.origin.x = (containerView.frame.width - rect.width) / 2
|
||||
}
|
||||
if(rect.size.height > containerView.frame.size.height) {
|
||||
rect.origin.y = (containerView.frame.height - rect.height) / 2
|
||||
}
|
||||
}
|
||||
return rect
|
||||
}
|
||||
}
|
||||
#endif
|
||||
5
Memola/Memola.entitlements
Normal file
5
Memola/Memola.entitlements
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
||||
@@ -18,6 +18,59 @@ extension Image {
|
||||
}
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
extension NSImage {
|
||||
func upsideDownMirrored() -> NSImage {
|
||||
let degrees: CGFloat = 180
|
||||
let sinDegrees = abs(sin(degrees * CGFloat.pi / 180.0))
|
||||
let cosDegrees = abs(cos(degrees * CGFloat.pi / 180.0))
|
||||
let newSize = CGSize(
|
||||
width: size.height * sinDegrees + size.width * cosDegrees,
|
||||
height: size.width * sinDegrees + size.height * cosDegrees
|
||||
)
|
||||
|
||||
let imageBounds = NSRect(
|
||||
x: (newSize.width - size.width) / 2,
|
||||
y: (newSize.height - size.height) / 2,
|
||||
width: size.width,
|
||||
height: size.height
|
||||
)
|
||||
|
||||
let otherTransform = NSAffineTransform()
|
||||
otherTransform.translateX(by: newSize.width / 2, yBy: newSize.height / 2)
|
||||
otherTransform.rotate(byDegrees: degrees)
|
||||
otherTransform.translateX(by: -newSize.width / 2, yBy: -newSize.height / 2)
|
||||
|
||||
let rotatedImage = NSImage(size: newSize)
|
||||
rotatedImage.lockFocus()
|
||||
otherTransform.concat()
|
||||
draw(in: imageBounds, from: CGRect.zero, operation: NSCompositingOperation.copy, fraction: 1.0)
|
||||
rotatedImage.unlockFocus()
|
||||
|
||||
return rotatedImage
|
||||
}
|
||||
|
||||
func flipped(flipHorizontally: Bool = false, flipVertically: Bool = false) -> NSImage {
|
||||
let flippedImage = NSImage(size: size)
|
||||
|
||||
flippedImage.lockFocus()
|
||||
|
||||
NSGraphicsContext.current?.imageInterpolation = .high
|
||||
|
||||
let transform = NSAffineTransform()
|
||||
transform.translateX(by: flipHorizontally ? size.width : 0, yBy: flipVertically ? size.height : 0)
|
||||
transform.scaleX(by: flipHorizontally ? -1 : 1, yBy: flipVertically ? -1 : 1)
|
||||
transform.concat()
|
||||
|
||||
draw(at: .zero, from: NSRect(origin: .zero, size: size), operation: .sourceOver, fraction: 1)
|
||||
|
||||
flippedImage.unlockFocus()
|
||||
|
||||
return flippedImage
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if os(iOS)
|
||||
extension UIImage {
|
||||
func imageWithUpOrientation() -> UIImage? {
|
||||
|
||||
Reference in New Issue
Block a user