PDA

View Full Version : How to know if a button is being held




McBgnr
Jun 12, 2009, 12:35 AM
In my application I want to know if the user has clicked on NSButton and not released the mouse, that is the user clicks and holds mouse on NSButton. Is there a way to track this action of user and react accordingly?



garethlewis2
Jun 12, 2009, 02:29 AM
In all honesty, I don't think this is possible.

You connect a button to an action in Interface Builder. Even if you could record the fact that the button is being held, who would get the event? Not your application. NSApplication forwards all messages received in the message loop, so you would probably need to override a very low level method there in either the CFRunloop or NSRunloop.

McBgnr
Jun 12, 2009, 03:27 AM
Thanks for the reply.

Even I was thinking that it would not be easy to track the holding of mouse on a button because we get a single action.

How can I override the message loop? I am not sure... Any pointers wrt it are welcome.

Thomas Harte
Jun 12, 2009, 04:10 AM
I continue to be enthusiastic but inexperienced at Cocoa, but have you tried subclassing NSButton and supplying mouseUp, mouseDown and, if you're interested, mouseDragged methods as per the informal protocol defined by NSResponder?

McBgnr
Jun 13, 2009, 11:52 PM
I tried to debug the code little more and realised that although the buttonclick function was getting called only once the user has released the button after holding it for a long time. Thus, my problem could be due to some other reason.

The problem that I am facing is that I have a worker thread in my code and from the worker thread i am calling perfromselectoronmainthread for updating some UI elements. But when the button is held by the user it, the performselectoronmainthread does not update the UI. I am not sure why the UI thread is not updating the UI while the user has held the button.

I am using performselectormainthread with waitUntilDone flag set to TRUE.

Please suggest how this problem could be solved.

Krevnik
Jun 14, 2009, 01:18 AM
Depends on how you define it as a problem. Is the problem that the UI doesn't update, or that the worker thread hangs while it waits for the main thread to unblock?

If the former, there isn't a whole lot you can do here.

If the latter, then the focus should be on not blocking the worker thread while the main thread is blocked. I'd personally do something like packaging the UI data as something that can be placed in a cross-thread data structure, adding it a queue object in the worker thread, and using the call into the main selector as an 'event'. You can even use a flag or some other check to make sure the selector being called multiple times doesn't actually try to perform work multiple times.

McBgnr
Jun 14, 2009, 01:47 AM
Thats gives another way to look at the problem.... yes it is indeed correct that I can take it as the worker thread is waiting for main thread to unblock.

I had earlier tried using performselectoronmainthread with waitUntilDone flag set to NO, but it did not update the UI at all. Thus I had shifted to waitUntilDone=YES. I had packaged my data in an object of myClass and was sending the object to the selector method as

myClass *obj = [[[myClass alloc] init] autorelease];
//initialize obj members here

//send obj to updateUserInterface method
[self performSelectorOnMainThread:@selector (updateUserInterface) withObject:obj waitUntilDone:YES];



Which other APIs can I use for this purpose so that my worker thread would not block and my job would be done.Please suggest.


[EDIT] Another thing that I am concerned is that when the Button is being held in the Main Window, the worker thread is getting blocked. Does it main that for that duration the UI thread will not be able to update other components as well?

Krevnik
Jun 14, 2009, 11:27 AM
No, it won't be able to update the UI on the main thread if something is blocking it from doing so. As that is functionality in AppKit, there isn't a whole lot you can do. I don't really know of many apps that can get away with updating while the mouse is down when written in Cocoa.

If you send an autoreleased object, you very well could wind up sending a nil/invalid object by the time the selector actually fires. I'd either use a different data structure (an array or something will probably work best) that lives in the class that will do the updating. Put your objects into that array, and only have the selector use the /latest/ or /last/ item in the array to update the UI when it fires. That way, you can simply empty/clear the array after one update and be done, no matter what causes the main thread to not run your selector for awhile.

kainjow
Jun 14, 2009, 01:09 PM
I think something else is going on in your code. I created a very simple demo project on 10.5 to test and I didn't see any delay when clicking and holding down on a button. The UI updates just fine. Here is the code I used (maybe I've oversimplified the situation?):

- (void)awakeFromNib {
[NSThread detachNewThreadSelector:@selector(thread)
toTarget:self withObject:nil];
}

- (void)thread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

NSInteger i = 0;
for (;;) {
[self performSelectorOnMainThread:@selector(setNumber:)
withObject:[NSNumber numberWithInteger:i++]
waitUntilDone:YES];
[NSThread sleepForTimeInterval:0.1];
}

[pool release];
}

- (void)setNumber:(NSNumber *)num {
[textField setIntegerValue:[num integerValue]];
}

McBgnr
Jun 15, 2009, 05:45 AM
Yes even I think that something else is also going on in my code... because even I tried a simple program and it worked.

In my thread I am enumerating through the file system to get the file names of the contents of the dirs... but not sure why the thread is not updating the UI when the button is held.