How to move a TPanel controlled by mouse at runtime? - delphi

I want to move a TPanel on another TPanel by mouse at runtime with Delphi 10.4.2 and FMX. I tried OnMouseDown, OnMouseMove and OnMouseUp events. But it is not clear what the contents of X and Y values are in the events. The documentation says that they are screen coordinates. Relative to the screen, form, parent control or the control itself? How can I solve the movement of the TPanel?

The documentation says that they are screen coordinates.
No, it doesn't. The FMX.Types.TMouseEvent and FMX.Types.TMouseMoveEvent
documentation both say:
X and Y--the pixel coordinates of the mouse pointer within the client area of the control.
How can I solve the movement of the TPanel?
Like this:
var
LastPt: TPointF;
Dragging: Boolean = False;
procedure TMyForm.PanelToDragMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
if Button = TMouseButton.mbLeft then
begin
LastPt := TPointF.Create(X, Y);
Dragging := True;
end;
end;
procedure TMyForm.PanelToDragMouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
if Button = TMouseButton.mbLeft then
Dragging := False;
end;
procedure TMyForm.PanelToDragMouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Single);
var
CurrPt: TPointF;
begin
if Dragging then
begin
CurrPt := TPointF.Create(X, Y);
PanelToDrag.Position.Point := PanelToDrag.Position.Point + (CurrPt - LastPt);
LastPt := CurrPt;
end;
end;
Basically, while the mouse is moving around the Panel, the code is simply calculating the offset the mouse has moved from the last known position to the current position, and then applying that offset to the Panel's current Position within its Parent.

Related

How to refresh a BalloonHint that is part of ControlList.CustomHint on MouseOver

Example without BalloonHint1 works as designed. No issues with the Hint refresh.
procedure TForm1.ControlList1MouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
ControlList1.ShowHint:=false;
//ControlList1.CustomHint <-------value is not set as it is not required.
ControlList1.Hint := IntToStr(ControlList1.HotItemIndex);
ControlList1.ShowHint:=true;
end;
When I add a TBalloonHint, the BalloonHint does not display properly.
procedure TForm1.ControlList1MouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
ControlList1.ShowHint:=false;
BalloonHint1.Delay:=0;
BalloonHint1.HideAfter:=-1;
ControlList1.CustomHint:=BalloonHint1;
ControlList1.Hint := IntToStr(ControlList1.HotItemIndex);
ControlList1.ShowHint:=true;
end;
When I move my mouse over the ControlList for the first time. A BalloonHint does not show.
If I move my mouse over again (for the 2nd time) then the HotItemIndex from the previous movement shows the index.
Is there a way to do a BalloonHint1.Refresh?
I have tested some of the following:
Application.CancelHint; ///something that I dont want to do... but i gave it a try
also
ControlList1.ShowHint:=false;
ControlList1.ShowHint:=true;
The following worked.
procedure TForm1.ControlList1MouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
BalloonHint1.HideHint;
BalloonHint1.Delay:=0;
BalloonHint1.HideAfter:=-1;
ControlList1.CustomHint:=BalloonHint1;
ControlList1.Hint := IntToStr(ControlList1.HotItemIndex);
BalloonHint1.ShowHint(ControlList1);
end;
I then found that the BalloonHint flickered. So I used advice from the following:
Delphi ListView hint flickers
Create a global variable in which I would store reference to last HotItemIndex for which the hint has been shown. Then verify if the current HotItemIndex is the same as the one we stored controlListHotItemIndex.
procedure TForm1.ControlList1MouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
if controlListHotItemIndex<>ControlList1.HotItemIndex then
begin
controlListHotItemIndex:=ControlList1.HotItemIndex;
BalloonHint1.HideHint;
BalloonHint1.Delay:=0;
BalloonHint1.HideAfter:=-1;
ControlList1.CustomHint:=BalloonHint1;
ControlList1.Hint := IntToStr(ControlList1.HotItemIndex);
BalloonHint1.ShowHint(ControlList1);
end;
end;

MediaPlayer returning wrong mouse position

I've run into a strange problem with the MediaPlayer component
that appears to be a bug with either the API or Delphi.
I display the video directly on the Form2 canvas and allow
the user to zoom and drag the video within the window.
I use the Form2.MouseDown and MouseUp events to drag.
The drag was behaving erratically and I traced the problem
to inconsistent use of the window coordinates in the events.
An MPG or WMV video (compressed) will report the MouseDown
coordinates relative to the video, but the MouseUp relative
to the form. An AVI video (uncompressed) will report both
relative to the form.
I'm using Delphi XE3 with Windows 7.
Has anyone else encountered this anomaly, and how can I
get consistent X,Y coordinates?
Added 7/20:
I don't know what MCVE means, but I added some code in case someone
wants to try and duplicate the problem.
Label1 & Label2 report the mouse coordinates, and if the video is moved
out of the (0,0) position then the coordinates will jump as the mouse
crosses into or out of the video. It should not do that, it should
always report coordinates relative to the window, not the video.
FormMouseUp will always report relative to the window.
FormMouseDown and FormMouseMove jump back and forth.
procedure TForm1.MFLoadFileClick(Sender: TObject);
begin
MediaPlayer1.Open;
MediaPlayer1.Display := Form2;
end;
procedure TForm2.FormMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Label1.Caption := IntToStr(X); {diag}
Label2.Caption := IntToStr(Y); {diag}
VidLoc := Form1.MediaPlayer1.DisplayRect;
mX := X; mY := Y;
MouseDown := True;
end;
procedure TForm2.FormMouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
Label1.Caption := IntToStr(X); {diag}
Label2.Caption := IntToStr(Y); {diag}
end;
procedure TForm2.FormMouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var VL : tRect;
begin
Label1.Caption := IntToStr(X); {diag}
Label2.Caption := IntToStr(Y); {diag}
if (MouseDown and Form1.Loaded) then begin
VL := VidLoc;
VL.Left := VidLoc.Left - mX + X;
VL.Top := VidLoc.Top - mY + Y;
Form1.MediaPlayer1.DisplayRect := VL;
Form1.MediaPlayer1.Step;
end;
MouseDown := False;
end;
Oddly enough, moving the video in a panel doesn't work, but moving the panel did.
I didn't want to do that because it requires keeping track of too many objects, but it'll have to do.
Moving the panel uses the same code as above, but with other assorted properties.

How to drag a borderless FMX form on the screen through another object?

I am trying to make a form draggable on the screen, i.e. that I could grab it and move it around the screen. Its transparent and has no borders, however an image serves to be the background for other controls. I want to use the image's events to control dragging of the form. How can I do that?
I have found the DragEnter, DragLeave, DragStart methods which have this TDragObject argument, I don't know about.
Can somebody help?
Basically you have to do it manually.
Here's some delphi/windows code from a form with a transparent Image (TransImage) on it, no borders etc
The events are in the form for the Image so Top & Left refer to TMainScanForm.Top/Left.
This will drag your form around using the image events to detect the clicks and moves
...
// Mouse Drag Control
MouseDown: Boolean;
TopLeft,
MouseStart: TPoint;
...
procedure TMainScanForm.TransImageMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
MouseDown := (Button = mbLeft);
if MouseDown then
begin
MouseStart.X := X;
MouseStart.Y := Y;
TopLeft := ClientToScreen(MouseStart);
TopLeft.X := TopLeft.X - X;
TopLeft.Y := TopLeft.Y - Y;
end;
end;
procedure TMainScanForm.TransImageMouseMove( Sender: TObject;
Shift: TShiftState;
X, Y: Integer);
var
NewPoint: TPoint;
begin
if MouseDown then
begin
NewPoint.X := X;
NewPoint.Y := Y;
NewPoint := ClientToScreen(NewPoint); // On Screen
NewPoint.Y := NewPoint.Y - MouseStart.Y; // New Onscreen
NewPoint.X := NewPoint.X - MouseStart.X;
Top := NewPoint.Y;
Left := NewPoint.X;
Refresh;
end;
end;
procedure TMainScanForm.TransImageMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
MouseDown := False;
end;

Delphi - move control in runtime alike on design mode

Hy guys,
I try to move my own component on runtime mode with mouse alike in design mode.
the component isn't moved untill mouse button isn't released and in this time a empty frame is displayed and a hint show lefttop corner possition.
I done a lots of tries but no success untill now.
Any help
Here (http://neftali.clubdelphi.com/?p=269) on my web, you can find a component called TSelectOnRuntime. You can view the source code and study it. It's an simple approach to select, resize and move components on runtime.
Download the demo and evaluate, if it's valid for you (include the source of component, demo sources and compiled demos).
Well, I'll post it here. The following code uses undocumented WM_SYSCOMMAND constant $F012 and works with TWinControl descendants.
Note, that it's undocumented and it might not work on future versions of Windows (as anything else from Windows API if they decide to), but it works (tested on several Windows versions) and it's the easiest way how to move the component at runtime.
procedure TForm.YourComponentMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
const
SC_DRAGMOVE = $F012;
begin
ReleaseCapture;
YourComponent.Perform(WM_SYSCOMMAND, SC_DRAGMOVE, 0);
end;
The similar magic exists also for sizing, namely command $F008.
procedure TForm.YourComponentMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
const
SC_DRAGSIZE = $F008;
begin
ReleaseCapture;
YourComponent.Perform(WM_SYSCOMMAND, SC_DRAGSIZE, 0);
end;
If what i think you are trying to do is move controls at runtime, then here is some code you may use (and possibly modify slightly) to your needs:
var
MouseDownPos, LastPosition : TPoint;
DragEnabled,Resizing : Boolean;
procedure TForm1.ControlMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
MouseDownPos.X := X;
MouseDownPos.Y := Y;
DragEnabled := True;
end;
//handle dragging of controls
procedure TForm1.ControlMouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
if DragEnabled then
begin
if Sender is TControl then
begin
TControl(Sender).Left := TControl(Sender).Left + (X - MouseDownPos.X);
TControl(Sender).Top := TControl(Sender).Top + (Y - MouseDownPos.Y);
end;
end;
end;
For resizing controls you could use something like:
procedure TForm1.ControlMouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
var cntrl : TControl;
begin
cntrl := Sender as TControl;
if ((cntrl.Width - X) < 15) and ((cntrl.Height - Y) < 15) then
cntrl.Cursor := crSizeNWSE
else cntrl.Cursor := crDefault;
if Resizing then
begin
cntrl.Width := cntrl.Width + (X - LastPosition.X);
LastPosition.X := X;
cntrl.Height := cntrl.Height + (Y - LastPosition.Y);
LastPosition.Y := Y;
end;
end;
procedure TForm1.ControlMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var cntrl : TControl;
begin
if ((cntrl.Width - X) < 15) and ((cntrl.Height - Y) < 15) then
begin
LastPosition.X := X;
LastPosition.Y := Y;
Resizing := True;
end;
end;
Extensions to this may be snapping to a grid. This code may need to be modified slightly.
There is a component out there named TSizeCtrl which lets you move controls at runtime. You can find source code here or the component for download at Torry's.
It can be used like this:
SizeCtrl1 := TSizeCtrl.Create(MyForm);
SizeCtrl1.GridSize := 20;
SizeCtrl1.Enabled := True;
SizeCtrl1.RegisterControl(MyControl);
SizeCtrl1.AddTarget(MyControl);
This will let you drag MyControl around and resize it. It draws a frame while dragging and provides the handles for resizing.

Delphi How to get cursor position on a control?

I want to know the position of the cursor on a TCustomControl. How does one go about finding the coordinates?
GetCursorPos can be helpful if you can't handle a mouse event:
function GetCursorPosForControl(AControl: TWinControl): TPoint;
var
P: TPoint;
begin
Windows.GetCursorPos(P);
Windows.ScreenToClient(AControl.Handle, P );
result := P;
end;
You can use MouseMove event:
procedure TCustomControl.MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
Label1.Caption := IntToStr(x) + ' ' + IntToStr(y);
end;
If you want the cursor position when they click on the control, then use Mouse.CursorPos to get the mouse position, and Control.ScreenToClient to convert this to the position relative to the Control.
procedure TForm1.Memo1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
pt: TPoint;
begin
pt := Mouse.CursorPos;
pt := Memo1.ScreenToClient(pt);
Memo1.Lines.Add(Format('x=%d, y=%d', [pt.X, pt.y]));
end;
EDIT:
As various people have pointed out, this is pointless on a mouse down event. However as TCustomControl.OnMouseDown is protected, it may not always be readily available on third-party controls - mind you I would probably not use a control with such a flaw.
A better example might be an OnDblClick event, where no co-ordinate information is given:
procedure TForm1.DodgyControl1DblClick(Sender: TObject);
var
pt: TPoint;
begin
pt := Mouse.CursorPos;
pt := DodgyControl1.ScreenToClient(pt);
Memo1.Lines.Add(Format('x=%d, y=%d', [pt.X, pt.y]));
end;

Resources