[comp.sys.mac.programmer] TrackButton

awd@dbase.UUCP (Alastair Dallas) (06/27/89)

It occurred to me that the following simple function might be interesting
to the net.  The first version was considerably longer and it got polished
down to this, so I thought I'd invite the net to critique it and help
_really_ polish it.  I have a fondness for "lapidary code."


Boolean TrackButton(button)
Rect *button;
{

	Boolean wasIN = FALSE;
	Point mouse;

	while (StillDown())
		{
		GetMouse(&mouse);
		if (PtInRect(mouse, button) != wasIN)
			{
			InvertRect(button);
			wasIN = !wasIN;
			}
		}

	if (wasIN)
		InvertRect(button);

	return (wasIN);

}	/* TrackButton */


This does the job using THINK C v3.0.  I'd be interested to hear of any
language incompatibilities, etc. that would make it non-portable.  Also, 
the various toolbox routines that Track.. things usually take an initial
point as an argument--why is this?  Finally, any thoughts about how to
make the button flash before returning TRUE?  My current solution (not shown)
is pretty crude and I'd like to be more Mac-family-clean (isn't there a
parameter RAM setting or something?)

By the way, I'm not missing something obvious, am I?  I mean, the Control
Manager seems like a lot of overkill to handle a simple (and custom)
button or two.  Should I write a CDEF for this?

Anyway.  Comments are welcome...

/alastair/

awd@dbase.UUCP (Alastair Dallas) (06/27/89)

I posted the TrackButton() function without a disclaimer.  This was a 
really stupid thing to do, because not everyone knows that I write IBM PC
code for Ashton-Tate and that Mac programming is a sideline/hobby for me.
Therefore, the code and discussion in the previous posting were mine:
my code, my opinions, my discussion.  I'm not publishing Ashton-Tate
code on the net (just call me the nuProtagonist :-), and in fact Ashton-Tate
is not involved with the previous posting whatsoever.  Thank you.

/alastair/

jmunkki@kampi.hut.fi (Juri Munkki) (06/27/89)

In article <128@dbase.UUCP> awd@dbase.UUCP (Alastair Dallas) writes:
<Boolean TrackButton(button)
<Rect *button;
<{
<	Boolean		wasIN = FALSE;
<	Point		mouse;
<	while (StillDown())
<	{ 	GetMouse(&mouse);
<		if (PtInRect(mouse, button) != wasIN)
<			{	InvertRect(button);
<				wasIN = !wasIN;
<			}
<		}
<	if (wasIN) 	InvertRect(button);
<	return (wasIN);
<}	/* TrackButton */
<By the way, I'm not missing something obvious, am I?  I mean, the Control
<Manager seems like a lot of overkill to handle a simple (and custom)
<button or two.  Should I write a CDEF for this?  Comments are welcome...

What if the user quickly presses and releases the mouse button on the
control and then moves the mouse away from the button area. Your routine
does not detect this. You should use GetNextEvent to find when the button
comes up and where it comes up. Since GetNextEvent returns the mouse
location, you can remove the GetMouse too. 

Your code is not compatible with Quickeys. I used to program this way
(take a look at my Mandelbrot DA source), but discovered that since it
does not depend on mouseUp events, it doesn't work with QuicKeys.

_._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._
|     Juri Munkki jmunkki@hut.fi  jmunkki@fingate.bitnet        I Want   Ne   |
|     Helsinki University of Technology Computing Centre        My Own   XT   |
^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^

lsr@Apple.COM (Larry Rosenstein) (06/28/89)

In article <23239@santra.UUCP> jmunkki@kampi.hut.fi (Juri Munkki) writes:
> What if the user quickly presses and releases the mouse button on the
> control and then moves the mouse away from the button area. Your routine
> does not detect this. You should use GetNextEvent to find when the button

The example code called StillDown which seems to be the right thing to do. 
 StillDown returns FALSE if the event queue contains a mouseUp event, 
which would handle the case you describe.  (You can also call WaitMouseUp, 
which does the same thing and removes the mouseUp from the event queue.)

Perhaps you can elaborate on why this does not work with QuicKeys; the 
code does take into account mouseUp events.

> comes up and where it comes up. Since GetNextEvent returns the mouse
> location, you can remove the GetMouse too. 

But you would have to add a call to GlobalToLocal because GetNextEvent 
returns global coordinates and GetMouse returns local coordinates.

Larry Rosenstein, Apple Computer, Inc.
Object Specialist

Internet: lsr@Apple.com   UUCP: {nsc, sun}!apple!lsr
AppleLink: Rosenstein1

jmunkki@kampi.hut.fi (Juri Munkki) (06/28/89)

In article <2509@internal.Apple.COM> lsr@Apple.COM (Larry Rosenstein) writes:
>In article <23239@santra.UUCP> jmunkki@kampi.hut.fi (Juri Munkki) writes:
>> What if the user quickly presses and releases the mouse button on the
>> control and then moves the mouse away from the button area. Your routine
>> does not detect this. You should use GetNextEvent to find when the button
>
>The example code called StillDown which seems to be the right thing to do. 
> StillDown returns FALSE if the event queue contains a mouseUp event, 
>which would handle the case you describe.  (You can also call WaitMouseUp, 
>which does the same thing and removes the mouseUp from the event queue.)

You still get the wrong mouse coordinates if you use stilldown/getmouse.
With GetNextEvent you get the coordinate where the mouseup happened. GetMouse
just tells you the current mouse locations. These are not the same thing.

>Perhaps you can elaborate on why this does not work with QuicKeys; the 
>code does take into account mouseUp events.

Ok. Here's the code I used in my Mandelbrot DA.

/* A general purpose pseudo-button
** handler. Give it a rect and it
** handles the rectangle as a button
** and returns true if the button was
** hit.
*/
int	ClickRect(box)
Rect	*box;
{
register	int	hilited=0,where;
		Point	spot;
	
	while(StillDown())
	{	GetMouse(&spot);
		where=PtInRect(spot,box);
		if(where!=hilited)
		{	hilited=where;
			InvertRect(box);
		}
	}
	if(hilited) InvertRect(box);

	return hilited;
}
Now take a look at the code that was posted. There are some very minor
differences, but basically it's the same program. I don't know how
QuicKeys is supposed to work, but defining a click in the box does
not produce the expected results.


_._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._
|     Juri Munkki jmunkki@hut.fi  jmunkki@fingate.bitnet        I Want   Ne   |
|     Helsinki University of Technology Computing Centre        My Own   XT   |
^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^

jmunkki@kampi.hut.fi (Juri Munkki) (06/28/89)

In article <2509@internal.Apple.COM> lsr@Apple.COM (Larry Rosenstein) writes:
>Perhaps you can elaborate on why this does not work with QuicKeys; the 
>code does take into account mouseUp events.

It took some writing to find this out and even then I can only guess at
what happens. The assumptions I make here are based on the following
code fragment and quickeys behavior with it:

int	ClickRect(box)
Rect	*box;
{
	EventRecord	myEvent;	
	register int	hilited=0,where;
	
	do
	{	GetNextEvent(mUpMask,&myEvent);
		GlobalToLocal(&myEvent.where);
		where=PtInRect(myEvent.where,box);
		if(where!=hilited)
		{	hilited=where;
			InvertRect(box);
		}
	}	while(myEvent.what!=mouseUp);
	if(hilited) InvertRect(box);
	return hilited;
}

What happens is that the mouseup is never detected. You have to click
somewhere outside the "button" to release the control. If you define
the click so that the mouse moves even a single pixel, both versions
will work. Neither works if the mouse doesn't move. I assume that
quickeys doesn't bother to post a mouseup if the mouse position doesn't
change. (I'd call that a bug.)

The above code is still preferable because it handles event buffering
correctly. You can click on buttons while a program is calculating and
the program will behave correctly.

IM-I-36:
>The system provides a "mouse-ahead"; that is, any mouse actions the user
>performs when the application isn't ready to process them are saved in a
>buffer and can be processed at the apllication's convenience. Alternatively,
>the application can choose to ignore saved-up mouse actions, but should do
>so only to protect the user from possibly damaging consequences.


_._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._
|     Juri Munkki jmunkki@hut.fi  jmunkki@fingate.bitnet        I Want   Ne   |
|     Helsinki University of Technology Computing Centre        My Own   XT   |
^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^

isle@eleazar.dartmouth.edu (Ken Hancock) (07/03/89)

In article <23257@santra.UUCP> jmunkki@kampi.hut.fi (Juri Munkki) writes:
:>
:>Ok. Here's the code I used in my Mandelbrot DA.
:>
:>/* A general purpose pseudo-button
:>** handler. Give it a rect and it
:>** handles the rectangle as a button
:>** and returns true if the button was
:>** hit.
:>*/
:>int	ClickRect(box)
:>Rect	*box;
:>{
:>register	int	hilited=0,where;
:>		Point	spot;
:>	
:>	while(StillDown())
:>	{	GetMouse(&spot);
:>		where=PtInRect(spot,box);
:>		if(where!=hilited)
:>		{	hilited=where;
:>			InvertRect(box);
:>		}
:>	}
:>	if(hilited) InvertRect(box);
:>
:>	return hilited;
:>}

Technically, you should force an invert first since the mouse may
be up by the time you arrive at your tracking routine...


Ken


Ken Hancock  '90                   | BITNET/UUCP/
Personal Computing Ctr Consultant  |   INTERNET:  isle@eleazar.dartmouth.edu
-----------------------------------+----------------------------------------
DISCLAIMER?  I don't get paid enough to worry about disclaimers.