Mouse - fazendo um hint que segue o mouse

Top  Previous  Next

How to show a hint that follows the mouse and that hides properly... This seems to be the problem of the month. I can see that I'm not the only one who had that problem. What follows here is more an extension to Michael Burton and Mike Tumy's article (Real Time Mouse Coordinates Display) because Mott Adler (Using OnHint for Real-Time Mouse Coordinates) provides a nice work around that has the strength of simplicity. Those two solutions share the same problem : when you have other controls on the form the hint is behaving strangely. Either it is waiting for the cursor to leave the component or it is immediately showing the component hint. The problem is not here, I think that you noticed the fact and that it was not the point in your article. 

 

Ok, we can show a hint that follows the mouse. But we have two problems. The first and most important is to hide the hint when the cursor is leaving the form. The second is to hide the hint when the cursor is over a component and act normal again. I don't know if we can handle the first problem with windows 95 or NT 3.5 or less. But windows 98 and NT 4 or greater bring to us a new function called TrackMouseEvent. This is the only way I can see for us to know each time that the cursor left the form. 

 

Answer:

 

 

This is how to use it : 

 

evtTrack: tagTRACKMOUSEEVENT; 

with evtTrack do 

  begin 

    cbSize := sizeOf(tagTRACKMOUSEEVENT); 

    dwFlags := TME_LEAVE; 

    hwndTrack := form1.Handle; 

    dwHoverTime := HOVER_DEFAULT; 

  end; 

if not isTracking then 

  begin 

    if not TrackMouseEvent(evtTrack) then 

       MessageDlg('Sorry, I' + #39 + 'not in the mood of tracking

         + #13 + #10 + 'your stupid events.', mtError, [mbOK], 0); 

    isTracking := True; 

  end; 

 

The TME_LEAVE flag asks windows to post us WM_MOUSELEAVE message. We keep track with the isTracking := True that we have already asked windows. I don't know if it matters. So next time the cursor leaves the form we get the message. We handle it with the following proc : 

private 

  { Private declarations } 

  procedure MouseLeavesTheForm(var m: TMessage); message WM_MOUSELEAVE; 

procedure TForm1.MouseLeavesTheForm(var m: TMessage); 

begin 

  inherited; 

  hwHint.ReleaseHandle; //we release the hint here 

  isTracking := False; 

  m.Result := 1

end

 

Sorry, but I don't know if I have to call inherited and if I shall tell with m.Result := 1 that I handled the message. Here we are. The first problem is solved. 

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); 

var 

  pt: TPoint; 

  sText: string; 

begin 

  if not isTracking then 

    begin 

      if not TrackMouseEvent(evtTrack) then 

        MessageDlg('Sorry, I' + #39 + 'not in the mood of tracking

              + #13 + #10 + 'your stupid events.', mtError, [mbOK], 0); 

        isTracking := True; 

    end; 

  getCursorPos(pt); 

  sText := Format('%d,%d', [pt.x, pt.y]); 

  hwHint.ActivateHint(Rect(pt.x + 10, pt.y + 10, pt.x + hwHint.Canvas.TextWidth(sText) + 19, 

       pt.y + hwHint.Canvas.TextHeight(sText) + 10), sText); 

  UpdateTracking; 

end; 

You may have noticed the UpdateTracking procedure ? This is something I snipped from the method used is speedbuttons to look if the cursor is over the speedbutton. This will help us to release the hint whenever the mouse is not over the nude background but still inside of the form. 

//check if the cursor is no longer over the background 

//e.g.. he is over a button or something 

procedure TForm1.UpdateTracking; 

var 

  P: TPoint; 

begin 

  GetCursorPos(P); 

  if not (FindDragTarget(P, True) = Self) then 

      Perform(WM_MOUSELEAVE, 0, 0); 

end; 

 

When the cursor leaves the background we post us a WM_MOUSELEAVE message, the speedbutton is using a CM_MOUSELEAVE. That way the hint is working properly over the other controls. 

Unfortunately the MSDN help file says : not for windows 95. This is sad because many of you are still using it I suppose. The work around is to use a TTimer, but this is not very clean. Another solution would be to use the SetCapture function to capture mouse events even when the mouse is not over the form.