Recent

Author Topic: ControlAtPos Behavior  (Read 872 times)

simsee

  • Full Member
  • ***
  • Posts: 184
ControlAtPos Behavior
« on: June 04, 2023, 11:48:03 am »
In a form, I'm trying to detect the control under mouse cursor with the following code;

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
  2. var
  3.   Control : TControl;
  4. begin
  5.   Control:=ControlAtPos(Point(X,Y),[capfRecursive, capfAllowWinControls]);
  6.   if Assigned(Control) then
  7.     Caption:=Control.Name
  8.   else
  9.     Caption:=X.ToString+':'+Y.ToString;
  10. end;

Unfortunately it doesn't work. Do you have any suggestions? Thank you.

Josh

  • Hero Member
  • *****
  • Posts: 1271
Re: ControlAtPos Behavior
« Reply #1 on: June 04, 2023, 12:12:21 pm »
formmousemove does not fire all the time, only when mouse is on the form canvas, ie it will not fire when over a tcontrol or twincontrol.

your best bet is use a timer that does somethig like this

Not tested so double check.

Code: Pascal  [Select][+][-]
  1. var cntrl:TControl;
  2.     CntrlPT:TPoint;
  3. begin
  4.   CntrlPT:=screentoclient(mouse.CursorPos); // this holds the co-ordinates of the cntrl, in relation to parent.
  5.   cntrl:= FindLCLControl(mouse.CursorPos);
  6.   if assigned(cntrl) then caption:=cntrl.name
  7.  
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: ControlAtPos Behavior
« Reply #2 on: June 04, 2023, 12:49:57 pm »
Drop a Tapplication on the form. Located on second tab.

use the OnUserInput.

mouse move code number is 512.. test it using this code

Code: Pascal  [Select][+][-]
  1. procedure TForm1.ApplicationProperties1UserInput(Sender: TObject; Msg: Cardinal
  2.   );
  3. begin
  4.   caption := Msg.ToString;
  5. end;                          
  6.  
The only true wisdom is knowing you know nothing

simsee

  • Full Member
  • ***
  • Posts: 184
Re: ControlAtPos Behavior
« Reply #3 on: June 04, 2023, 12:57:19 pm »
Thanks for the great suggestions. Another question: When there are controls whose surface overlaps, how can I detect them all?

wp

  • Hero Member
  • *****
  • Posts: 11855
Re: ControlAtPos Behavior
« Reply #4 on: June 04, 2023, 01:07:36 pm »
When there are controls whose surface overlaps, how can I detect them all?
Iterate over all controls on the form and check the mouse position. Untested:
Code: Pascal  [Select][+][-]
  1. uses
  2.   LCLIntf;
  3.  
  4. procedure TForm1.FindControlsUnderMouse(List: TFPList);
  5. var
  6.   i: Integer;
  7.   c: TControl;
  8.   P: TPoint;
  9. begin
  10.   for i := 0 to ComponentCount-1 do
  11.   begin
  12.     if Components[i] is TControl then
  13.     begin
  14.       c := TControl(Components[i]);
  15.       P := c.ScreenToClient(Mouse.CursorPos);
  16.       if PtInRect(Rect(0, 0, c.Width, c.Height), P) then
  17.         List.Add(c);
  18.     end;
  19.   end;
  20. end;
   

Zoran

  • Hero Member
  • *****
  • Posts: 1829
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: ControlAtPos Behavior
« Reply #5 on: June 04, 2023, 01:08:38 pm »
I think that the cleanest way is to add your UserInput event in Application object, when the forms gets activated.
Try the attached application and see how it is done!

Zoran

  • Hero Member
  • *****
  • Posts: 1829
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: ControlAtPos Behavior
« Reply #6 on: June 04, 2023, 02:02:45 pm »
When there are controls whose surface overlaps, how can I detect them all?
Iterate over all controls on the form and check the mouse position. Untested:
Code: Pascal  [Select][+][-]
  1. uses
  2.   LCLIntf;
  3.  
  4. procedure TForm1.FindControlsUnderMouse(List: TFPList);
  5. var
  6.   i: Integer;
  7.   c: TControl;
  8.   P: TPoint;
  9. begin
  10.   for i := 0 to ComponentCount-1 do
  11.   begin
  12.     if Components[i] is TControl then
  13.     begin
  14.       c := TControl(Components[i]);
  15.       P := c.ScreenToClient(Mouse.CursorPos);
  16.       if PtInRect(Rect(0, 0, c.Width, c.Height), P) then
  17.         List.Add(c);
  18.     end;
  19.   end;
  20. end;
   

WP, this checks only Form's direct children. This is your example, updated to check all (direct and indirect) children controls:
Code: Pascal  [Select][+][-]
  1. procedure TForm2.ListAllControlsUnderMouse(List: TFPList);
  2.  
  3.   procedure DoListAllControlsUnderMouse(WC: TWinControl);
  4.   var
  5.     I: Integer;
  6.     C: TControl;
  7.     P: TPoint;
  8.   begin
  9.     for I := 0 to WC.ControlCount - 1 do begin
  10.       if WC.Controls[I] is TControl then begin
  11.         C := TControl(WC.Controls[I]);
  12.         if Assigned(C.Parent) then
  13.           P := C.Parent.ScreenToClient(Mouse.CursorPos)
  14.         else
  15.           P := WC.ScreenToClient(Mouse.CursorPos);
  16.         if PtInRect(C.BoundsRect, P) then begin
  17.           List.Add(C);
  18.           if C is TWinControl then
  19.             DoListAllControlsUnderMouse(TWinControl(C));
  20.         end;
  21.       end;
  22.     end;
  23.   end;
  24.  
  25. begin
  26.   DoListAllControlsUnderMouse(Self);
  27. end;
  28.  

I am attaching the same application I previously uploaded, but now included listing of all (overlapped) controls on mouse position. Try!

wp

  • Hero Member
  • *****
  • Posts: 11855
Re: ControlAtPos Behavior
« Reply #7 on: June 04, 2023, 03:23:56 pm »
WP, this checks only Form's direct children.
No, it checks all components owned by the form, and this usually includes all controls sitting directly or indirectly on the form (I do agree that there may be some rare crazy cases where somebody might make the form own a control parented by another form...)

Zoran

  • Hero Member
  • *****
  • Posts: 1829
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: ControlAtPos Behavior
« Reply #8 on: June 04, 2023, 04:24:32 pm »
WP, this checks only Form's direct children.
No, it checks all components owned by the form, and this usually includes all controls sitting directly or indirectly on the form (I do agree that there may be some rare crazy cases where somebody might make the form own a control parented by another form...)

I see, I though you used Components property (instead of Controls) by accident, sorry.

You are right, although my version is still cleaner -- at least for these "rare crazy cases" -- actually, although I don't think I would ever have controls created by another form, it is not rare that I (when creating controls in code) create some controls with nil owner (and take care myself of destroying these correctly it in code), or put a panel or some other control as the owner.

I also did one thing wrong -- when converting your procedure, I replaced Components with Controls, but kept the line
Code: [Select]
if WC.Controls[I] is TControl then, which is now completely unnecessary, although it makes no harm. I really don't like this -- looks like overdefensive programming style, which is just a bad style.
« Last Edit: June 04, 2023, 04:26:20 pm by Zoran »

 

TinyPortal © 2005-2018