How to detect drag and drop outside the application?

I am trying to emulate the drag and drop features of Chrome tabs. I want the user to be able to drag a tab to a new location in the tab bar or drop it outside the application to create a new window. Dragging and dropping into the application is easy, but how can I tell when a user crashes somewhere outside my application?

In essence, I am trying to implement the tab "separation".

+6
source share
1 answer

Since the mouse was captured during the drag operation, there is no problem detecting when the drag operation is completed in the OnEndDrag handler, even if it is outside any form of application. You can find out if the fall is accepted or not by testing the target object, and if it is not accepted, you can determine if it is outside the application by checking the position of the mouse.

However, there is still a problem with this approach. You cannot determine whether the drag has been canceled by pressing the Esc key. Also, the problem arises of the inability to move the drag cursor to the "accepted" outside the form, since OnDragOver control will not be called OnDragOver .

You can overcome this problem by changing the behavior of the drag and drop operation using the drag and drop object of your creation. The following is one example:

 unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls; type TForm1 = class(TForm) PageControl1: TPageControl; TabSheet1: TTabSheet; TabSheet2: TTabSheet; TabSheet3: TTabSheet; procedure FormCreate(Sender: TObject); procedure PageControl1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure PageControl1StartDrag(Sender: TObject; var DragObject: TDragObject); procedure PageControl1EndDrag(Sender, Target: TObject; X, Y: Integer); procedure PageControl1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin PageControl1.DragMode := dmManual; end; type TDragFloatSheet = class(TDragControlObjectEx) private class var FDragSheet: TTabSheet; FDragPos: TPoint; FCancelled: Boolean; protected procedure WndProc(var Msg: TMessage); override; end; procedure TDragFloatSheet.WndProc(var Msg: TMessage); begin if (Msg.Msg = CN_KEYDOWN) and (Msg.WParam = VK_ESCAPE) then FCancelled := True; FDragPos := DragPos; inherited; if (Msg.Msg = WM_MOUSEMOVE) and (not Assigned(FindVCLWindow(SmallPointToPoint(TWMMouse(Msg).Pos)))) then Winapi.Windows.SetCursor(Screen.Cursors[GetDragCursor(True, 0, 0)]); end; //------------------- procedure TForm1.PageControl1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin TDragFloatSheet.FDragSheet := (Sender as TPageControl).Pages[TPageControl(Sender).IndexOfTabAt(X, Y)]; PageControl1.BeginDrag(False); end; procedure TForm1.PageControl1StartDrag(Sender: TObject; var DragObject: TDragObject); begin DragObject := TDragFloatSheet.Create(Sender as TPageControl); end; procedure TForm1.PageControl1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); var TargetSheet: TTabSheet; begin TargetSheet := (Sender as TPageControl).Pages[TPageControl(Sender).IndexOfTabAt(X, Y)]; Accept := Assigned(TargetSheet) and (TargetSheet <> TDragFloatSheet.FDragSheet); end; procedure TForm1.PageControl1EndDrag(Sender, Target: TObject; X, Y: Integer); begin if Assigned(Target) then begin // normal processing, fi find the target tab as in OnDragOver // and switch positions with TDragFloatSheet.FDragSheet end else begin if not TDragFloatSheet.FCancelled then begin if not Assigned(FindVCLWindow(TDragFloatSheet.FDragPos)) then begin // drop TDragFloatSheet.FDragSheet at TDragFloatSheet.FDragPos end; end; end; end; end. 
+7
source

Source: https://habr.com/ru/post/928054/


All Articles