4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
6
import { DataTransfers , IDragAndDropData } from 'vs/base/browser/dnd' ;
7
- import { DragAndDropObserver , EventType , addDisposableListener } from 'vs/base/browser/dom' ;
7
+ import { DragAndDropObserver , EventType , addDisposableListener , onDidRegisterWindow } from 'vs/base/browser/dom' ;
8
8
import { DragMouseEvent } from 'vs/base/browser/mouseEvent' ;
9
9
import { IListDragAndDrop } from 'vs/base/browser/ui/list/list' ;
10
10
import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView' ;
11
11
import { ITreeDragOverReaction } from 'vs/base/browser/ui/tree/tree' ;
12
12
import { coalesce } from 'vs/base/common/arrays' ;
13
13
import { UriList , VSDataTransfer } from 'vs/base/common/dataTransfer' ;
14
- import { Emitter } from 'vs/base/common/event' ;
14
+ import { Emitter , Event } from 'vs/base/common/event' ;
15
15
import { Disposable , DisposableStore , IDisposable , markAsSingleton } from 'vs/base/common/lifecycle' ;
16
16
import { stringify } from 'vs/base/common/marshalling' ;
17
17
import { Mimes } from 'vs/base/common/mime' ;
@@ -35,6 +35,8 @@ import { IHostService } from 'vs/workbench/services/host/browser/host';
35
35
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles' ;
36
36
import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing' ;
37
37
import { IEditorOptions } from 'vs/platform/editor/common/editor' ;
38
+ import { mainWindow } from 'vs/base/browser/window' ;
39
+ import { BroadcastDataChannel } from 'vs/base/browser/broadcast' ;
38
40
39
41
//#region Editor / Resources DND
40
42
@@ -48,7 +50,6 @@ export class DraggedEditorGroupIdentifier {
48
50
constructor ( readonly identifier : GroupIdentifier ) { }
49
51
}
50
52
51
-
52
53
export async function extractTreeDropData ( dataTransfer : VSDataTransfer ) : Promise < Array < IDraggedResourceEditorInput > > {
53
54
const editors : IDraggedResourceEditorInput [ ] = [ ] ;
54
55
const resourcesKey = Mimes . uriList . toLowerCase ( ) ;
@@ -187,10 +188,10 @@ export class ResourcesDropHandler {
187
188
}
188
189
}
189
190
190
- export function fillEditorsDragData ( accessor : ServicesAccessor , resources : URI [ ] , event : DragMouseEvent | DragEvent ) : void ;
191
- export function fillEditorsDragData ( accessor : ServicesAccessor , resources : IResourceStat [ ] , event : DragMouseEvent | DragEvent ) : void ;
192
- export function fillEditorsDragData ( accessor : ServicesAccessor , editors : IEditorIdentifier [ ] , event : DragMouseEvent | DragEvent ) : void ;
193
- export function fillEditorsDragData ( accessor : ServicesAccessor , resourcesOrEditors : Array < URI | IResourceStat | IEditorIdentifier > , event : DragMouseEvent | DragEvent ) : void {
191
+ export function fillEditorsDragData ( accessor : ServicesAccessor , resources : URI [ ] , event : DragMouseEvent | DragEvent , options ?: { disableStandardTransfer : boolean } ) : void ;
192
+ export function fillEditorsDragData ( accessor : ServicesAccessor , resources : IResourceStat [ ] , event : DragMouseEvent | DragEvent , options ?: { disableStandardTransfer : boolean } ) : void ;
193
+ export function fillEditorsDragData ( accessor : ServicesAccessor , editors : IEditorIdentifier [ ] , event : DragMouseEvent | DragEvent , options ?: { disableStandardTransfer : boolean } ) : void ;
194
+ export function fillEditorsDragData ( accessor : ServicesAccessor , resourcesOrEditors : Array < URI | IResourceStat | IEditorIdentifier > , event : DragMouseEvent | DragEvent , options ?: { disableStandardTransfer : boolean } ) : void {
194
195
if ( resourcesOrEditors . length === 0 || ! event . dataTransfer ) {
195
196
return ;
196
197
}
@@ -217,22 +218,25 @@ export function fillEditorsDragData(accessor: ServicesAccessor, resourcesOrEdito
217
218
218
219
return resourceOrEditor ;
219
220
} ) ) ;
220
- const fileSystemResources = resources . filter ( ( { resource } ) => fileService . hasProvider ( resource ) ) ;
221
221
222
- // Text: allows to paste into text-capable areas
223
- const lineDelimiter = isWindows ? '\r\n' : '\n' ;
224
- event . dataTransfer . setData ( DataTransfers . TEXT , fileSystemResources . map ( ( { resource } ) => labelService . getUriLabel ( resource , { noPrefix : true } ) ) . join ( lineDelimiter ) ) ;
225
-
226
- // Download URL: enables support to drag a tab as file to desktop
227
- // Requirements:
228
- // - Chrome/Edge only
229
- // - only a single file is supported
230
- // - only file:/ resources are supported
231
- const firstFile = fileSystemResources . find ( ( { isDirectory } ) => ! isDirectory ) ;
232
- if ( firstFile ) {
233
- const firstFileUri = FileAccess . uriToFileUri ( firstFile . resource ) ; // enforce `file:` URIs
234
- if ( firstFileUri . scheme === Schemas . file ) {
235
- event . dataTransfer . setData ( DataTransfers . DOWNLOAD_URL , [ Mimes . binary , basename ( firstFile . resource ) , firstFileUri . toString ( ) ] . join ( ':' ) ) ;
222
+ const fileSystemResources = resources . filter ( ( { resource } ) => fileService . hasProvider ( resource ) ) ;
223
+ if ( ! options ?. disableStandardTransfer ) {
224
+
225
+ // Text: allows to paste into text-capable areas
226
+ const lineDelimiter = isWindows ? '\r\n' : '\n' ;
227
+ event . dataTransfer . setData ( DataTransfers . TEXT , fileSystemResources . map ( ( { resource } ) => labelService . getUriLabel ( resource , { noPrefix : true } ) ) . join ( lineDelimiter ) ) ;
228
+
229
+ // Download URL: enables support to drag a tab as file to desktop
230
+ // Requirements:
231
+ // - Chrome/Edge only
232
+ // - only a single file is supported
233
+ // - only file:/ resources are supported
234
+ const firstFile = fileSystemResources . find ( ( { isDirectory } ) => ! isDirectory ) ;
235
+ if ( firstFile ) {
236
+ const firstFileUri = FileAccess . uriToFileUri ( firstFile . resource ) ; // enforce `file:` URIs
237
+ if ( firstFileUri . scheme === Schemas . file ) {
238
+ event . dataTransfer . setData ( DataTransfers . DOWNLOAD_URL , [ Mimes . binary , basename ( firstFile . resource ) , firstFileUri . toString ( ) ] . join ( ':' ) ) ;
239
+ }
236
240
}
237
241
}
238
242
@@ -467,9 +471,6 @@ export class CompositeDragAndDropObserver extends Disposable {
467
471
registerTarget ( element : HTMLElement , callbacks : ICompositeDragAndDropObserverCallbacks ) : IDisposable {
468
472
const disposableStore = new DisposableStore ( ) ;
469
473
disposableStore . add ( new DragAndDropObserver ( element , {
470
- onDragEnd : e => {
471
- // no-op
472
- } ,
473
474
onDragEnter : e => {
474
475
e . preventDefault ( ) ;
475
476
@@ -533,16 +534,15 @@ export class CompositeDragAndDropObserver extends Disposable {
533
534
534
535
const disposableStore = new DisposableStore ( ) ;
535
536
536
- disposableStore . add ( addDisposableListener ( element , EventType . DRAG_START , e => {
537
- const { id, type } = draggedItemProvider ( ) ;
538
- this . writeDragData ( id , type ) ;
539
-
540
- e . dataTransfer ?. setDragImage ( element , 0 , 0 ) ;
537
+ disposableStore . add ( new DragAndDropObserver ( element , {
538
+ onDragStart : e => {
539
+ const { id, type } = draggedItemProvider ( ) ;
540
+ this . writeDragData ( id , type ) ;
541
541
542
- this . onDragStart . fire ( { eventData : e , dragAndDropData : this . readDragData ( type ) ! } ) ;
543
- } ) ) ;
542
+ e . dataTransfer ?. setDragImage ( element , 0 , 0 ) ;
544
543
545
- disposableStore . add ( new DragAndDropObserver ( element , {
544
+ this . onDragStart . fire ( { eventData : e , dragAndDropData : this . readDragData ( type ) ! } ) ;
545
+ } ,
546
546
onDragEnd : e => {
547
547
const { type } = draggedItemProvider ( ) ;
548
548
const data = this . readDragData ( type ) ;
@@ -661,3 +661,68 @@ export class ResourceListDnDHandler<T> implements IListDragAndDrop<T> {
661
661
}
662
662
663
663
//#endregion
664
+
665
+ class GlobalWindowDraggedOverTracker extends Disposable {
666
+
667
+ private static readonly CHANNEL_NAME = 'monaco-workbench-global-dragged-over' ;
668
+
669
+ private readonly broadcaster = this . _register ( new BroadcastDataChannel < boolean > ( GlobalWindowDraggedOverTracker . CHANNEL_NAME ) ) ;
670
+
671
+ constructor ( ) {
672
+ super ( ) ;
673
+
674
+ this . registerListeners ( ) ;
675
+ }
676
+
677
+ private registerListeners ( ) : void {
678
+ this . _register ( Event . runAndSubscribe ( onDidRegisterWindow , ( { window, disposables } ) => {
679
+ disposables . add ( addDisposableListener ( window , EventType . DRAG_OVER , ( ) => this . markDraggedOver ( false ) , true ) ) ;
680
+ disposables . add ( addDisposableListener ( window , EventType . DRAG_LEAVE , ( ) => this . clearDraggedOver ( false ) , true ) ) ;
681
+ } , { window : mainWindow , disposables : this . _store } ) ) ;
682
+
683
+ this . _register ( this . broadcaster . onDidReceiveData ( data => {
684
+ if ( data === true ) {
685
+ this . markDraggedOver ( true ) ;
686
+ } else {
687
+ this . clearDraggedOver ( true ) ;
688
+ }
689
+ } ) ) ;
690
+ }
691
+
692
+ private draggedOver = false ;
693
+ get isDraggedOver ( ) : boolean { return this . draggedOver ; }
694
+
695
+ private markDraggedOver ( fromBroadcast : boolean ) : void {
696
+ if ( this . draggedOver === true ) {
697
+ return ; // alrady marked
698
+ }
699
+
700
+ this . draggedOver = true ;
701
+
702
+ if ( ! fromBroadcast ) {
703
+ this . broadcaster . postData ( true ) ;
704
+ }
705
+ }
706
+
707
+ private clearDraggedOver ( fromBroadcast : boolean ) : void {
708
+ if ( this . draggedOver === false ) {
709
+ return ; // alrady cleared
710
+ }
711
+
712
+ this . draggedOver = false ;
713
+
714
+ if ( ! fromBroadcast ) {
715
+ this . broadcaster . postData ( false ) ;
716
+ }
717
+ }
718
+ }
719
+
720
+ const globalDraggedOverTracker = new GlobalWindowDraggedOverTracker ( ) ;
721
+
722
+ /**
723
+ * Returns whether the workbench is currently dragged over in any of
724
+ * the opened windows (main windows and auxiliary windows).
725
+ */
726
+ export function isWindowDraggedOver ( ) : boolean {
727
+ return globalDraggedOverTracker . isDraggedOver ;
728
+ }
0 commit comments