« Mouse Events

Posted by Brad Miller on June 21, 2002 [Feedback (0) & TrackBack (0)]

// Introduction

In this tutorial we'll explore capturing mouse events in a custom view by expanding the Rotate application that was created in the tutorial on NSAffineTransformations. Support will be added so that mouse events control the three types of transformations. Translations will be controlled by normal mouse clicks and dragging. Rotation and scaling will both be controlled by either control clicking or by the right mouse button if the user has a multi button mouse. In addition, scaling will also depend on the shift key being pressed.

 

// Preliminaries

We'll start off by adding the few structural additions that are needed. The first thing we need to do is add a new variable and method to RotateView.h. In RotateView.h add:

NSPoint lastPoint;

- (void)broadcast;

The lastPoint variable will be used to hold the location of the last mouse event that we have received and the broadcast method will post a notification when a mouse event occurs. The notification will be watched for by our Controller so that it can update its sliders and text fields to reflect the new values set by the mouse event.

The broadcast method posts the notification by first obtaining a reference to the default notification center and then posting a notification called tranfromChanged.

- (void)broadcast
{
    NSNotificationCenter *notify;
    notify =[NSNotificationCenter defaultCenter];
    [notify postNotificationName:@"transformChanged" object:nil];
}

For the Controller to react to the notification, we first have to modify awakeFromNib so that it registers itself to observe the tranfromChanged notification. Since we're good citizens, we also need to remove it as an observer when the object is destroyed.

- (void)awakeFromNib
{
    NSNotificationCenter *notify;

    notify =[NSNotificationCenter defaultCenter];
    [notify addObserver:self selector:@selector(handleNotify:)
                   name:@"tranformChanged" object:nil];;
    [self setFields];    
}

- (void)dealloc
{
    NSNotificationCenter *notify;
    notify =[NSNotificationCenter defaultCenter];
    [notify removeObserver:self];
    
    [super dealloc];
}

The handleNotify method that we set as the observer's selector simply calls the setFields function method.

- (void)handleNotify:(NSNotification *)n
{
    [self setFields];
}

 

// Eek! A Mouse Event

The first thing we need to do is set the lastPoint variable to be equal to the point in our view that the mouse down event occurs at. We do this by first extracting the location from the event and then converting the location to our view's coordinate system. The conversion is needed because the event's location is in the window's coordinate system, not in the view's. We record the location in both the mouseDown and rightMouseDown methods that are inherited from NSResponder.

Additionally, in mouseDown we also sets the point that we translate the object to. We only perform the translation though if the control key is not press since control clicking is equivalent to a right mouse click in this application. We check if the control is pressed by performing a bitwise and with NSControlKeyMask and the event's modifier flags.

- (void)mouseDown:(NSEvent *)event
{
    NSPoint p = [event locationInWindow];
    lastPoint = [self convertPoint:p fromView:nil];

    if (!([event modifierFlags] & NSControlKeyMask))
    {
        [self setPosition:p];
        [self broadcast];
        [self setNeedsDisplay:YES];
    }
}

- (void)rightMouseDown:(NSEvent *)event
{
    NSPoint p = [event locationInWindow];
    lastPoint = [self convertPoint:p fromView:nil];
}

The mouseDragged method works in almost the same way as the the mouseDown method. The only difference is that if the control key is pressed, we call rightMouseDragged and pass the event to it.

- (void)mouseDragged:(NSEvent *)event
{
    NSPoint p = [event locationInWindow];
    p = [self convertPoint:p fromView:nil];

    if ([event modifierFlags] & NSControlKeyMask)
    {
        [self rightMouseDragged:event];
    }
    else
    {
        [self setPosition:p];
    }

    lastPoint = p;

    [self broadcast];
    [self setNeedsDisplay:YES];
}

The rightMouseDown method is where we control the actual scaling and rotation of the drawing. If the shift key is pressed, it means that the user is scaling. If its not, then the user is rotating. So, we start by checking for the shift key's modifier mask. For both operations, we check if the cursor is moving to the left or to the right. Then depending on the direction we scale up or down by 5% if scaling or rotate by 5 degrees.

- (void)rightMouseDragged:(NSEvent *)event
{
    NSPoint p = [event locationInWindow];
    p = [self convertPoint:p fromView:nil];

    if ([event modifierFlags] & NSShiftKeyMask)
    {
        if (p.y < lastPoint.y)
        {
            scale *= .95;
        }
        else
        {
            scale *= 1.05;

            if (scale > 300)
                scale = 300;
        }
    }
    else
    {
        if (p.x < lastPoint.x)
        {
            rotate += 5;

            if (rotate > 360)
                rotate -= 360;
        }
        else
        {
            rotate -= 5;

            if (rotate < 0)
                rotate += 360;
        }
    }

    lastPoint = p;

    [self broadcast];
    [self setNeedsDisplay:YES];
}

 

// Conclusion

Those are the basics of handling mouse events. We now have an application that will perform all of our transformations with mouse events. There's a lot more that can be done with them. Check out the documentation for the additional methods and modifiers that can be used.

As always, feel free to contact me if you have any questions or comments. You can also download a copy of my project from here.


Comments
Post a comment