programmerdelphi2k 237 Posted December 21, 2022 (edited) hello girls and boys, RAD 11.2 Alexandria FMX project / 32bits Button1 is on Form1 (directly); Button1.HitTest = false; I'm having a little problem with something that would, shall we say, not be a problem... I need to get control over the mouse cursor to perform some extra tasks. However, I'm not understanding why the result is always "nil" for the "ObjectAtPoint" function. According to the code below, I believe that it should have the intended return, that is, the control "Button1" (IControl representing it)... however, the function is returning "nil"... and, how can be seen on the printscreen, everything goes well until the line number "101", after it, the "result" is again used to check if "ITSELF" has an "object (children... but does not )" in the given coordinates... this part seems strange to me, however, the code is from Embarcadero... so there was even more doubt! Quote function TCommonCustomForm.ObjectAtPoint(AScreenPoint: TPointF): IControl; // FMX.Forms.pas on RAD 11.2 var I: Integer; begin for I := ChildrenCount - 1 downto 0 do if Supports(Children, IControl, Result) and Result.GetVisible then begin Result := Result.ObjectAtPoint(AScreenPoint); if Result <> nil then Exit; end; Result := nil; end; So, if anyone can say something that helps me, I thank you in advance. here my code for tests... type TForm1 = class(TForm) Button1: TButton; // <---- my control into form, "Button1.HitTest := false" ... procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: single); var LPointF: TPointF; LIControl: IControl; begin LPointF := TPointF.Create(X, Y); // LIControl := ObjectAtPoint(LPointF); = it's nil // or LIControl := ObjectAtPoint(ClientToScreen(LPointF)); = it's nil ... end; NOTE: on screenshot is "TEDIT" but in fact is "TButton"... ok == I pasted old printscreen ! SORRY! Edited December 21, 2022 by programmerdelphi2k Share this post Link to post
ULIK 16 Posted December 22, 2022 The problem is: Button1.HitTest := False. This makes it invisible for ObjectAtPoint. See end of 'TControl.ObjectAtPoint' code 1 Share this post Link to post
ULIK 16 Posted December 22, 2022 What do you mean by nope!? Definitely HitTest = False is the reason, why ObjectAtPoint will not recognize the button. You had to step through all the inherited calls down to TControl. ObjectAtPoint. Here the line if PointInObject(LP.X, LP.Y) and CheckHitTest(HitTest) then is the critical part. The first condition is true if over a button, but the second one makes it always fail. Btw: you had to call ObjectAtPoint with Screen coordinates: LIControl := ObjectAtPoint(ClientToScreen(LPointF)); Share this post Link to post
programmerdelphi2k 237 Posted December 22, 2022 I can not "have" HitTest defined, at all! I need pass over it Share this post Link to post
ULIK 16 Posted December 23, 2022 Then you can't use ObjectAtPoint . You will have to write your own version of it (not that hard, just iterate over all the children and see if position is inside and if it is visible. If so, do the testing on that found control again to get the inner controls of it (if any). Share this post Link to post
programmerdelphi2k 237 Posted December 27, 2022 (edited) Now I can find "control" under mouse cursor .... of course, needs verify if "AControl" is valid / not nil , etc..... type TMyHack = class(TControl); // to avoid hack the TControl class.... just copy this function and changes last line to "dont verify HitTest property" function MyObjectAtPoint(const AControl: TControl; AScreenPoint: TPointF): IControl; var // I : Integer; // NewObj : IControl; // Control: TControl; LP: TPointF; begin if not AControl.ShouldTestMouseHits then Exit(nil); // LP := AScreenPoint; // if AControl.Scene <> nil then LP := AControl.Scene.ScreenToLocal(LP); // if (AControl.ClipChildren or TMyHack(AControl).SmallSizeControl) and not AControl.PointInObject(LP.X, LP.Y) then Exit(nil); // (* I dont need it, in my usage! I just want first layer!!! if AControl.ControlsCount > 0 then for I := TMyHack(AControl).GetLastVisibleObjectIndex - 1 downto TMyHack(AControl).GetFirstVisibleObjectIndex do begin Control := TMyHack(AControl).Controls[I]; if not TMyHack(Control).GetVisible then Continue; // // pay attention if needs look at "into controls...", then, I should use "MyObjectAtPoint(...') recursivelly" // not in "TMyHack(Control).ObjectAtPoint(AScreenPoint);" NewObj := TMyHack(Control).ObjectAtPoint(AScreenPoint); // if NewObj <> nil then Exit(NewObj); end; *) result := nil; if TMyHack(AControl).PointInObject(LP.X, LP.Y) then // and TMyHack(AControl).CheckHitTest(AControl.HitTest) then result := AControl; end; procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single); var LControl : IControl; LControlAtPoint: IControl; LMousePos : TPointF; begin LMousePos := Screen.MousePos; Memo1.Text := Format('%s -> Mouse cursor at: %f x %f', [TimeToStr(now), LMousePos.X, LMousePos.Y]); // LControl := Self.ObjectAtPoint(LMousePos); // if (LControl <> nil) then Memo1.Lines.Add('LControl = ' + TControl(LControl).ToString) // else // trying find TControls (TButton in this case) with "HitTest = false or true" begin for var I: Integer := 0 to (Self.ChildrenCount - 1) do begin if not(Self.Children[I] is TButton) then Continue; // if Supports(Children[I], IControl, LControl) and (LControl <> nil) then begin LControlAtPoint := MyObjectAtPoint(TControl(LControl), LMousePos); // if (LControlAtPoint <> nil) then Memo1.Lines.Add('Control at Point = ' + TControl(LControlAtPoint).ToString); end; end; end; end; Edited December 27, 2022 by programmerdelphi2k Share this post Link to post