Recent

Author Topic: How can I make a borderless form moveable  (Read 14451 times)

J-G

  • Hero Member
  • *****
  • Posts: 992
How can I make a borderless form moveable
« on: October 31, 2016, 02:21:57 pm »
Three weeks ago RVK and Fungus pointed me in the right direction to create a borderless and transparent form and that program is now fully operational on a two screen PC providing a full screen analogue clock on the second screen with diary and repeatable alarm events. The small compromise to 'click through' being the need to have a second monitor  :)

Now I'm ready to progress to the next stage which is to have a borderless-transparent form - now smaller than full screen  - which can be MOVED  i.e. DRAGGED  with a MouseDown Event  -  just like the Clock 'Widget'.

I can see how to create a procedure structure for an OnMouseDown, OnMouseMove etc. but can't think what I need to put in the body of those structures. I imagine that it must have something to do with determining the current XY location of the form but beyond that I'm 'all at sea' and would be grateful for guidance.
« Last Edit: October 31, 2016, 02:29:59 pm by J-G »
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

macmike

  • Jr. Member
  • **
  • Posts: 85
    • Soft-Practice
Re: How can I make a borderless form moveable
« Reply #1 on: October 31, 2016, 02:46:52 pm »
OnMouseDown save the current drag point (the position of the mouse on the form)

OnMouseMove if the button is pressed then make the form position the mouse position - the previously saved drag point.

rvk

  • Hero Member
  • *****
  • Posts: 6953
Re: How can I make a borderless form moveable
« Reply #2 on: October 31, 2016, 03:00:14 pm »
The small compromise to 'click through' being the need to have a second monitor  :)
I would think having a click-through transparent form you wouldn't need a second monitor. (We showed you that the transparent part of the form is click-through so you can access the programs behind it.)

For the moving... is part of the form still visible? In that case you can use LM_NCHitTest.

Code: Pascal  [Select][+][-]
  1. uses LMessages;
  2.  
  3. type
  4.   TForm1 = class(TForm)
  5.     //...
  6.   private
  7.     { private declarations }
  8.     procedure LMNCHitTest(var Msg: TLMNCHitTest) ; message LM_NCHitTest;
  9.     //...
  10.  
  11. procedure TForm1.LMNCHitTest(var Msg: TLMNCHitTest);
  12. begin
  13.    inherited;
  14.    if Msg.Result = htClient then Msg.Result := htCaption;
  15. end;

Dragging the form-background will result in dragging the complete form.

Otherwise you can put this in de OnMouseDown or a TButton or TPanel:
Code: Pascal  [Select][+][-]
  1. uses LMessages;
  2.  
  3. procedure TForm1.Button1MouseDown(Sender: TObject; Button: TMouseButton;
  4.   Shift: TShiftState; X, Y: Integer);
  5. begin
  6.    ReleaseCapture;
  7.    SendMessage(Form1.Handle, LM_SYSCOMMAND, 61458, 0) ;
  8. end;
No need to save and restore locations.

(Although if you do this for TButton you'll loose the TButton.Click but for a TPanel this would be fine.)

« Last Edit: October 31, 2016, 03:04:02 pm by rvk »

RAW

  • Hero Member
  • *****
  • Posts: 871
Re: How can I make a borderless form moveable
« Reply #3 on: October 31, 2016, 03:04:07 pm »
This would be one way... but of course there are a lot more...
Normally you would use "OnMouseMove" and not a separate timer...

Code: Pascal  [Select][+][-]
  1. Unit uMoveForm;
  2.  {$MODE OBJFPC}{$H+}
  3.  
  4. Interface
  5.  USES
  6.   Classes, SysUtils, Forms, Controls, ExtCtrls;
  7.  
  8.  TYPE
  9.   TForm1 = Class(TForm)
  10.  
  11.    Procedure FormCreate    (Sender: TObject);
  12.    Procedure FormMouseDown (Sender: TObject;  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  13.    Procedure FormMouseUp   (Sender: TObject;  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  14.    Procedure OnTimer       (Sender: TObject);
  15.  
  16.      PRIVATE
  17.       iPosX : Integer;
  18.       iPosY : Integer;
  19.       tiMove: TTimer;
  20.   End;
  21.  
  22.  VAR
  23.   Form1: TForm1;
  24.  
  25. Implementation
  26.  {$R *.lfm}
  27.  
  28.  
  29. Procedure TForm1.FormCreate(Sender: TObject);
  30.  Begin
  31.   DoubleBuffered:= True;
  32.   BorderStyle   := bsNone;
  33.  
  34.   tiMove         := TTimer.Create(Application);
  35.   tiMove.Interval:= 30;
  36.   tiMove.Enabled := False;
  37.   tiMove.OnTimer := @OnTimer;
  38.  End;
  39.  
  40.  
  41. Procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  42.  Begin
  43.   If Button = mbLeft
  44.   Then
  45.    Begin
  46.     iPosX:= Mouse.CursorPos.X - Left;
  47.     iPosY:= Mouse.CursorPos.Y - Top;
  48.  
  49.     //ShowCursor(False);  // Optional: USES Windows...
  50.     tiMove.Enabled:= True;
  51.    End;
  52.  End;
  53.  
  54.  
  55. Procedure TForm1.OnTimer(Sender: TObject);
  56.  Begin
  57.   Top := Mouse.CursorPos.Y - iPosY;
  58.   Left:= Mouse.CursorPos.X - iPosX;
  59.  End;
  60.  
  61.  
  62. Procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  63.  Begin
  64.   If Button = mbLeft
  65.   Then
  66.    Begin
  67.     tiMove.Enabled:= False;
  68.     //ShowCursor(True);   // Optional: USES Windows...
  69.    End;
  70.  End;
  71. End.
  72.  
« Last Edit: October 31, 2016, 06:49:38 pm by SoE »

J-G

  • Hero Member
  • *****
  • Posts: 992
Re: How can I make a borderless form moveable
« Reply #4 on: October 31, 2016, 03:10:15 pm »
Thanks for the reply macmike but as yet I have no idea HOW to determine the current drag point!

Although I tend to post a question as soon as I come across a problem, I continue to investigate elsewhere and in this case quickly found a forum post from February 2015 which gave me a piece of code :-
Code: Pascal  [Select][+][-]
  1.   if ssLeft in Shift then
  2.     SetBounds(Left + (X - M_Pos.X), Top + (Y - M_Pos.Y), Width, Height);
  3.  
I've incorporated this in the body of a MouseMove Procedure (having also created a Var M_Pos : TPoint; )  and it has done exactly what I need. 

The problem I now have is that whilst copying existing code does the job, my knowledge has not advanced in any way because I don't know WHY it does the job!

WHY 'ssLeft'
WHY in 'Shift'
WHY 'SetBounds'
I haven't declared X or Y  so why does the code even compile!
EDIT !!!!   DUH  -  I've just seen that the procedure declaration declares X, Y and Shift :-[

I'm pleased that it works but would still appreciate explanations  ;D
« Last Edit: October 31, 2016, 03:16:41 pm by J-G »
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

RAW

  • Hero Member
  • *****
  • Posts: 871
Re: How can I make a borderless form moveable
« Reply #5 on: October 31, 2016, 03:13:31 pm »
With OnMouseMove...

Code: Pascal  [Select][+][-]
  1. Unit uMovePNG;
  2.  {$MODE OBJFPC}{$H+}
  3.  
  4. Interface
  5.  USES
  6.   Classes, SysUtils, Forms, Controls, ExtCtrls;
  7.  
  8.  TYPE
  9.   TForm1 = Class(TForm)
  10.  
  11.    picLAZ: TImage;
  12.  
  13.     Procedure FormCreate      (Sender: TObject);
  14.     Procedure picLAZMouseDown (Sender: TObject;  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  15.     Procedure picLAZMouseMove (Sender: TObject;  Shift : TShiftState;  X, Y : Integer);
  16.     Procedure picLAZMouseUp   (Sender: TObject;  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  17.  
  18.      PRIVATE
  19.       iPosX  : Integer;
  20.       iPosY  : Integer;
  21.       booMove: Boolean;
  22.   End;
  23.  
  24.  VAR
  25.   Form1 : TForm1;
  26.  
  27. Implementation
  28.  {$R *.lfm}
  29.  
  30.  
  31. Procedure TForm1.FormCreate(Sender: TObject);
  32.  Begin
  33.   DoubleBuffered:= True;
  34.   booMove       := False;
  35.  End;
  36.  
  37.  
  38. Procedure TForm1.picLAZMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  39.  Begin
  40.   If Button = mbLeft
  41.   Then
  42.    Begin
  43.     iPosX:= X;
  44.     iPosY:= Y;
  45.  
  46.     booMove:= True;
  47.     //ShowCursor(False); Optional: USES Windows...
  48.    End;
  49.  End;
  50.  
  51.  
  52. Procedure TForm1.picLAZMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
  53.   Var
  54.    p: TPoint;
  55.  Begin
  56.   p:= ScreenToClient(Mouse.CursorPos);
  57.  
  58.   If booMove
  59.   Then
  60.    Begin
  61.     picLaz.Top := p.Y - iPosY;
  62.     picLaz.Left:= p.X - iPosX;
  63.    End;
  64.  End;
  65.  
  66.  
  67. Procedure TForm1.picLAZMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  68.  Begin
  69.   If Button = mbLeft
  70.   Then
  71.    Begin
  72.     booMove:= False;
  73.     //ShowCursor(True); Optional: USES Windows...
  74.    End;
  75.  End;
  76. End.
  77.  
« Last Edit: October 31, 2016, 07:06:36 pm by SoE »

Handoko

  • Hero Member
  • *****
  • Posts: 5524
  • My goal: build my own game engine using Lazarus
Re: How can I make a borderless form moveable
« Reply #6 on: October 31, 2016, 03:14:55 pm »
Try my code with source files you can download.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Shape1: TShape;
  16.     procedure Shape1MouseDown(Sender: TObject; Button: TMouseButton;
  17.       Shift: TShiftState; X, Y: Integer);
  18.     procedure Shape1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer
  19.       );
  20.     procedure Shape1MouseUp(Sender: TObject; Button: TMouseButton;
  21.       Shift: TShiftState; X, Y: Integer);
  22.   private
  23.     { private declarations }
  24.   public
  25.     { public declarations }
  26.   end;
  27.  
  28. var
  29.   Form1: TForm1;
  30.  
  31. implementation
  32.  
  33. var
  34.   lastX, lastY: Integer;
  35.   isMoving: Boolean = False;
  36.  
  37. {$R *.lfm}
  38.  
  39. { TForm1 }
  40.  
  41. procedure TForm1.Shape1MouseDown(Sender: TObject; Button: TMouseButton;
  42.   Shift: TShiftState; X, Y: Integer);
  43. begin
  44.   isMoving := True;
  45.   lastX := X;
  46.   lastY := Y;
  47. end;
  48.  
  49. procedure TForm1.Shape1MouseMove(Sender: TObject; Shift: TShiftState; X,
  50.   Y: Integer);
  51. begin
  52.   if isMoving then begin
  53.     Left := Left+X-lastX;
  54.     Top  := Top +Y-lastY;
  55.   end;
  56. end;
  57.  
  58. procedure TForm1.Shape1MouseUp(Sender: TObject; Button: TMouseButton;
  59.   Shift: TShiftState; X, Y: Integer);
  60. begin
  61.   isMoving := False;
  62. end;
  63.  
  64. end.


rvk

  • Hero Member
  • *****
  • Posts: 6953
Re: How can I make a borderless form moveable
« Reply #7 on: October 31, 2016, 03:16:41 pm »
... but as yet I have no idea HOW to determine the current drag point!
You don't need current drag point etc.

Look at my solution in reply #2. You can just put a message (61458) through to the underlying form to start dragging. It's that simple.

And if you want to drag the form-background (instead of dragging through a component) you can catch LM_NCHitTest like I showed you.
You can also combine them so you can drag the form-background AND a TPanel.

RAW

  • Hero Member
  • *****
  • Posts: 871
Re: How can I make a borderless form moveable
« Reply #8 on: October 31, 2016, 03:45:47 pm »
Quote
    uses LMessages;
     
    type
      TForm1 = class(TForm)
        //...
      private
        { private declarations }
        procedure LMNCHitTest(var Msg: TLMNCHitTest) ; message LM_NCHitTest;
        //...
     
    procedure TForm1.LMNCHitTest(var Msg: TLMNCHitTest);
    begin
       inherited;
       if Msg.Result = htClient then Msg.Result := htCaption;
    end;
This way you need USES: LCLType...
It's working fine but there can be a problem in relation to the user settings (at least on Windows)...
It's not a big deal, but when you move the window it won't show up and you will only see a rectangle...

Probably in most cases it doesn't matter...

rvk

  • Hero Member
  • *****
  • Posts: 6953
Re: How can I make a borderless form moveable
« Reply #9 on: October 31, 2016, 03:50:47 pm »
This way you need USES: LCLType...
Yes... LCLType was already needed for TCreateParams mentioned in the method to create a transparent Windows. That's why I forgot to include it (because it was already in my uses at the top).

Quote
It's working fine but there can be a problem in relation to the user settings (at least on Windows)...
It's not a big deal, but when you move the window it won't show up and you will only see a rectangle...
For me (Windows 10) the transparent form (I used a circle) showed up completely/full during dragging.
I think dragging with showing a rectangle is a Windows-setting. In that case all windows should draw a rectangle during dragging.

J-G

  • Hero Member
  • *****
  • Posts: 992
Re: How can I make a borderless form moveable
« Reply #10 on: October 31, 2016, 04:05:33 pm »
The small compromise to 'click through' being the need to have a second monitor  :)
I would think having a click-through transparent form you wouldn't need a second monitor. (We showed you that the transparent part of the form is click-through so you can access the programs behind it.)

You did RVK but you'll also recall that the buttons on the form were also 'click-through' and we couldn't solve that issue. Your further comment that   "...you'll loose the TButton.Click but for a TPanel this would be fine." may well be worth further investigation.

For the moving... is part of the form still visible? In that case you can use LM_NCHitTest.

Yes there is a background image which fills most of the form but now I've found what is a simple If-Then solution I'm happy to go with that. Especially since I've proved that I can click on any part of the image to drag the form anywhere I wish. The small irritation that as soon as I move the mouse (button down) the form jumps so that the top-left becomes the mouse cursor focus is of little consequence.

It also shows the full image during the drag (not just a rectangle).

The speed of response on this forum is sometimes just too good !!!   but I'd still like to know where  'ssLeft'  comes from?


FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

RAW

  • Hero Member
  • *****
  • Posts: 871
Re: How can I make a borderless form moveable
« Reply #11 on: October 31, 2016, 04:13:28 pm »
Quote
I think dragging with showing a rectangle is a Windows-setting. In that case all windows should draw a rectangle during dragging.
Yes, I think it's in the advanced system-properties where the user can enable or disable this...

btw:
Is there a way to send (or simulate) messages without USES: Windows ???

I tried this (with LMessages and LCLType), but I get "Identifier not found"  ?
Is there another unit that needs to be included ?
Quote
procedure TForm1.Button1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
   ReleaseCapture;
   SendMessage(Form1.Handle, LM_SYSCOMMAND, 61458, 0) ;
end;
With Delphi I used this all the time... // Perform(WM_SYSCOMMAND, $F012, 0);

rvk

  • Hero Member
  • *****
  • Posts: 6953
Re: How can I make a borderless form moveable
« Reply #12 on: October 31, 2016, 04:42:58 pm »
I tried this (with LMessages and LCLType), but I get "Identifier not found"  ?
Is there another unit that needs to be included ?
Yeah, you don't need uses Windows;
This is enough (ReleaseCapture is defined in LCLintf although internally that one uses {$I winapih.inc} again :) ):
Code: Pascal  [Select][+][-]
  1. uses LMessages, LCLType, LCLIntf;

Code: Pascal  [Select][+][-]
  1. Perform(WM_SYSCOMMAND, $F012, 0);
This is the same as  SendMessage(Form1.Handle, LM_SYSCOMMAND, 61458, 0) ;
LM_SYSCOMMAND is the same as WM_SYSCOMMAND.
$F012 is the same as 61458.

rvk

  • Hero Member
  • *****
  • Posts: 6953
Re: How can I make a borderless form moveable
« Reply #13 on: October 31, 2016, 04:48:27 pm »
The small irritation that as soon as I move the mouse (button down) the form jumps so that the top-left becomes the mouse cursor focus is of little consequence.
How did you do this now... For me dragging seems just as natural as using the title-border. It doesn't jump for me to 0,0.

Quote
but I'd still like to know where  'ssLeft'  comes from?
ssLeft is a TShiftStateEnum. (State for the Shift-key).

Code: Pascal  [Select][+][-]
  1.   TShiftStateEnum = (ssShift, ssAlt, ssCtrl,
  2.     ssLeft, ssRight, ssMiddle, ssDouble,
  3.     // Extra additions
  4.     ssMeta, ssSuper, ssHyper, ssAltGr, ssCaps, ssNum,
  5.     ssScroll,ssTriple,ssQuad,ssExtra1,ssExtra2);
  6.  

If you used that method instead of mine I can imagine the jumping around. Did you try my method for TPanel (or TImage).OnMouseDown?

Code: Pascal  [Select][+][-]
  1. uses LMessages, LCLType, LCLIntf;
  2.  
  3. procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  4.   Shift: TShiftState; X, Y: Integer);
  5. begin
  6.    ReleaseCapture;
  7.    SendMessage(Form1.Handle, LM_SYSCOMMAND, 61458, 0) ;
  8. end;

J-G

  • Hero Member
  • *****
  • Posts: 992
Re: How can I make a borderless form moveable
« Reply #14 on: October 31, 2016, 05:22:32 pm »
The small irritation that as soon as I move the mouse (button down) the form jumps so that the top-left becomes the mouse cursor focus is of little consequence.
How did you do this now... For me dragging seems just as natural as using the title-border. It doesn't jump for me to 0,0.


If you used that method instead of mine I can imagine the jumping around. Did you try my method for TPanel (or TImage).OnMouseDown?

I have now, and yes, it has rid me of the 0,0 jump  - thanks  -  a much cleaner/nicer action!

Thanks also for the ssLeft explanation.

I've attached a cropped screen grab to show what I've achieved.

Now I have to code the main functions to get the hands moving  ;)
« Last Edit: October 31, 2016, 05:25:43 pm by J-G »
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

 

TinyPortal © 2005-2018