Cocoa - capturing specific events - objective-c

I'm fairly new to Cocoa programming, and have a question about control event handling.
I create an 'action' for a button, and get an updated AppDelegate.m to handle this eg.
- (IBAction)seedBtnPressed:(id)sender {
NSString* myString = #"Hi there";
[_updateLbl setStringValue:myString];
}
When running this, pressing the 'seed' button does what it should - the label updates.
My question is: why have I captured the 'button press event' by default, as I don't see any place where I've specified this. Alternately, how would I capture a mouse-over event with an action? I gather I'd create another action for the button, but am not sure how to specify this to handle 'mouse-over' events only? Sorry if I've used Windows terminology here, I understand Cocoa uses different names for things.
Thanks
Pete

You need to Subclass the NSButton class (or even better the NSButtonCell class).
- (void)mouseEntered:(NSEvent *)theEvent;
- (void)mouseExited:(NSEvent *)theEvent;
They should get called when the mouse enter and exit the area. You may also need to re create the tracking area, look here:
- (void)updateTrackingAreas
For fade in and fade out effect I played with animator and alpha value for example:
[[self animator]setAlphaValue:0.5];

To get mouse-over events for an NSView you should use the NSTrackingArea class (assuming you're targeting a relatively modern version of OS X). Apple have good documentation on this available at http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/EventOverview/TrackingAreaObjects/TrackingAreaObjects.html
For your other query about the seedBtnPressed: triggering although you don't specify it - have you set an action in Interface Builder for the button rather than programmatically?

Related

Cocoa window dragging

I'm using addGlobalMonitorForEventsMatchingMask to listen to events in Cocoa:
[NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDraggedMask
handler:^(NSEvent *event) {
NSLog(#"Dragged...");
}];
Though I would like to know if I'm dragging/moving a window (and which window that is, I can find the focused window though when holding command and dragging a window it doesn't get focus as far as I know.)
So, can I detect whether or not I'm dragging a window?
Update:
I now have a class: "SATest : NSObject <NSWindowDelegate>" in which I implement the windowDidMove method (and later perhaps windowWillMove too.) Though, now the next step would be attaching this to a window, right? So my question now is: How do I attach the delegate to all windows of all apps?
Update 2:
I now can find a list of all open windows on the screen:
AXUIElementRef _systemWideElement;
_systemWideElement = AXUIElementCreateSystemWide();
CFArrayRef _windows;
AXUIElementCopyAttributeValues(_systemWideElement, kAXWindowsAttribute, 0, 100, &_windows);
Now I have to iterate over the windows, and of each get it's NSWindow so I can add my delegate to it: [window setDelegate:self];
Update 3: To be clear, this question is about detecting the dragging of ALL windows of ALL apps. Not only the windows of my own app.
Also, I'm very new to this event and window management stuff, so no need to keep your answer short I'm happy to read a lot :P
Thanks!
-P
To find out if a window is being dragged you need to have an object that acts as the delegate of the window by responding to the following messages of the NSWindowDelegate protocol:
windowWillMove - this tells the delegate that the window is about to move.
windowDidMove - this tells the delegate that the window has moved.
You can retrieve the NSWindow object in question by sending object to the notification parameter sent to these methods:
e.g.
NSWindow draggedWindow = [notification object];
More information can be found here.
Update:
In response to your request about getting this information for all windows the NSApplication class provides a method which returns an array of all windows owned by the application. The typical way of getting this information is to use one of the NSApplicationDelegate methods to get a reference to your application object.
For example, in your app delegate (pseudocode):
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSApplication * app = [aNotification object];
// you now have a reference to your application.
// and can iterate over the collection of windows and call
// [window setDelegate:self]; for each window.
}
Note that you will need to add / remove you delegates as windows are added and removed. The best method for doing this is – applicationDidUpdate:.
This should be enough to get you started solving your problem.
As suggested by Benjamin the answer lies in the accessibility API. I was looking around in this for a while, even before I asked this question, but never got it to do what I wanted. I now have found a pretty nice solution.
At a high-level I do the following:
Listen to the mouse down event and remember both on which window you clicked and it's location.
Listen to the mouse up event and check if the location has changed, if so you know you moved a window
You can do something similar for the size if you also want to know if you resized. There might be a better solution, but after days of trying stuff this is the only way I got it to work the way I wanted.
Hope this helps anyone who was looking for something similar.
-Pablo

Why cant I see all available methods or properties present in a Class in XCode?

I am noob to XCode and Obj-C world, so pardon me for my naive question.
Following a tutorial, I was writing a basic program in Cocos2D that would accept touch input.
And one of the task was to add implement ccTouchBegan method, but that would not show up in XCode's code sense.
I wanted to implement this method
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
but I would find only this one (related to touch began)
-(BOOL) ccTouchesBeganWithEvent:(*)event
Now that I a noob, how do I explore list of all available methods and properties for a particular Class?
The image shows list of available methods that starts with ccT. ccTouchBegan and ccTouchEnded showed up because I have implemented them already. Else, they would not show just like ccTouchCancelled and ccTouchMoved events didn't show up.
How do I see all available methods & properties of a Class or Object?
To see all the available methods & properties just press Ctrl+Space
Option+Click on the object (in this case, that would be the delegate name in the header file between the < >). A small help popup will appear. Click the blue text inside the popup and the Organizer window will open at the documentation for the Class. The Documentation has all methods and properties you can use.
Just press Backspace 3 times to delete the ccT you typed in. The list will increase with every character you erase. The full list not only shows methods, as you will see.
Maybe it does not show up because you have already defined it in your code? That would be logical, as double implementation would lead to a compiler error.

UIImage for UIActionSheet button?

I know that UIActionSheets don't offer that much customization but what I am asking, is that instead of the grayish/white buttons, can I use a green button (my own UIImage)? I can supply my own image with the text already on there that I want; so using a normal UIActionSheet, can I supply my own image on one of the buttons? If so, how should I go upon doing that?
Thanks,
O.Z
#huesforalice is right - the cleanest way would be to create your own replacement of UIActionSheet. Basically you have 3 options:
A real replacement: You create a UIActionSheet-subclass to be protocol-compatible to ´UIActionSheetDelegate´. This would allow you to use it exactly as a UIActionSheet — but it might be a costly process to figure out when and why a UIActionSheet will call the delegates method implementation.
Even go a bit further and also extend the protocol. This will give you more possibilities, how to use it (i.e. allow picker to be used via new protocol methods), but will be even harder.
The most easiest way will be to create a very own implementation, that doesn't rely on UIActionSheet nor it's protocol — but it won't replace real UIActionSheet, in the meaning that you cannot drop it into your project and expect it to work. But you will have the highest degree on freedom.
I would recommend 3. I found a project, that is working like that. But be warned: It shows you how to do it in general, but has some poor underlying design-decisions:
It uses a method
- (void) addButtonWithTitle: (NSString*) buttonTitle buttonId: (NSInteger) buttonId textColor: (UIColor*) textColor textShadowColor: (UIColor*) textShadowColor backgroundColor: (UIColor*) buttonBackgroundColor increasedSpacing: (BOOL) spacing
Instead — IMHO — it should be
- (void) addButton: (UIButton*) button;
So you can add buttons with different designs more flexible, and don't depend for a section id, what is totally unnecessary, as the object has its own identity as an object already.
The method [actionSheet showWithAnimation:YES]; should be called
showor showAnimated: as …withAnimation: usually takes a block to perform a custom animation.
This ist not possible using UIActionSheet and the documented methods. You could write your own actionsheet, which you then probably would add to the main window and animate to slide up. Possibly there is a way to do what you want by analyzing the actionsheets private view hierarchy and adding custom buttons, but you have to keep in mind, that private view hierarchies may change from one iOS Version to another, so your app might break or might even get rejected from apple.

How to activate a custom screensaver preview in Cocoa/Obj-C?

I have created a fairly simple screensaver that runs on Mac OS 10.6.5 without issue.
The configuration screen has accumulated quite a few different options and I'm trying to implement my own preview on the configureSheet window so the user (just me, currently) can immediately see the effect of a change without having to OK and Test each change.
I've added an NSView to the configureSheet and set the custom class in Interface Builder to my ScreenSaverView subclass. I know that drawRect: is firing, because I can remove the condition for clearing the view to black, and my custom preview no longer appears with the black background.
Here is that function (based on several fine tutorials on the Internet):
- (void)drawRect:(NSRect)rect
{
if ( shouldDrawBackground )
{
[super drawRect:rect];
shouldDrawBackground = NO;
}
if (pausing == NO)
[spiroForm drawForm];
}
The spiroForm class simply draws itself into the ScreenSaverView frame using NSBezierPath and, as mentioned, is not problematical for the actual screensaver or the built-in System Preferences preview. The custom preview (configureView) frame is passed into the init method for, um, itself (since its custom class is my ScreenSaverView subclass.) The -initWithFrame method is called in configureSheet before returning the configureSheet object to the OS:
[configureView initWithFrame:[configureView bounds] isPreview:YES];
Maybe I don't have to do that? It was just something I tried to see if it was required for drawing.
I eventually added a delegate to the configureSheet to try triggering the startAnimation and stopAnimation functions of my preview via windowWillBeginSheet and windowWillEndSheet notifications, but those don't appear to be getting called for some reason. The delegate is declared as NSObject <NSWindowDelegate> and I set the delegate in the configureSheet method before returning the configureSheet object.
I've been working on this for days, but haven't been able to find anything about how the OS manages the ScreenSaverView objects (which I think is what I'm trying to emulate by running my own copy.)
Does anybody have any suggestions on how to manage this or if Apple documents it somewhere that I haven't found? This isn't really required for the screensaver to work, I just think it would be fun (I also looked for a way to use the OS preview, but it's blocked while the configureSheet is activated.)
OK, there are a couple of 'duh' moments involved with the solution:
First of all, I was setting the delegate for the sheet notifications to the sheet itself. The window that the sheet belongs to gets the notifications.
Secondly, that very window that the sheet belongs to is owned by System Preferences, I don't see any way to set my delegate class as a delegate to that window, so the whole delegate thing doesn't appear to be a viable solution.
I ended up subclassing NSWindow for the configureSheet so that I could start and stop animation on my preview by over-riding the makeKeyWindow and close methods.
- (void) makeKeyWindow
{
if (myPreview != nil)
if ( ! [myPreview isAnimating])
{
[myPreview startAnimation];
}
[super makeKeyWindow];
}
I also had to add an IBOutlet for my preview object itself and connect it in Interface Builder.
Still working out a couple of issues, but now when I click on my screensaver Options button, my configureSheet drops down and displays its own preview while you set options. Sheesh. The hoops I jump through for these little niceties. Anyway, I like it. Onward and upward.

NSDocument and keyboard (like keyDown)

I've tried to read on apple documentation but I can't find anywhere how to capture key event (space or other) into an NSDocument application.
With initialFirstRepsodner if I've understand well it's not possible to do.
Any idea?
thanks to all!
Andrea
I've tried to read on apple documentation but I can't find anywhere how to capture key event (space or other) into an NSDocument application.
What do you want to handle key events for? You need to implement keyDown: somewhere, but exactly where depends on what you intend to do.
If you want to capture all the events going to a window, you can subclass it and override -sendEvent:. If you want to capture all the events in the entire app, you can override the same method in an NSApplication subclass.
For first, I'd like to thank Peter for help!
I've used hotkey and this sample has been very usefull!
http://dbachrach.com/blog/2005/11/program-global-hotkeys-in-cocoa-easily/
thanks to all!
Andrea
First of all you have to create a subclass of NSWindow.
In xcode do: File -> New File -> Objective C Class.
give a name like "NSWindowMyEvents". That will create 2 files: .h & .m, go to the NSWindowMyEvents.h and make the declaration as follows:
#interface NSWindowMyEvents : NSWindow {
}
Save changes and compile (to be sure that IB reads the new header 0 if it is already open).
Open interface builder and load your nib/xib file that contains your Document/Main Window.
Ensure that the "window" outlet of the File's owner is set to your main window.
Click on your main Window (the one that you want to get events) and set its class (via Identity inspector cmd+6) to be: NSWindowMyEvents instead of NSWindow that it is now.
Save changes!
Go back to xcode and NSWindowMyEvents.m and paste the following code:
- (void)keyDown:(NSEvent *)theEvent
{
NSLog(#"keyDown!");
if ([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask)
{
NSLog(#"CommandKey Down!");
}
[super keyDown:theEvent];
}
Send the Event to super IF you want, to pass the event to the rest responder chain.
You are now handling keyboard Events.
Similarly you can handle any event in NSWindowMyEvent.m
Hope that helps....
I would recommend using NSUserDefaults and storing your shared global key combos and then checking keyDown: against those stored preferences and then basing your actions on what key was pressed.
ie: #define kMyKeyCommand #"i"