I want to implement to composite pattern in my simple drawing program written in Swift for macOS.
The goal is to be able to group figures in order to handle (move, etc.) them as being one figure.
I have already implemented the DrawingComposite class that inherits from the Drawing class. I am currently stuck on which method I need to implement in order to be able to group figures and move them as one.
import Cocoa
import Foundation
class Drawing: NSView {
var x = 0
var selected = false
var shouldMove = false
var anchorPoint: NSPoint!
var nframe: NSRect
var shouldDrawRectangle: Bool
var view: NSView
let colorSelection = NSColorPanel.shared.color.cgColor
var firstMouseDownPoint: NSPoint = NSZeroPoint
init(frame: NSRect, positions _: NSRect, drawRect: Bool, view: NSView) {
self.view = view
nframe = frame
shouldDrawRectangle = drawRect
super.init(frame: nframe)
self.view.addSubview(self)
arrayOfRect.append(frame)
arrayOfType.append(drawRect)
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let context = NSGraphicsContext.current!.cgContext
let border = NSBezierPath(rect: dirtyRect)
var color = NSColor()
context.saveGState()
context.setFillColor(colorSelection)
context.fillEllipse(in: dirtyRect)
if shouldDrawRectangle {
context.addRect(dirtyRect)
context.drawPath(using: .fillStroke)
}
if selected {
color = NSColor.black
border.lineWidth = CGFloat(1)
color.setStroke()
border.stroke()
}
context.restoreGState()
}
// Allow view to receive keypress (remove the purr sound)
override var acceptsFirstResponder: Bool {
return true
}
override func mouseDown(with theEvent: NSEvent) {
// get coordinates
let pos = theEvent.locationInWindow
// Check if inside the rect
if pos.x >= nframe.origin.x, pos.x <= nframe.origin.x + nframe.size.width {
// X match, now check Y
if pos.y >= nframe.origin.y, pos.y <= nframe.origin.y + nframe.size.height {
// If we get here, then we're insisde the rect!
shouldMove = true
selected = true
display()
updateValues(nframe: nframe)
// OPTIONAL : Set an anchor point
// self.anchorPoint = NSMakePoint(pos.x - self.nframe.origin.x, pos.y - self.nframe.origin.y);
firstMouseDownPoint = (window?.contentView?.convert(theEvent.locationInWindow, to: self))!
}
}
}
override func mouseUp(with _: NSEvent) {
if shouldMove {
selected = false
shouldMove = false
display()
}
}
@objc func moveShape(offset: NSPoint, d: Drawing, index: Int) {
(undoManager?.prepare(withInvocationTarget: self) as AnyObject).unmoveShape(offset: offset, d: d, index: index)
undoManager?.setActionName("Move Shape")
print("Move Shape")
ViewController.MoveShape(offset: offset, drawing: d, index: index).execute()
}
@objc func unmoveShape(offset: NSPoint, d: Drawing, index: Int) {
(undoManager?.prepare(withInvocationTarget: self) as AnyObject).moveShape(offset: offset, d: d, index: index)
undoManager?.setActionName("Unmove Shape")
print("Unmove Shape")
ViewController.MoveShape(offset: offset, drawing: d, index: index).unexecute()
}
override func mouseDragged(with theEvent: NSEvent) {
if shouldMove {
let newPoint = (window?.contentView?.convert(theEvent.locationInWindow, to: self))!
for _ in arrayOfRect {
if let index = arrayOfRect.firstIndex(of: self.frame) {
let offset = NSPoint(x: newPoint.x - firstMouseDownPoint.x, y: newPoint.y - firstMouseDownPoint.y)
moveShape(offset: offset, d: self, index: index)
break
}
}
// Redraw the view
display()
}
}
private func rectsBeingDrawn() -> [NSRect] {
var rectsPtr: UnsafePointer<NSRect>?
var count: Int = 0
getRectsBeingDrawn(&rectsPtr, count: &count)
return Array(UnsafeBufferPointer(start: rectsPtr, count: count))
}
}
class DrawingComposite : Drawing{
private let drawings:[Drawing]
init(frame: NSRect, positions _: NSRect, drawRect: Bool, view: NSView, drawings: Drawing) {
self.drawings = drawings
super.init(frame: frame, positions: positions, drawRect: drawRect, view:view)
}
/* implement some method to group figures */
}
class ViewController: NSViewController {
var brect = true
var startPoint: NSPoint?
var endPoint: NSPoint?
var URL: URL?
lazy var window: NSWindow = self.view.window!
var mouseLocation: NSPoint {
return NSEvent.mouseLocation
}
var location: NSPoint {
return window.mouseLocationOutsideOfEventStream
}
override func mouseDown(with event: NSEvent) {
startPoint = event.locationInWindow
positionXYMouse.stringValue = NSStringFromPoint(startPoint ?? NSMakePoint(0, 0))
}
override func mouseUp(with event: NSEvent) {
// TODO: fix for drawing rectangles leftwards and upwards.
endPoint = event.locationInWindow
let positions = NSMakeRect(0, 0, 0, 0)
addShape(x: x, y: y, w: w, h: h, positions: positions, drawRect: brect, view: view)
positionXYMouse.stringValue = NSStringFromPoint(endPoint ?? NSMakePoint(0, 0))
}
class MoveShape: Command {
let offset: NSPoint
let drawing: Drawing
let index: Int
init(offset: NSPoint, drawing: Drawing, index: Int) {
self.offset = offset
self.drawing = drawing
self.index = index
}
func execute() {
drawing.frame.origin = NSMakePoint(drawing.frame.origin.x + offset.x, drawing.frame.origin.y + offset.y)
drawing.nframe.origin = NSMakePoint(drawing.nframe.origin.x + offset.x, drawing.nframe.origin.y + offset.y)
arrayOfRect[self.index] = drawing.nframe
}
func unexecute() {
drawing.frame.origin = NSMakePoint(drawing.frame.origin.x - offset.x, drawing.frame.origin.y - offset.y)
drawing.nframe.origin = NSMakePoint(drawing.nframe.origin.x - offset.x, drawing.nframe.origin.y - offset.y)
arrayOfRect[self.index] = drawing.nframe
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
I expect that when properly implemented, I should be able to move multiple figures/drawings as one.
Aucun commentaire:
Enregistrer un commentaire