Cocoa Programming for Mac OS X 第十八章(Images and Mouse Events)摘录

Chapter 18. Images and Mouse Events


NSResponder

NSView inherits from NSResponder. All the event-handling methods are declared in NSResponder. We discuss keyboard events in the next chapter. For now, we are interested only in mouse events. NSResponder declares these methods:

    - (void)mouseDown:(NSEvent *)theEvent;
    - (void)rightMouseDown:(NSEvent *)theEvent;
    - (void)otherMouseDown:(NSEvent *)theEvent;
 
 
    - (void)mouseUp:(NSEvent *)theEvent;
    - (void)rightMouseUp:(NSEvent *)theEvent;
    - (void)otherMouseUp:(NSEvent *)theEvent;
 
 
    - (void)mouseDragged:(NSEvent *)theEvent;
    - (void)rightMouseDragged:(NSEvent *)theEvent;
    - (void)otherMouseDragged:(NSEvent *)theEvent;
    
    - (void)scrollWheel:(NSEvent *)theEvent;


Note that the argument is always an NSEvent object.


NSEvent

An event object has all the information about what the user did to trigger the event. When you are dealing with mouse events, you might be interested in the following methods:

- (NSPoint)locationInWindow

This method returns the location of the mouse event.


- (unsigned int)modifierFlags

The integer tells you which modifier keys the user is holding down on the keyboard. This enables the programmer to tell a Control-click from a Shift-click, for example. The code would look like this:

- (void)mouseDown:(NSEvent *)e
{
    unsigned int flags;
    flags = [e modifierFlags];
    if (flags & NSControlKeyMask) {
         ...handle control click...
    }
    if (flags & NSShiftKeyMask) {
         ...handle shift click...
    }
}

Here are the constants that you commonly AND (&) against the modifier flags:

   NSShiftKeyMask
   NSControlKeyMask
   NSAlternateKeyMask
   NSCommandKeyMask

 
  
- (NSTimeInterval)timestamp
This method gives the time interval in seconds between the time the machine booted and the time of the event. NSTimeInterval is a double.




- (NSWindow *)window
This method returns the window associated with the event.


- (int)clickCount
Was it a single-, double-, or triple-click?


- (float)pressure

If the user is using an input device that gives pressure (a tablet, for example), this method returns the pressure. It is between 0 and 1.


- (float)deltaX;
- (float)deltaY;
- (float)deltaZ;

These methods give the change in the position of the mouse or scroll wheel.


Getting Mouse Events

To get mouse events, you need to override the mouse event methods in StretchView.m:

#pragma mark Events

- (void)mouseDown:(NSEvent *)event{
    NSLog(@"mouseDown: %d", [event clickCount]);
}

- (void)mouseDragged:(NSEvent *)event{
    NSPoint p = [event locationInWindow];
    NSLog(@"mouseDragged:%@", NSStringFromPoint(p));
}

- (void)mouseUp:(NSEvent *)event{
    NSLog(@"mouseUp:");
}

Build and run your application. Try double-clicking, and check the click count. Note that the first click is sent and then the second click. The first click has a click count of 1; the second click has a click count of 2.

Note the use of #pragma mark. At the top of any Xcode editing window there is a pop-up that enables you to jump to any of the declarations and definitions in the file. #pragma mark puts a label into that pop-up. Stylish programmers (like you, dear reader) use it to group their methods.


Composite an Image onto Your View

You will also need to change StretchView so that it uses the opacity and image. First, declare variables and methods in your StretchView.h file:

#import <Cocoa/Cocoa.h>

@interface StretchView : NSView{
    NSBezierPath *path;
    NSImage *image;
    float opacity;
}

@property (readwrite) float opacity;
- (void)setImage:(NSImage *)newImage;
- (NSPoint)randomPoint;

@end

Now implement these methods in your StretchView.m file:

#pragma mark Accessors

- (void)setImage:(NSImage *)newImage{
    [newImage retain];
    [image release];
    image = newImage;
    [self setNeedsDisplay:YES];
}

- (float)opacity{
    return opacity;
}
 
 
- (void)setOpacity:(float)x{
    opacity = x;
    [self setNeedsDisplay:YES];
}

At the end of each of the methods, you inform the view that it needs to redraw itself. Near the end of the initWithFramemethod, set opacity to be 1.0:

        [path closePath];
        opacity = 1.0;
        return self;
}

Also in StretchView.m, you need to add compositing of the image to the drawRect: method:

- (void)drawRect:(NSRect)rect{
    NSRect bounds = [self bounds];
    [[NSColor greenColor] set];
    [NSBezierPath fillRect:bounds];
    [[NSColor whiteColor] set];
    [path fill];
    if (image) {
        NSRect imageRect;
        imageRect.origin = NSZeroPoint;
        imageRect.size = [image size];
        NSRect drawingRect = imageRect;
        [image drawInRect:drawingRect
                 fromRect:imageRect
                operation:NSCompositeSourceOver
                 fraction:opacity];
    }
}

Note that the drawInRect:fromRect:operation:fraction: method composites the image onto the view. The fraction determines the image's opacity.

You should release the image in your dealloc method:

- (void)dealloc
{
    [path release];
    [image release];
    [super dealloc];
}

Build and run your application. You will find a few images in /Developer/Examples/AppKit/Sketch . When you open an image, it will appear in the lower-left corner of your StretchView object.


The View's Coordinate System

The final bit of fun comes from being able to choose the location and dimensions of the image, based on the user's dragging. The mouseDown will indicate one corner of the rectangle where the image will appear, and the mouseUp will indicate the opposite corner. The final application will look something like Figure 18.8.

Figure 18.8. Completed Application


Each view has its own coordinate system. By default, (0, 0) is in the lower-left corner. This is consistent with PDF and PostScript. If you wish, you can change the coordinate system of the view. You can move the origin, change the scale, or rotate the coordinates. The window also has a coordinate system.

If you have two views, a and b, and you need to translate an NSPoint p from b's coordinate system to a's coordinate system, it would look like this:

NSPoint q = [a convertPoint:p fromView:b];

If b is nil, the point is converted from the window's coordinate system.

Mouse events have their locations in the window's coordinate system, so you will nearly always have to convert the point to the local coordinate system. You are going to create variables to hold onto the corners of the rectangle where the image will be drawn.

Add these instance variables to StretchView.h:

    NSPoint downPoint;
    NSPoint currentPoint;

The location of the mouseDown: will be downPoint and currentPoint will be updated by mouseDragged: and mouseUp:.

Edit the mouse event-handling methods to update downPoint and currentPoint:

- (void)mouseDown:(NSEvent *)event
{
    NSPoint p = [event locationInWindow];
    downPoint = [self convertPoint:p fromView:nil];
    currentPoint = downPoint;
    [self setNeedsDisplay:YES];
}

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

- (void)mouseUp:(NSEvent *)event
{
    NSPoint p = [event locationInWindow];
    currentPoint = [self convertPoint:p fromView:nil];
    [self setNeedsDisplay:YES];
}

Add a method to calculate the rectangle based on the two points:

- (NSRect)currentRect
{
    float minX = MIN(downPoint.x, currentPoint.x);
    float maxX = MAX(downPoint.x, currentPoint.x);
    float minY = MIN(downPoint.y, currentPoint.y);
    float maxY = MAX(downPoint.y, currentPoint.y);

    return NSMakeRect(minX, minY, maxX-minX, maxY-minY);
}

(I don't know why, but many people mistype that last method. Look at yours once more before going on. If you get it wrong, the results are disappointing.)

Declare the currentRect method in StretchView.h.

So that the user will see something even if he or she has not dragged, initialize downPoint and currentPoint in thesetImage: method:

- (void)setImage:(NSImage *)newImage
{
    [newImage retain];
    [image release];
    image = newImage;
    NSSize imageSize = [newImage size];
    downPoint = NSZeroPoint;
    currentPoint.x = downPoint.x + imageSize.width;
    currentPoint.y = downPoint.y + imageSize.height;
    [self setNeedsDisplay:YES];
}

In the drawRect: method, composite the image inside the rectangle:

- (void)drawRect:(NSRect)rect
{
    NSRect bounds = [self bounds];
    [[NSColor greenColor] set];
    [NSBezierPath fillRect:bounds];
    [[NSColor whiteColor] set];
    [path stroke];
    if (image) {
        NSRect imageRect;
        imageRect.origin = NSZeroPoint;
        imageRect.size = [image size];
        NSRect drawingRect = [self currentRect];
        [image drawInRect:drawingRect
                 fromRect:imageRect
                operation:NSCompositeSourceOver
                 fraction:opacity];
    }
}

Build and run your application. Note that the view doesn't scroll when you drag past the edge. It would be nice if the scroll view would move to allow users to see where they have dragged to, a technique known as autoscrolling.

Autoscrolling

To add autoscrolling to your application, you will send the message autoscroll: to the clip view when the user drags. You will include the event as an argument. Open StretchView.m and add the following line to the mouseDragged: method:

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

Build and run your application.

Note that autoscrolling happens only as you drag. For smoother autoscrolling, most developers will create a timer that sends the view the autoscroll: method periodically while the user is dragging. Timers are discussed in Chapter 24.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值