Taras Shchybovyk
Taras Shchybovyk

Reputation: 416

NSTrackingArea for custom shapes

I want to create a canvas that draws diagrams.

For that I've created custom class derived from NSView where I've drown two rectangles and connected them with two lines.

I am using NSView.addTrackingArea functionality in order to move and resize rectangles.

The issue I am facing with, How to create tracking areas to track mouse events for lines? (NSTrackingArea accepts NSRect shapes.)

The second question is more about approach. How do you think, this approach is okay to manipulate with graphic objects?

enter image description here

Upvotes: 3

Views: 721

Answers (1)

Paul Patterson
Paul Patterson

Reputation: 6918

I don't think the approach you outline in your question is appropriate. As you've seen already tracking areas can't represent non-rectangular shapes, and if you're creating a graphics app you're going to need to represent a variety of shapes that aren't rectangular, not just lines. Fortunately help is at hand from Apple itself in the form Sketch, a sample drawing app that answers some fundamental questions about the basic setup of any program that wants to draw lots of different shapes to the screen.

enter image description here

Here's a quick overview of how they get shapes onto the canvas and how they hit-test those shapes:

  • The canvas is a single NSView with a single array containing Graphic objects.
  • Graphic is an abstract base class that inherits from NSObject. It exposes a number of empty methods:

    Graphic: NSObject
        - isContentUnderPoint(point: CGPoint) -> Bool
        - frame: CGRect
        - drawInRect(rect: CGPoint)
    
  • There are a number of concrete Graphic subclasses (Line, Rectangle, Circle, Square, etc), each of which implements the above methods, as required.

  • Getting the shapes to the screen occurs in the canvas's drawRect:

    // In MyCanvas (an NSView subclass)
    func drawRect(dirtyRect: CGRect) {
        for shape in self.graphics {
            shape.drawInRect(dirtyRect) 
        }
    }
    
  • Hit testing is much the same principle...

    // in MyCanvas (an NSView subclass)
    func mouseDown(theEvent: NSEvent {
        let canvasPoint = convertPoint(theEvent.locationInWindow, fromView: nil)
        for shape in self.graphics {
            if shape.isContentUnderPoint(canvasPoint) {
                // do something!
            }
        }
    }
    

In the case of a Line shape, a isContentUnderPoint implementation would maybe calculate the equation of the line (using its start- and end- point) and then plug in the mouse-down point to see if it fits the equation. All in all it would take no more than a handful of lines of code, and it completely avoids any Cocoa drawing gymnastics.

Upvotes: 3

Related Questions