NSEvent/mouse events

Discussion in 'Mac Programming' started by liptonlover, Jul 31, 2008.

  1. liptonlover macrumors 6502a

    Joined:
    Mar 13, 2008
    #1
    I read the introduction to cocoa event-handling guide, but I'm just as lost as before except that I know slightly what's going on.

    I have a custom view, which I need so that I can handle mouse events in it. How do I do this? If you can just help me here, or have other tutorials/documents I'd love to try it.

    Thanks in advance, Nate
     
  2. HiRez macrumors 603

    HiRez

    Joined:
    Jan 6, 2004
    Location:
    Western US
    #2
    I assume your custom view is a subclass of NSView (MyView : NSView). If so all you need to do is implement one or more of the methods you're interested in listening for in your view, and they will automatically get called when one of those events occurs, such as:

    - (void)mouseDown:(NSEvent*)theEvent;
    - (void)mouseDragged:(NSEvent*)theEvent;
    - (void)mouseUp:(NSEvent*)theEvent;
    - (void)mouseMoved:(NSEvent*)theEvent;
    - (void)mouseEntered:(NSEvent*)theEvent;
    - (void)mouseExited:(NSEvent*)theEvent;

    You're just overriding the methods in your .m (implementation) file, you don't need to declare these methods in .h because they're already inherited from NSResponder. In Interface Builder, remember to import the header for your custom view and set the class of the view you dropped on the interface to it (otherwise it'll just be an NSView and your code won't get called). Look these methods up in the documentation and they'll tell you how to extract the information you want from the NSEvent object that gets delivered to your overridden method (such as the x, y coordinates of the mouse).
     
  3. liptonlover thread starter macrumors 6502a

    Joined:
    Mar 13, 2008
    #3
    It is a subclass by default, so yes. But I have not created a custom class and assigned it to the view yet, because I wasn't sure of my next step.

    While we're on the topic, I have a few questions if you don't mind.
    All I have is a basic knowledge of methods. All I've dealt with is (IBAction) so I don't really know how methods really work. How do I know if a method will get called automatically and what triggers it? What does the (class *)object? at the end do?

    Thanks for your help so far!
    Nate

    Oh yeah... where'd you get that saying in your signature from? I have almost the same thing... :lol:
     
  4. HiRez macrumors 603

    HiRez

    Joined:
    Jan 6, 2004
    Location:
    Western US
    #4
    Wow, that is really weird on the sig, I actually made that up when I saw my nephews playing Morrowind on the Xbox and talking about how awesome the graphics were. Hmph! Anyway...

    The (class*)object thingys are parameters passed into the method when they are called. the parameters don't have to be objects either, they can be primitives or structs as well, but you use * to denote that it's a pointer to an object.

    To determine which methods get called "automatically" (triggered in response to certain events), it's a little tricky. Cocoa events such as mouse movement and key presses are some of those, as well as many different things the runtime will throw out for interested observers, such as applicationWillTerminate:, windowDidResize:, awakeFromNib:, etc. Some of these you receive by subclassing a class that defines these (as in the case of NSView and mouseDown:, or awakeFromNib:), some of them will be received through a delegate connection, some will show up as notifications if you registered for them. A timer firing is another way a method might be called. You need to have a look at the superclasses to see what methods are there. In short, there is no easy answer, you will learn by building programs.

    I highly recommend you go through a Cocoa book (such as the Hillegass one) cover to cover. It will make your life much easier because a lot of that stuff shows up in the examples along the way.
     
  5. liptonlover thread starter macrumors 6502a

    Joined:
    Mar 13, 2008
    #5
    I have the hillegass book and it's always nearby when I'm programming... but I just can't go straight through it. Maybe I'm being lazy but I can't learn that way. But I love the book because it has 30+ chapters all on different topics, and when something new comes up that I want to learn I always read the chapter on it first if it's there. I was surprised there's nothing on NSEvent...
    So the documentation doesn't tell you, there's no hints except what makes sense? *sigh* oh well.

    I probably got it from you, I don't remember where exactly. I'll take mine down lol.
     
  6. Darkroom Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #6
    there's also:
    - (void)rightMouseDown:(NSEvent*)theEvent;
    - (void)rightMouseUp:(NSEvent*)theEvent;

    which i find awesome... but think that anyone using rightMouseDown (especially for popUpContextMenus) should also code in a mouseDown function that would incorporate NSControlKeyMask that simply directs to the rightMouseDown method for the old schoolers (or all those crazies still using one-button mice :p)
     
  7. liptonlover thread starter macrumors 6502a

    Joined:
    Mar 13, 2008
    #7
    I'm trying to test this to get it to work, but I'm running into difficulties.

    I have a custom view who's class is my custom class which is a subclass of NSView. Here's the files:


    #import <Cocoa/Cocoa.h>


    @interface gameView : NSView {
    NSPoint *mouseLoc;
    NSSize *mySize;
    NSRect *myFrame;
    IBOutlet id imageView;
    }

    @end



    #import "gameView.h"


    @implementation gameView

    - (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
    // Initialization code here.
    }
    return self;
    }

    - (void)drawRect:(NSRect)rect {
    // Drawing code here.
    }

    - (void)mouseMoved:(NSEvent*)theEvent {
    [mouseLoc mouseLocation];
    }




    I get this warning for the [mouseLoc mouseLocation]; line.
    warning: invalid receiver type 'NSPoint *'

    If I don't declare it as a pointer, I get this error in addition to the warning.
    error: cannot convert to a pointer type
    What's wrong with my code?
    Thanks in advance, Nate
     
  8. HiRez macrumors 603

    HiRez

    Joined:
    Jan 6, 2004
    Location:
    Western US
    #8
    OK well NSPoint is not an object, it's just a C struct, so you would not want to use * with it. The same goes for NSSize and NSRect. This also threw me when I was learning Cocoa, I think they should have named them such that they don't appear to be object classes. Also, another confusing thing is you never use * with the id type (which you are not here), because it implicitly includes *, which is not obvious to newbies. NSPoint is defined as:

    Code:
    typedef struct _NSPoint {
       CGFloat x;
       CGFloat y;
    } NSPoint;
    As far as the second error goes ("error: cannot convert to a pointer type"), you're trying to send a message to the NSPoint struct. Since it's not an object, it can't receive messages. If you look at the documentation for NSEvent, notice that the mouseLocation: method is preceded by a "+". That means it's a class method, so you have to call it from the class itself, not an object instance, and then you assign it to your variable. In your case you'd write:

    Code:
    mouseLoc = [NSEvent mouseLocation];
    
    CGFloat x = mouseLoc.x; // x location
    CGFloat y = mouseLoc.y; // y location
     
  9. liptonlover thread starter macrumors 6502a

    Joined:
    Mar 13, 2008
    #9
    Ok I have a bugfree app. Trouble is, it isn't doing anything.

    My window has a NSCustomView who's class is customClass which is a subclass of NSView. I have a NSButton that is attached to an outlet called myButton in customClass. Like I said, I don't get any warnings or errors. But nothing happens when I move the mouse around. Here's my new code.


    #import <Cocoa/Cocoa.h>

    @interface customClass : NSView {
    NSPoint mouseLoc;
    NSRect myRect;
    IBOutlet id myButton;
    }

    @end

    #import "customClass.h"

    @implementation customClass
    - (void)mouseMoved:(NSEvent*)theEvent {
    mouseLoc=[NSEvent mouseLocation];
    myRect.origin.x=mouseLoc.x;
    myRect.origin.y=mouseLoc.y;
    [myButton setFrame:myRect];
    }
    @end


    Thanks so much for your help so far, I'm learning more than I expected and it's very helpful :)
     
  10. xinevil macrumors newbie

    Joined:
    Jul 12, 2008
    #10
    If you have the Hillegass book, there actually is a chapter about mouse events (and one about keyboard events). See Chapter 18 (I have the third edition) for a good explanation :) .

    For your specific problem see Chapter 19, and then "For the More Curious: Rollovers". You need to tell it to enable mouseMoved events.
     
  11. liptonlover thread starter macrumors 6502a

    Joined:
    Mar 13, 2008
    #11
    I have the book sitting in front of me and I don't see what I'm missing.

    Here's my code:
    [self setAcceptsMouseMovedEvents:YES];

    I tried doing self, I tried doing [self window], I tried an outlet pointing to the window, I tried doing this in (id)init and I tried doing this in (void)awakeFromNib but nothing is getting any results. I checked my connections, my outlet is good and all that but the darn button isn't moving!.
     
  12. HiRez macrumors 603

    HiRez

    Joined:
    Jan 6, 2004
    Location:
    Western US
  13. HiRez macrumors 603

    HiRez

    Joined:
    Jan 6, 2004
    Location:
    Western US
    #13
    First of all I would put an NSLog statement in your mouseMoved: method to see if it's getting called or not. I would do the setAcceptsMouseMovedEvents line in awakeFromNib:, and there you might also send the button a message to double-check your connection is active (like change the title). You might also need to tell the button to redisplay after you move it:

    Code:
    [myButton setNeedsDisplay:YES];
    or if that doesn't work:

    Code:
    [myButton display];
    If nothing works maybe zip up your project and post it here.

    By the way, by convention the first letter of class names are capitalized in Cocoa (CustomClass, or better yet LLCustomClass or something).
     
  14. liptonlover thread starter macrumors 6502a

    Joined:
    Mar 13, 2008
    #14
    I've been working on the problem more since I posted last... I have new code.


    #import <Cocoa/Cocoa.h>

    @interface customClass : NSView {
    NSPoint mousePoint;
    NSRect buttonRect;
    IBOutlet id myButton;
    }

    @end

    #import "customClass.h"

    @implementation customClass
    - (void)mouseDown:(NSEvent*)theEvent {
    mousePoint=[NSEvent mouseLocation];
    buttonRect.origin.x=mousePoint.x;
    buttonRect.origin.y=mousePoint.y;
    buttonRect.size.width=50;
    buttonRect.size.height=50;
    [myButton setFrame:buttonRect];
    [self display];
    [myButton display];
    }
    @end


    I'm trying to accomplish what should be simple. Click the left mouse button, and the button moves to the location of the mouse. I changed it so I don't have to deal with all that mousemoved stuff that wasn't working.

    What happens with this latest edition is when I click the button I made disappears. This confirms both that my outlet is good and that the method is getting called. I'm not sure why it disappears instead of moving though...
     
  15. HiRez macrumors 603

    HiRez

    Joined:
    Jan 6, 2004
    Location:
    Western US
    #15
    This may have to do with differing coordinate systems between the mouse point you receive (in screen coordinates) and setting the button's frame, which is probably in its superview's coordinate system. Try using:

    Code:
    NSPoint convertedPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil]
    ;

    It's possible you're setting the frame of your button to be outside the bounds of the view, hence it appears invisible.

    Also, wrapping your code in [ code ][ / code ] (without the spaces) tags will make it easier to read.
     
  16. liptonlover thread starter macrumors 6502a

    Joined:
    Mar 13, 2008
    #16
    Thanks but I'm not putting that code in until I know word for word what it's doing. I'm doing this to learn, after all.
    What is the coordinate system dealing with? Why don't all objects start with the same system by default, only changing when you specify?
    What's theEvent doing in there, and why does the location in window matter? And what's fromView for and why isn't it being used?

    Finally, where would I put this line. Right before changing the location?

    Thanks for your help!
     
  17. HiRez macrumors 603

    HiRez

    Joined:
    Jan 6, 2004
    Location:
    Western US
    #17
    From the Cocoa Drawing Guide:

    Converting from Window to View Coordinates
    Events sent to your view by the operating system are sent using the coordinate system of the window. Before your view can use any coordinate values included with the event, it must convert those coordinates to its own local coordinate space. The NSView class provides several functions to facilitate the conversion of NSPoint, NSSize, and NSRect structures. Among these methods are convertPoint:fromView: and convertPoint:toView:, which convert points to and from the view’s local coordinate system. For a complete list of conversion methods, see NSView Class Reference.

    Important: Cocoa event objects return y coordinate values that are 1-based instead of 0-based. Thus, a mouse click on the bottom left corner of a window or view would yield the point (0, 1) in Cocoa and not (0, 0). Only y-coordinates are 1-based.

    The following example converts the mouse location of a mouse event from window coordinates to the coordinates of the local view. To convert to the view’s local coordinate space, you use the convertPoint:fromView: method. The second parameter to this method specifies the view in whose coordinate system the point is currently specified. Specifying nil for the second parameter tells the current view to convert the point from the window’s coordinate system.

    NSPoint mouseLoc = [theView convertPoint:[theEvent locationInWindow] fromView:nil];

    You would put the conversion line somewhere before changing the location, yes. When you use setFrame: on your button, though, I don't know if that's supposed to be in window Coordinates or in local view coordinates.

    You could try a test by setting your button frame to, instead of the mouse location, a reasonable, hard-coded location like 10, 10 just to see if that even works.
     

Share This Page