Solved

TSpeedButtons

Posted on 2000-04-28
34
399 Views
Last Modified: 2010-04-04
Hello,
  Here is my problem : i wrote an component based on tPanel, and using 8 TSpeedbuttons and when the mouse leave quickly any of the SpeedButtons, this one stay with the border drawn, it doesn't returns to "flat state".
As it is well known that a good experience is better than an long discussion : Full source code (8 Ko) available for who wants to help me.


Thanks for al ideas.
dsk@pt.lu
          HelpMe
0
Comment
Question by:helpme020897
  • 11
  • 6
  • 6
  • +4
34 Comments
 
LVL 10

Expert Comment

by:Lischke
ID: 2759678
Hi helpme,

this is a well known problem and has to do with the limited mouse leave detection support by Windows. For NT there's an API to register a window to check it for a leaving mouse cursor, but this is not available for Win9x. The only reliable solution I found is to use a timer. Set it up to check, say, every 300ms the window over which the mouse is hovering and if it is no longer the one from the last test then invalidate the last one to get it updated.

Ciao, Mike
0
 

Author Comment

by:helpme020897
ID: 2759779
Hi Lischke,
Thanks for your intervantion,
   But why If I put a TPanel on a form and then i put several TSpeedButons on th eTPanel, I do not have the problem ?
  If i understand what you said, the problem sould occurs.

HelpME
0
 

Author Comment

by:helpme020897
ID: 2759886
re-hi Lischke,
  I tryed what you tell me, but it does'nt works. It seems that TSpeedButtons "repaint", "invalidate" or update methods doesn't the test if mouse is over or not, so the control is effectively re-drawn but keep its state.
So it is drawn as if the mouse where still over it.

   HelpMe
0
 
LVL 10

Expert Comment

by:Lischke
ID: 2760360
Mmmh, what can we do then?

It's a fact that the leave detection is not reliable because it is based on mouse moves over a VCL window. If you move the mouse fast enough away to a place not covered by a your Delphi application then the VCL will not get the opportunity to determine that the mouse left. You can see that in the VCL code (see also Forms.pas line 6899 ff.). The VCL internal messages CM_MOUSEENTER and CM_MOUSELEAVE are exclusive created in this code and only really used by TSpeedButton (and only if it is flat style).

In order to execute the DoMouseIdle code the application needs to get any message which triggers TApplication.Idle method, which in turn triggers the leave detection. If the app does not get messages (usually by moving the mouse, but also a paint or timer event would be okay) then the leave detection is never executed.

To precise what I said about resetting the left button in an timer event: Just send the button a dummy mouse leave message (Button1.Perform(CM_MOUSELEAVE, 0, 0);) and it should reset its state correctly.

Ciao, Mike
0
 
LVL 3

Expert Comment

by:shenqw
ID: 2760374
hi. helpme

  It's not bug for delphi. ok,see the delphi source code for reason.

The speedbutton isn't a Twincontrol, he is TGraphicControl,No Message
he can receive.so his parent control must is TWincontorl. The Parent
receive the message and find the current pos control,if current pos
control is not nil, then the parent contorl send the message to the
TGraphicContorl. Your can see Source code at Contols.pas

function TWinControl.IsControlMouseMsg(var Message: TWMMouse): Boolean;

The speedbutton state will change only receive CM_MOUSELEAVE & CM_MOUSEENTER
self-defined messages.Trigger the two message source code as

function TApplication.DoMouseIdle: TControl;
var
  CaptureControl: TControl;
  P: TPoint;
begin
  GetCursorPos(P);
  Result := FindDragTarget(P, True);
  if (Result <> nil) and (csDesigning in Result.ComponentState) then
    Result := nil;
  CaptureControl := GetCaptureControl;
  if FMouseControl <> Result then
  begin
    if ((FMouseControl <> nil) and (CaptureControl = nil)) or
      ((CaptureControl <> nil) and (FMouseControl = CaptureControl)) then
      FMouseControl.Perform(CM_MOUSELEAVE, 0, 0);
    FMouseControl := Result;
    if ((FMouseControl <> nil) and (CaptureControl = nil)) or
      ((CaptureControl <> nil) and (FMouseControl = CaptureControl)) then
      FMouseControl.Perform(CM_MOUSEENTER, 0, 0);
  end;
end;


so if you move mouse quickly and mouse leave the application's any window, the DoMouseIdle
will not execute. then the cm_mouseleve or cm_mouseenter will not send to the contorl.
but when you mouse enter any window of the application, the speedbutton will become ok.
we can make test on that;

drag a speedbutton into your form.and make the speedbutton left=-1,then run your pragram.
you will find that how slowly and how quickly you move your mouse leave the contorl and
also leave the form, the contorl's state is still up. but when your mouse enter in the
application, from anywhere,the speedbutton's state is ok. another test : when your mouse
leave you form.

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if not PtInRect(GetClientRect,Mouse.CursorPos) then
  PostMessage(handle,wm_mousemove,0,0);
end;


a second later(Timer.Internal:=1000),the speedbutton's state is ok.   That's why,I think.

If you want avoid that , use a timer to check the mouse currpos, if the mouse not in the
application, the update you speedbutton state(by send a message , like wm_mosuemove,
cm_mosueexit etc.)

Good Luck
shenqw
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 2760504
Hi, why don't you design your component using a frame? Once you have created a frame with the SpeedButtons you can add the frame to the palette as a template. Now you can drop it on a form like any other component.
And the problem apears to be gone...


Regards,

Epsylon.
0
 
LVL 2

Expert Comment

by:k6__
ID: 2760830
The only Solution to your problem is
to buy (very very cheap! only $20) and
use the TToolbarButton97!!! it's the
best without any mouseover problems =)

the address:
http://www.jordanr.dhs.org/

This problem is history for me =)
0
 
LVL 2

Expert Comment

by:k6__
ID: 2760868
Oh i forgot to mention that TToolbarbutton97
is included in TTOOLBAR97! so you must
download TToolbar97 and install the
package in order to get TToolbarbutton97.
Now inside your component replace
all the "TSpeedButton" with "TToolbarButton97" and include in
your unit "USES" the "TBCTRLS" unit in
order your component to get support
of the TToolbarButton97 component.

That's all



0
 
LVL 10

Expert Comment

by:Lischke
ID: 2761803
shenqw, not a very nice plagiat :-(

Epsilon, even with frames the basic problem still persists as the mouse leave detection is still the same.

k6__, most of the people here know me as a very friendly and peaceful man, but sometimes I become really annoyed. Not only that your suggestion is totally senseless as we are talking here about fixing a problem in a control of the questioneer, not to replace the own work with something other which is likely totally different. No, you also proposed an answer, which is VERY UNFRIENDLY because you are the last one here and other people already discussed here.

Please, propose comments only! If it is the solution you will still earn well deserved points. Otherwise it is like everybody stands politely in a line while you jump the queue.

Ciao, Mike
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 2761879
Mike, I can't reproduce the problem so I must be missing something...
0
 
LVL 10

Expert Comment

by:Lischke
ID: 2762332
Have you also tried to move the mouse quickly so that it leaves your application entirely?
0
 
LVL 2

Expert Comment

by:k6__
ID: 2762633
I'm sorry Lischke =( .. i'll post
comments next time...

Anyway.. because from Delphi 3 Speedbutton
has problems the only solution is to
gain a third party component... i have
tried many ways to pass this problem
in the past but none worked.. espesially
try to execute a loop with
application.processmessage inside and
pass the mouse over a TSpeedbutton..
The button will remain "UP" until the
loop finish... This doesn't happend with
Jordan's TToolbarButton97 =)
0
 
LVL 10

Expert Comment

by:Lischke
ID: 2763443
k6__, I'm sorry if I sounded to harsh, but what you did happens regularly and other circumstances made me crazy for a moment.

I hope we still can make up a good team here...

Ciao, Mike
0
 
LVL 20

Expert Comment

by:Madshi
ID: 2765435
Hi friends, Eps, did you test it under win9x or winNT? Because this problem only occurs under win9x...

I've solved this problem in my own speed button component with a timer, too. That works perfectly. BTW, Jordan Russell's Toolbar97 components are shareware, but (if I remember correctly) you can download the sources without registering - and if you only want to look at how he solved this problem (by using a timer again), you are allowed to do so I think.

Hi, k6__, how about withdrawing your answer?   :-)

Regards, Madshi.
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 2767100
Hi everyone, I can't remember it for sure anymore but there's a good chance I tried it on NT   :o)
0
 

Author Comment

by:helpme020897
ID: 2768451
Sorry K6, but i would like to be abble to resolve this probleme with standards components. I will never use any solution like this because if i give my component to a friend, he will have to purchase the TtoolBarButton97 and this is not my own goal.


   Thanks anyway


                  HelpMe
0
 

Author Comment

by:helpme020897
ID: 2768486
Hi all, and thanks everybody.
Here more informations :
1) even when the mouse leaves the Form, the button(s) doesn't refresh and stay in "up" state.
2) I draw myself the différents glyphs, and if there are no glyphs loaded, there is no problems. I use canvas.draw in order to draw those glyph, isn't it correct  ?

Remember : just ask me i you want the source code to have a look on this, and do what you want with it, I just wants this TDBnav works correctly in my app.

HelpME
dsk@pt.lu

0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 10

Expert Comment

by:Lischke
ID: 2768489
HelpMe, have you then tried to combine the timer approach with the Perform call I suggested above?

Ciao, Mike
0
 
LVL 10

Accepted Solution

by:
Lischke earned 500 total points
ID: 2768540
HelpMe,

To 1) In particular when the mouse leaves the form then the problem appears. As long as you move the mouse over any VCL window the mouse enter and leave detection works.

To 2) I assume drawing the glyphs changes the overall timing a bit so that it seems to work without glyphs. But this has nothing to do with the leave detection.

My 3) It seems you don't believe what I explain all the time. Perhaps a short summary convinces you:

a) Mouse enter and leave detection in Delphi programs is entirely (!) done by the VCL.
b) This detection is called after a message has been process, so if there are no messages (e.g. because the mouse is not over a VCL window) then this mechanism cannot work.
c) The up/down state of flat speed buttons is based on this enter/leave detection. This does not work reliably, so the buttons might not reflect correctly the mouse position.


Point c) is not very important, though, because you need some special action to create the bad effect. If it is still an issue then use then read ID Q183107 of MSDN of which I have cited the summary below:

SUMMARY
Microsoft Windows NT 4.0 and Microsoft Windows 98 provide the TrackMouseEvent API to notify a window that the mouse has left the window. Windows 95 does not provide any such notification. An application can detect when the mouse exits a window by starting a timer when the mouse enters the window, and using that timer to monitor the mouse position and find when it is no longer within the window.
---- end quote

Ciao, Mike
0
 
LVL 2

Expert Comment

by:mullet_attack
ID: 2773572
Hi everyone !

I've never noticed this problem before, so thanks all for making me aware. I read all before this, and agree fully with why it occurs. Using a timer to solve the problem seemed a simple solution, I can't figure why helpme doesn't do that. Anyway, for my own fun I did it. Here's my results. It seems to work. (uses standard components :-))

unit SpeedyButton;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Buttons, Extctrls;

type
  TSpeedyButton = class(TSpeedButton)
  private
    BtnTimer : TTimer;
    { Private declarations }
  protected
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure OnBtnTimer(Sender : TObject);
    { Protected declarations }
  public
    constructor Create(AOwner : TComponent) ; override;
    destructor Destroy ; override;
    { Public declarations }
  published
    { Published declarations }
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Planetech', [TSpeedyButton]);
end;

{ TSpeedyButton }

constructor TSpeedyButton.Create(AOwner: TComponent);
begin
  inherited;
  btnTimer := TTimer.create(owner);
  with btnTimer do
    begin
      interval := 100;
      OnTimer := OnBtnTimer;
    end;
end;

destructor TSpeedyButton.Destroy;
begin
  btnTimer.enabled := false;
  btnTimer.free;
  inherited;

end;

procedure TSpeedyButton.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  btnTimer.enabled := true;
  inherited;
end;

procedure TSpeedyButton.OnBtnTimer(Sender: TObject);
begin
  if Not PtInRect(ClientRect,ScreenToClient(Mouse.CursorPos)) then
    begin
      btnTimer.enabled := false;
      Perform(CM_MOUSELEAVE, 0, 0);
    end;
end;

end.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 2773740
Hi mullett_attack, that looks alright. Just one little addition: When creating a TTimer object, Enabled is set to true by default. So you should do this:

  with btnTimer do
    begin
      Enabled := false;
      interval := 100;
      OnTimer := OnBtnTimer;
    end;

Regards, Madshi.

P.S: In my components I've improved mouse-out-detection with this idea: In the MouseMove event I'm looking through all other speedButtons on the form. If one of them is still in MouseOver-state (though the mouse has already left it), I'm performing MOUSELEAVE, too. This way the react time is even better, and the timer is only the very last resort.
0
 
LVL 2

Expert Comment

by:mullet_attack
ID: 2773848
Madshi,
good comment, I meant to write enabled := false, but you know what happens when in a hurry :-)

I like your idea. If you have already done this, why not answer the Q and get the points? 500 is a lot !
0
 
LVL 10

Expert Comment

by:Lischke
ID: 2773910
Because I have already answered the question only HelpMe is not convinced yet.

Ciao, Mike
0
 
LVL 2

Expert Comment

by:mullet_attack
ID: 2775768
Couldn't agree more. Reading it all again, this question has been answered 3 or 4 times already.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 2776322
:-)  And I can't post my components sources here, because they need some units which are owned by my firm. Besides, the sources are much too long (the speedButtons have a lot of extra functionality).
0
 
LVL 2

Expert Comment

by:mullet_attack
ID: 2776373
Madshi, I know the problem. I am a contract programmer, so when I write code for clients, I have in my contract that components that don't have their secrets in them I can keep also. Usually little stuff, but handy...
0
 

Author Comment

by:helpme020897
ID: 2781287
Mullet_Attack : all right !  Now i understand what Lischke means with timers ! I have to create a component from TSpeedButon class...  I didn't understood this.

So , now my problem is solved... but i hazve anither problem : Who earned the points ? Lischke with the earliest correct answerd i didn' understood or Mullet_Attack with later answer but with code source ?

Listening.

Thanks all of you.


       HelpME
0
 
LVL 10

Expert Comment

by:Lischke
ID: 2781883
Hi helpme,

usually the first one with the correct answer gets the points, but this depends of course on the answer. If you expected to get source code and prefer mullet_attacks contribution then you can of course accept one comment from him/her as answer. But think about it and decide what's more important: the fish or to learn fishing. I gave you the latter...

Btw: Next time when you have problems to understand then you should ask more, so I know my words are not good enough. There's no need for shame if one does not know a particular fact. It happens to me all the time :-)

Ciao, Mike
0
 
LVL 2

Expert Comment

by:mullet_attack
ID: 2783085
Mike, I agree with your "fish or learn fishing" comment. I too usually like to give answers that cause the asker to learn something. However in this case your answer was first and correct because the original question asked 'why?'. If the Q was 'can someone give me code to do blah' then my answer was better (although you would have given code in that case I'm sure). The problem here is do we 'experts' just answer the question, or do we solve the problem? For some-one with good Delphi skill your answer would be sufficient, as they would understand to sub-class the existing button. (no offense, helpme :-)) I am happy if helpme reads my code, _fully_ understands it and realises your answer was right. Mike, points I don't care too much about, you have them. One day maybe the opposite will occur. :-)
0
 
LVL 10

Expert Comment

by:Lischke
ID: 2783478
Well spoken...
0
 
LVL 2

Expert Comment

by:mullet_attack
ID: 2783518
BTW, mullet_attack is 'him' :-)

Mark
0
 

Author Comment

by:helpme020897
ID: 2787317
Okay, points are for Lischke, and i do applause Mullet_Attack's fair play.  :-)
0
 
LVL 3

Expert Comment

by:shenqw
ID: 2787482
hi Lischke:

>shenqw, not a very nice plagiat :-(

Your Time:Saturday, April 29 2000 - 02:18AM CST
My Time:Saturday, April 29 2000 - 02:21AM CST

Do you think i can write so long in only 3 minutes???I had used half an hour to wirte that because My english is very poor.
0
 
LVL 10

Expert Comment

by:Lischke
ID: 2787547
Thank you helpme.

shenqw, I'm really sorry if I offended you and claimed something untrue. The posting times here at E-E are not reliable at all. I even had situations where I posted two comments related to another one and both still apeared before the comment they commented :-)

Please accept my apologies.

Ciao, Mike
0

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now