 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Ramy Guest
|
Posted: Tue Jun 29, 2004 11:34 am Post subject: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
Hi, i think it's better to open new tread becouse the last one
already has too many subjects. My new code is the code bellow,
my button base on a Panel, on this panel i have a small image
which is part of the button's class ( later i'll add also
2 labels under this image ), when i move the mouse curser over
the button i show the panel Inner Bevel, when the mouse is out
of the button i don't show this bevel ( BevelInner = bvNone ).
As i said in the other tread i also need to know if the user
has press inside the button but released the mouse button only
when it was out of my button's borders, then i don't want to
call my ButtonClick operation.
I see 2 problems with this code -
1. if i enter to the button slow and inside the image, and then
go out of the button slowly then everything is fine. but if i am
inside the image, and then i move the mouse fast ( not even too
fast ) out of the button's borders, then the WndProc get only
'CM_MOUSELEAVE' message for the Image, but is doesn't get the
'CM_MOUSELEAVE' and the 'CM_MOUSEENTER' messages for the panel,
so the application still think that it is inside the button
( and i still see panel's bevel... ). Why? and how can i solve
such situations?
2. If i click inside the button, on the image for example, and
then i move the mouse cursor out of the button, and only then
release it, the application still think that it is inside the
button and it calls the MouseUpInsideButton(); function,
this is not good, if the user released the mouse button out
of the button i don't want to do the operation.
Is there any good solution for this situation?
Thanks very much,
Ramy
Here is the code -
// - - - - - - - - - - - WndProc - - - - - - - - - - - - -
void __fastcall TBaseButton::WndProc( TMessage &Message )
{
static bool MouseAlreadyInButton = false;
if ( Message.Msg == CM_MOUSEENTER || Message.Msg == CM_MOUSELEAVE )
{
TImage *ImagePtr = dynamic_cast<TImage*>( reinterpret_cast<TControl*>( Message.LParam ) );
if ( Message.Msg == CM_MOUSEENTER )
{
if ( ImagePtr )
MouseAlreadyInButton = true;
else
if( !MouseAlreadyInButton )
OnMouseEnter();
}
else if( !ImagePtr )
{
RECT Rect;
POINT Point;
::GetWindowRect( Handle, &Rect ); // ( Win 32 Call )
::GetCursorPos( &Point ); // ( Win 32 Call )
if ( !::PtInRect( &Rect, Point ) )
{
MouseAlreadyInButton = false;
OnMouseLeave();
}
}
}
if ( MouseInImage )
{
if ( Message.Msg == WM_LBUTTONDOWN )
{
MouseDownInsideButton();
}
if ( Message.Msg == WM_LBUTTONUP )
{
MouseUpInsideButton(); // = "Button Click"
}
}
inherited::WndProc( Message );
}
// Mouse Enter Event
// - - - - - - - - - - Mouse Enter - - - - - - - - - - - - -
void __fastcall TBaseButton::OnMouseEnter( void )
{
BevelOuter = bvNone;
BevelInner = bvRaised;
MouseInImage = true;
}
// - - - - - - - - - - Mouse Leave - - - - - - - - - - - - -
// Mouse Leave Event
void __fastcall TBaseButton::OnMouseLeave( void )
{
BevelOuter = bvNone;
BevelInner = bvNone;
MouseInImage = false;
}
|
|
| Back to top |
|
 |
Ramy Guest
|
Posted: Tue Jun 29, 2004 11:55 am Post subject: Re: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
OK forget for now about problem 2, i solved it like this -
if ( MouseInImage )
{
if ( Message.Msg == WM_LBUTTONDOWN )
{
MouseDownInsideButton();
}
if ( Message.Msg == WM_LBUTTONUP )
{
RECT Rect;
POINT Point;
::GetWindowRect( Handle, &Rect ); // ( Win 32 Call )
::GetCursorPos( &Point ); // ( Win 32 Call )
if ( !::PtInRect( &Rect, Point ) )
{
MouseAlreadyInButton = false;
OnMouseLeave();
}
else
MouseUpInsideButton(); // = "Button Click"
}
}
Although that it's not a perfect solution becouse i want the
panel's bevel to disappear at the moment i leave the button's
borders, but in my solution if the user press inside the button
and release the mouse left button only when it's out of the
button, then the panel's bevel will be visible untill the user
has release the mouse button...
Thanks,
Ramy
|
|
| Back to top |
|
 |
JD Guest
|
Posted: Tue Jun 29, 2004 8:08 pm Post subject: Re: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
"Ramy" <Ramy (AT) NoMail (DOT) com> wrote:
| Quote: | [...] I see 2 problems with this code -
|
You'll need to search and replace 'ButtonImage' with the name
that you gave the image on the panel and then insert your
OnClick code.
//-------------------------------------------------------------
class TBaseButton : public TPanel
{
protected:
DYNAMIC void __fastcall MouseDown(TMouseButton Button, TShiftState Shift, int X, int Y);
DYNAMIC void __fastcall MouseMove(TShiftState Shift, int X, int Y);
DYNAMIC void __fastcall MouseUp(TMouseButton Button, TShiftState Shift, int X, int Y);
private:
typedef TPanel inherited;
HDC hdc;
TCanvas* ButtonCanvas;
bool ButtonDown;
void __fastcall ButtonImageMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y);
void __fastcall ButtonImageMouseMove(TObject *Sender, TShiftState Shift, int X, int Y);
void __fastcall ButtonImageMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y);
void __fastcall ButtonMouseDown();
void __fastcall ButtonMouseMove( TShiftState Shift );
void __fastcall ButtonMouseUp();
bool __fastcall MouseInButton();
void __fastcall ReleaseButton();
public:
__fastcall TBaseButton( TComponent* Owner );
__fastcall TBaseButton::~TBaseButton();
};
//-------------------------------------------------------------
__fastcall TBaseButton::TBaseButton(TComponent* Owner) : TPanel( Owner )
{
hdc = ::GetDC( Handle );
ButtonCanvas = new TCanvas();
ButtonCanvas->Handle = hdc;
ButtonDown = false;
ButtonImage->OnMouseDown = ButtonImageMouseDown;
ButtonImage->OnMouseMove = ButtonImageMouseMove;
ButtonImage->OnMouseUp = ButtonImageMouseUp;
}
//-------------------------------------------------------------
__fastcall TBaseButton::~TBaseButton()
{
delete ButtonCanvas;
::ReleaseDC( Handle, hdc );
}
//-------------------------------------------------------------
void __fastcall TBaseButton::WndProc( TMessage &Message )
{
if( Message.Msg == CM_MOUSEENTER || Message.Msg == CM_MOUSELEAVE )
{
TRect r = Rect( 0, 0, Width, Height );
if( MouseInButton() ) Frame3D( ButtonCanvas, r, clBtnHighlight, clBtnShadow, 2 );
else Frame3D( ButtonCanvas, r, Color, Color, 2 );
}
inherited::WndProc( Message );
}
//-------------------------------------------------------------
void __fastcall TBaseButton::MouseDown(TMouseButton Button, TShiftState Shift, int X, int Y)
{
ButtonMouseDown();
inherited::MouseDown(Button, Shift, X, Y);
}
//-------------------------------------------------------------
void __fastcall TBaseButton::MouseMove(TShiftState Shift, int X, int Y)
{
ButtonMouseMove( Shift );
inherited::MouseMove(Shift, X, Y);
}
//-------------------------------------------------------------
void __fastcall TBaseButton::MouseUp(TMouseButton Button, TShiftState Shift, int X, int Y)
{
ButtonMouseUp();
inherited::MouseUp(Button, Shift, X, Y);
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ButtonImageMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
ButtonMouseDown();
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ButtonImageMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
ButtonMouseMove( Shift );
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ButtonImageMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
ButtonMouseUp();
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ButtonMouseDown()
{
TRect r = Rect( 0, 0, Width, Height);
Frame3D( ButtonCanvas, r, clBtnShadow, clBtnHighlight, 2 );
ButtonImage->Left = ButtonImage->Left + 1;
ButtonImage->Top = ButtonImage->Top + 1;
ButtonDown = true;
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ButtonMouseMove( TShiftState Shift )
{
if( Shift.Contains(ssLeft) && ButtonDown )
{
if( !MouseInButton() ) ReleaseButton();
}
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ButtonMouseUp()
{
if( ButtonDown )
{
ReleaseButton();
ShowMessage("Execute Button Click");
}
}
//-------------------------------------------------------------
bool __fastcall TBaseButton::MouseInButton()
{
RECT Rect;
POINT Point;
::GetWindowRect( Handle, &Rect );
::GetCursorPos( &Point );
if( ! ::PtInRect(&Rect, Point) ) return false;
else return true;
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ReleaseButton()
{
TRect r = Rect( 0, 0, Width, Height);
Frame3D( ButtonCanvas, r, Color, Color, 2 );
ButtonImage->Left = ButtonImage->Left - 1;
ButtonImage->Top = ButtonImage->Top - 1;
ButtonDown = false;
}
//-------------------------------------------------------------
~ JD
|
|
| Back to top |
|
 |
Ramy Guest
|
Posted: Wed Jun 30, 2004 6:13 am Post subject: Re: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
Hmm... maybe i was a little confused... i think that you already
gave me the answer, i'll check it and let you know if i'll have
problem with this code, now after that i removed the Bevel.
"If the only reason that you're keeping track of the mouse
entering and leaving the panel/image is to know when it was
clicked, you can delete all of that code and use the mouse
events directly.
Assign OnMouseDown/Move/Up events to the Image and check for
clicking only on the Image:
class TBaseButton : public TPanel
{
private:
bool Clicked;
void __fastcall ImageMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y);
void __fastcall ImageMouseMove(TObject *Sender, TShiftState Shift, int X, int Y);
void __fastcall ImageMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y);
};
//-------------------------------------------------------------
__fastcall TBaseButton::TBaseButton(TComponent* Owner) : TPanel( Owner )
{
Clicked = false;
// allocate the image
Image->OnMouseDown = ImageMouseDown;
Image->OnMouseMove = ImageMouseMove;
Image->OnMouseUp = ImageMouseUp;
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ImageMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
Clicked = true;
// call the function to animate a button down click
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ImageMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
if( Shift.Contains(ssLeft) )
{
RECT Rect;
POINT Point;
::GetWindowRect( Image->Handle, &Rect );
::GetCursorPos( &Point );
if( ! ::PtInRect(&Rect, Point) )
{
// animate the button up
Clicked = false;
}
}
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ImageMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( Clicked )
{
Clicked = false;
// animate the button up
// perform OnClick
}
}
//-------------------------------------------------------------
~ JD"
|
|
| Back to top |
|
 |
JD Guest
|
Posted: Wed Jun 30, 2004 6:34 am Post subject: Re: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
"Ramy" <Ramy (AT) NoMail (DOT) com> wrote:
| Quote: | [...] i'll check it and let you know if i'll have problem
with this code [...]
|
Don't use this code. Use the code in my last post. I tested it
by subclass the panel's WndProc because I wasn't inclined to
create a seperate class so there might be a type-o in the
coversion but in my test it worked perfectly.
~ JD
|
|
| Back to top |
|
 |
Ramy Guest
|
Posted: Wed Jun 30, 2004 10:00 am Post subject: Re: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
Hi JD, if you still have that test on your PC, can you please
copy - paste it's code into your next respond in this thread?
Thanks!
"JD" <nospam (AT) nospam (DOT) com> wrote:
| Quote: | Don't use this code. Use the code in my last post. I tested it
by subclass the panel's WndProc because I wasn't inclined to
create a seperate class so there might be a type-o in the
coversion but in my test it worked perfectly.
~ JD
|
|
|
| Back to top |
|
 |
JD Guest
|
|
| Back to top |
|
 |
Ramy Guest
|
Posted: Wed Jun 30, 2004 1:20 pm Post subject: Re: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
Sorry JD i did not see this post, don't have to be so angry,
i'm reading the newsgroup directly from Borland's pages and it's
very hard to follow messages here becouse you don't see them as
one cluster of messages, but messages from one thread are
scattered all around over several pages, so i missed this post.
I'll test your code, and i'll let you know how it goes, thanks.
Ramy
"JD" <nospam (AT) nospam (DOT) com> wrote:
|
|
| Back to top |
|
 |
Ramy Guest
|
Posted: Wed Jun 30, 2004 3:59 pm Post subject: Re: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
Hi JD!
Your new code works great! :-)
I did several changings, for example i kept getting an arror of
"control has no parent window" on the line -
hdc = ::GetDC( Handle );
which was solved after i added this line before it -
Parent = dynamic_cast<TWinControl *>( Owner );
But then i kept getting another error (don't even remember
even what) on the Destructor...
But it doesn't matter any more, i just removed all the code for
the Canvas becouse i don't really need it, i just changes the
Panel's bevels to get the effect that i need.
I'm just interesting, what did you change between this code and
the code before, that solved the problem of not detecting
"MouseLeave" when i moved the mouse fast from the image and out
of the button? becouse now it's working real good, what's the
different than?
Anyway, thanks a LOT for your help!
( My code after the changings is bellow )
Ramy
__fastcall TBaseButton::TBaseButton( TComponent *Owner ) : TPanel( Owner )
{
ButtonImage = new TImage( this );
ButtonImage->Parent = this;
ButtonImage->AutoSize = true;
ButtonImage->Stretch = true;
ButtonImage->Picture->LoadFromFile( "CloseIcon.bmp" );
ButtonImage->Top = 10;
ButtonImage->Left = 10;
ButtonImage->OnMouseDown = ButtonImageMouseDown;
ButtonImage->OnMouseMove = ButtonImageMouseMove;
ButtonImage->OnMouseUp = ButtonImageMouseUp;
ButtonImage->OnMouseDown = ButtonImageMouseDown;
ButtonImage->OnMouseMove = ButtonImageMouseMove;
ButtonImage->OnMouseUp = ButtonImageMouseUp;
BevelOuter = bvNone;
BevelInner = bvNone;
}
//-------------------------------------------------------------
__fastcall TBaseButton::~TBaseButton()
{
//
}
//-------------------------------------------------------------
void __fastcall TBaseButton::WndProc( TMessage &Message )
{
if( Message.Msg == CM_MOUSEENTER || Message.Msg == CM_MOUSELEAVE )
{
TRect r = Rect( 0, 0, Width, Height );
if ( MouseInButton() )
{
BevelOuter = bvNone;
BevelInner = bvRaised;
Form1->Label1->Caption = "In";
}
else
{
BevelOuter = bvNone;
BevelInner = bvNone;
Form1->Label1->Caption = "Out";
}
}
inherited::WndProc( Message );
}
//-------------------------------------------------------------
void __fastcall TBaseButton::MouseDown( TMouseButton Button, TShiftState Shift, int X, int Y )
{
ButtonMouseDown();
inherited::MouseDown( Button, Shift, X, Y );
}
//-------------------------------------------------------------
void __fastcall TBaseButton::MouseMove( TShiftState Shift, int X, int Y )
{
ButtonMouseMove( Shift );
inherited::MouseMove( Shift, X, Y );
}
//-------------------------------------------------------------
void __fastcall TBaseButton::MouseUp( TMouseButton Button, TShiftState Shift, int X, int Y )
{
ButtonMouseUp();
inherited::MouseUp( Button, Shift, X, Y );
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ButtonImageMouseDown( TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y )
{
ButtonMouseDown();
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ButtonImageMouseMove( TObject *Sender, TShiftState Shift, int X, int Y )
{
ButtonMouseMove( Shift );
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ButtonImageMouseUp( TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y )
{
ButtonMouseUp();
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ButtonMouseDown()
{
TRect r = Rect( 0, 0, Width, Height );
BevelOuter = bvRaised;
BevelInner = bvRaised;
ButtonImage->Left = ButtonImage->Left + 1;
ButtonImage->Top = ButtonImage->Top + 1;
ButtonDown = true;
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ButtonMouseMove( TShiftState Shift )
{
if ( Shift.Contains(ssLeft) && ButtonDown )
{
if ( !MouseInButton() )
ReleaseButton();
}
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ButtonMouseUp()
{
if( ButtonDown )
{
ReleaseButton();
ShowMessage( "Execute Button Click" );
}
}
//-------------------------------------------------------------
bool __fastcall TBaseButton::MouseInButton()
{
RECT Rect;
POINT Point;
::GetWindowRect( Handle, &Rect );
::GetCursorPos( &Point );
if( ! ::PtInRect( &Rect, Point ) )
return false;
else
return true;
}
//-------------------------------------------------------------
void __fastcall TBaseButton::ReleaseButton()
{
TRect r = Rect( 0, 0, Width, Height);
BevelOuter = bvNone;
BevelInner = bvNone;
Form1->Label1->Caption = "Out";
ButtonImage->Left = ButtonImage->Left - 1;
ButtonImage->Top = ButtonImage->Top - 1;
ButtonDown = false;
}
//-------------------------------------------------------------
void __fastcall TForm1::Timer1Timer( TObject *Sender )
{
Timer1->Enabled = false;
TBaseButton *Button1 = new TBaseButton( this );
Button1->Parent = this;
Button1->Top = 20;
Button1->Left = 20;
Button1->Width = 40;
Button1->Height = 40;
}
//--------------------------------------------------------------
|
|
| Back to top |
|
 |
Ramy Guest
|
Posted: Wed Jun 30, 2004 4:04 pm Post subject: Re: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
__fastcall TBaseButton::~TBaseButton()
{
delete ButtonImage;
}
Of Course....
Ramy
|
|
| Back to top |
|
 |
JD Guest
|
Posted: Wed Jun 30, 2004 7:30 pm Post subject: Re: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
"Ramy" <Ramy (AT) NoMail (DOT) com> wrote:
| Quote: | [...] i kept getting an arror of "control has no parent
window" on the line -
hdc = ::GetDC( Handle );
|
I didn't think of that one because I just dropped a panel on
the form and tested with it. The error is because until it's
been constructed, the TBaseButton doesn't have a Parent. That
can be fixed by moving that line after the Parent has been
assigned.
| Quote: | which was solved after i added this line before it -
Parent = dynamic_cast<TWinControl *>( Owner );
|
There you go.
| Quote: | I'm just interesting, what did you change between this code
and the code before, that solved the problem of not detecting
"MouseLeave" when i moved the mouse fast from the image and
out of the button?
|
The first sample relied on the messages arriving in a specific
order. I was able to see that the messages were sent to each
control correctly but by moving the mouse quickly, the
MouseLeave for the Panel would arrive before the MouseLeave
for the Image which screwed up the static bool and that code
depended on the static bool being correct.
The big difference in this code is that it doesn't try to
determine if the message was from the panel or the image and
that when it gets either of the messages, it checks where the
mouse is independent of the VCL.
| Quote: | ( My code after the changings is bellow )
void __fastcall TForm1::Timer1Timer( TObject *Sender )
{
Timer1->Enabled = false;
TBaseButton *Button1 = new TBaseButton( this );
|
Why are you using a Timer to allocate the button? Timers are a
limited system resource (16 of them if I remember correctly)
that should be avoided when ever possible because if other
applications have used up the resource, your program won't run
and you'll get an Out Of Resource error.
~ JD
|
|
| Back to top |
|
 |
JD Guest
|
Posted: Wed Jun 30, 2004 7:35 pm Post subject: Re: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
"Ramy" <Ramy (AT) NoMail (DOT) com> wrote:
| Quote: |
__fastcall TBaseButton::~TBaseButton()
{
delete ButtonImage;
}
|
In reality, you don't need that there. You assigned the button's
owner as the form when you allocated it and since you want the
button to be there as long as the form is there, you *could*
just let the native behavior handle it's destruction.
To be clear, when an object is destroyed, all of it's owned
objects are also destroyed in a nice orderly fashion.
~ JD
|
|
| Back to top |
|
 |
JD Guest
|
Posted: Wed Jun 30, 2004 7:43 pm Post subject: Re: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
"Ramy" <Ramy (AT) NoMail (DOT) com> wrote:
| Quote: | [...] don't have to be so angry,
|
That wasn't anger. It was more like when you do something
stupid and your friends just won't let you forget it. <vbg>
| Quote: | i'm reading the newsgroup directly from Borland's pages [...]
|
Me too. I hate readers. They just don't give me enough room.
What you can do is click on any link in the thread and then
click on the Related button. That's what I do.
~ JD
|
|
| Back to top |
|
 |
Ramy Guest
|
Posted: Thu Jul 01, 2004 6:16 am Post subject: Re: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
| Quote: | That wasn't anger. It was more like when you do something
stupid and your friends just won't let you forget it.
|
:-D
| Quote: | What you can do is click on any link in the thread and then
click on the Related button. That's what I do.
|
Thanks that's was a good tip!
Ramy
|
|
| Back to top |
|
 |
Ramy Guest
|
Posted: Thu Jul 01, 2004 6:24 am Post subject: Re: Problems with OnClick, OnMouseEnter & OnMouseLeave |
|
|
Thanks, basicaly i know this rule, i don't know why i did that,
but tell me, why in your example did you wrote -
[...]
ButtonCanvas = new TCanvas();
[...]
and than -
__fastcall TBaseButton::~TBaseButton()
{
delete ButtonCanvas;
[...]
}
?
Why didn't you gave the ButtonCanvas an owner so that when the
button is destroyed so is the ButtonCanvas? why did you have to
destroyed it manualy in your Destructor?
Ramy
| Quote: | In reality, you don't need that there. You assigned the button's
owner as the form when you allocated it and since you want the
button to be there as long as the form is there, you *could*
just let the native behavior handle it's destruction.
To be clear, when an object is destroyed, all of it's owned
objects are also destroyed in a nice orderly fashion.
~ JD
|
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|