Refactored DragDrop

This commit is contained in:
Simon Lübeß
2025-06-26 22:35:38 +02:00
parent 5fb8401a1a
commit 2cc8889ac1
6 changed files with 155 additions and 68 deletions
+3 -4
View File
@@ -9,6 +9,7 @@ using DirectX.Common;
using GlitchyEditor.Platform.Windows;
using GlitchyEditor.Multithreading;
using System.Threading;
using GlitchyEditor.Platform;
namespace GlitchyEditor
{
@@ -29,16 +30,14 @@ namespace GlitchyEditor
_backgroundTaskManager.Init();
// TODO: Windows only
HResult result = OleInitialize(null);
Log.EngineLogger.Assert(result case .S_OK);
DragDropManager.Init();
PushLayer(new EditorLayer(args, _contentManager));
}
public ~this()
{
OleUninitialize();
DragDropManager.Deinit();
_backgroundTaskManager.Deinit();
}
+21 -47
View File
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using ImGui;
using GlitchyEngine;
using GlitchyEditor.EditWindows;
@@ -116,45 +117,6 @@ namespace GlitchyEditor
private bool _showImguiDemoWindow;
#endif
/// Registers a drag'n'drop target that will fire corresponding DragDropEvent using the engine's event system.
class GlobalDragDropTarget : IDropTargetImplBase
{
public override Result<DropEffect> OnDragEnter(int2 cursorPosition)
{
DragDropEvent event = scope DragDropEvent(.Enter, cursorPosition);
Application.Instance.OnEvent(event);
return event.OutDropEffect;
}
public override Result<DropEffect> OnDragOver(int2 cursorPosition)
{
DragDropEvent event = scope DragDropEvent(.Over, cursorPosition);
Application.Instance.OnEvent(event);
return event.OutDropEffect;
}
public override Result<void> OnDragLeave()
{
DragDropEvent event = scope DragDropEvent(.Leave, .(-1, -1));
Application.Instance.OnEvent(event);
return .Ok;
}
public override Result<DropEffect> OnDrop(int2 cursorPosition, List<String> fileNames)
{
DragDropEvent event = scope DragDropEvent(.Drop, cursorPosition);
event.FileNames = fileNames;
Application.Instance.OnEvent(event);
return event.OutDropEffect;
}
}
private GlobalDragDropTarget _dragDropTarget ~
{
_.Unregister();
_?.ReleaseRef();
}
[AllowAppend]
public this(String[] args, EditorContentManager contentManager) : base("Editor")
{
@@ -165,9 +127,6 @@ namespace GlitchyEditor
// TODO: Das muss weg
_layer = this;
_dragDropTarget = new .();
_dragDropTarget.Register();
_contentManager = contentManager;
InitGraphics();
@@ -1709,7 +1668,7 @@ namespace GlitchyEditor
{
if (_layer._isDraggingFromOutside == .None)
{
Log.EngineLogger.Warning("Setting drop effect, eventhou the user is currently not dragging.");
Log.EngineLogger.Warning("Setting drop effect, even though the user is currently not dragging.");
}
_layer._effectWhenDropping = effect;
@@ -1722,7 +1681,7 @@ namespace GlitchyEditor
ImGui.GetIO().MousePos = .(e.CursorPosition.X, e.CursorPosition.Y);
}
if (e.FileNames == null)
if (e.FileNames.IsEmpty)
{
DeleteContainerAndItems!(_droppedFiles);
_droppedFiles = null;
@@ -1734,12 +1693,27 @@ namespace GlitchyEditor
_droppedFiles = new List<String>();
}
for (String fileName in e.FileNames)
int firstIndexToRemove = 0;
for (StringView filePath in e.FileNames)
{
if (!_droppedFiles.Contains(fileName, .Ordinal))
int index = _droppedFiles.IndexOfAlt(filePath);
if (index == -1)
{
_droppedFiles.Add(new String(fileName));
index = _droppedFiles.Count;
_droppedFiles.Add(new String(filePath));
}
// Make sure, all paths that are in e.FileNames are before firstIndexToRemove
Swap!(_droppedFiles[firstIndexToRemove], _droppedFiles[index]);
firstIndexToRemove++;
}
// Remove all paths beyond firstIndexToRemove
for (int i = _droppedFiles.Count; i > firstIndexToRemove; i--)
{
delete _droppedFiles.PopBack();
}
}
@@ -0,0 +1,9 @@
namespace GlitchyEditor.Platform;
/// Manages system drag and drop events and fires corresponding engine events.
static class DragDropManager
{
public extern static void Init();
public extern static void Deinit();
}
@@ -1,3 +1,5 @@
#if BF_PLATFORM_WINDOWS
using System;
using DirectX.Common;
using DirectX.Math;
@@ -10,18 +12,18 @@ using System.IO;
namespace GlitchyEditor.Platform.Windows;
// This entire thing is so platform dependent, that it really doesn't make sense to abstract while we are windows only.
static
{
[Import("Ole32.lib"), CLink]
[Import("Ole32.lib"), CLink, CallingConvention(.Stdcall)]
public static extern HResult OleInitialize(void* reserved);
[Import("Ole32.lib"), CLink]
[Import("Ole32.lib"), CLink, CallingConvention(.Stdcall)]
public static extern void OleUninitialize();
[Import("Ole32.lib"), CLink]
[Import("Ole32.lib"), CLink, CallingConvention(.Stdcall)]
public static extern HResult RegisterDragDrop(HWnd windowHandle, IDropTarget* dropTarget);
[Import("Ole32.lib"), CLink]
[Import("Ole32.lib"), CLink, CallingConvention(.Stdcall)]
public static extern HResult RevokeDragDrop(HWnd windowHandle);
[Import("ole32.dll"), CLink, CallingConvention(.Stdcall)]
public static extern HResult DoDragDrop(IDataObject* pDataObj, IDropSource* pDropSource, DropEffect dwOKEffects, out DropEffect pdwEffect);
}
[CRepr]
@@ -85,7 +87,6 @@ public struct FORMATETC
public typealias HBITMAP = int;
public typealias HENHMETAFILE = int;
public typealias PWSTR = char16*;
public typealias BOOL = int32;
[CRepr]
public struct STGMEDIUM
{
@@ -116,7 +117,7 @@ public struct IDataObject : IUnknown
public HResult GetDataHere(ref FORMATETC pformatetc, out STGMEDIUM pmedium) mut => VT.GetDataHere(ref this, ref pformatetc, out pmedium);
public HResult QueryGetData(ref FORMATETC pformatetc) mut => VT.QueryGetData(ref this, ref pformatetc);
public HResult GetCanonicalFormatEtc(ref FORMATETC pformatectIn, out FORMATETC pformatetcOut) mut => VT.GetCanonicalFormatEtc(ref this, ref pformatectIn, out pformatetcOut);
public HResult SetData(ref FORMATETC pformatetc, ref STGMEDIUM pmedium, BOOL fRelease) mut => VT.SetData(ref this, ref pformatetc, ref pmedium, fRelease);
public HResult SetData(ref FORMATETC pformatetc, ref STGMEDIUM pmedium, BigBool fRelease) mut => VT.SetData(ref this, ref pformatetc, ref pmedium, fRelease);
public HResult EnumFormatEtc(uint32 dwDirection, out /*IEnumFORMATETC*/ IUnknown* ppenumFormatEtc) mut => VT.EnumFormatEtc(ref this, dwDirection, out ppenumFormatEtc);
public HResult DAdvise(ref FORMATETC pformatetc, uint32 advf, ref /*IAdviseSink*/ IUnknown* pAdvSink, out uint32 pdwConnection) mut => VT.DAdvise(ref this, ref pformatetc, advf, ref pAdvSink, out pdwConnection);
public HResult DUnadvise(uint32 dwConnection) mut => VT.DUnadvise(ref this, dwConnection);
@@ -129,7 +130,7 @@ public struct IDataObject : IUnknown
public new function [CallingConvention(.Stdcall)] HResult(ref IDataObject self, ref FORMATETC pformatetc, out STGMEDIUM pmedium) GetDataHere;
public new function [CallingConvention(.Stdcall)] HResult(ref IDataObject self, ref FORMATETC pformatetc) QueryGetData;
public new function [CallingConvention(.Stdcall)] HResult(ref IDataObject self, ref FORMATETC pformatectIn, out FORMATETC pformatetcOut) GetCanonicalFormatEtc;
public new function [CallingConvention(.Stdcall)] HResult(ref IDataObject self, ref FORMATETC pformatetc, ref STGMEDIUM pmedium, BOOL fRelease) SetData;
public new function [CallingConvention(.Stdcall)] HResult(ref IDataObject self, ref FORMATETC pformatetc, ref STGMEDIUM pmedium, BigBool fRelease) SetData;
public new function [CallingConvention(.Stdcall)] HResult(ref IDataObject self, uint32 dwDirection, out /*IEnumFORMATETC*/ IUnknown* ppenumFormatEtc) EnumFormatEtc;
public new function [CallingConvention(.Stdcall)] HResult(ref IDataObject self, ref FORMATETC pformatetc, uint32 advf, ref /*IAdviseSink*/ IUnknown* pAdvSink, out uint32 pdwConnection) DAdvise;
public new function [CallingConvention(.Stdcall)] HResult(ref IDataObject self, uint32 dwConnection) DUnadvise;
@@ -229,6 +230,8 @@ abstract class IDropTargetImplBase : RefCounted
{
HResult result = RevokeDragDrop(Application.Instance.Window.[Friend]_windowHandle);
Log.EngineLogger.Assert(result case .S_OK);
OleUninitialize();
}
private static IDropTargetImplBase GetInstance(IUnknown* self)
@@ -279,7 +282,7 @@ abstract class IDropTargetImplBase : RefCounted
public abstract Result<DropEffect> OnDragEnter(int2 cursorPosition);
public abstract Result<DropEffect> OnDragOver(int2 cursorPosition);
public abstract Result<void> OnDragLeave();
public abstract Result<DropEffect> OnDrop(int2 cursorPosition, List<String> fileNames);
public abstract Result<DropEffect> OnDrop(int2 cursorPosition, Span<StringView> fileNames);
[CallingConvention(.Stdcall)]
private static HResult DragEnterImpl(IDropTarget* self, /*IDataObject*/ IUnknown* dataObject, uint32 grfKeyState, int2 point, ref DropEffect effect)
@@ -338,15 +341,16 @@ abstract class IDropTargetImplBase : RefCounted
tymed = (.)TYMED.HGLOBAL
};
List<String> files = null;
defer {DeleteContainerAndItems!(files);}
String paths = scope .();
List<StringView> files = null;
defer { delete files; }
if (dataObject.GetData(ref format, var stgm).Succeeded)
{
HDROP hdrop = (HDROP)stgm.hGlobal;
uint32 fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, null, 0);
files = new List<String>(fileCount);
files = new List<StringView>(fileCount);
List<char16> buffer = scope List<char16>();
buffer.Resize(256);
@@ -359,14 +363,27 @@ abstract class IDropTargetImplBase : RefCounted
uint32 retrievedSize = DragQueryFileW(hdrop, i, buffer.Ptr, (.)buffer.Count);
if (retrievedSize > 0 && retrievedSize < buffer.Count)
{
String str = new String(buffer.Ptr);
files.Add(str);
int startIndex = paths.Length;
paths.Append(buffer);
int endIndex = paths.Length;
// StringViews might be invalid until fixup step down below
files.Add(StringView(paths, startIndex, endIndex - startIndex));
}
}
ReleaseStgMedium(ref stgm);
}
int index = 0;
// Fixup pointer of string views
for (ref StringView path in ref files)
{
path.Ptr = paths.Ptr + index;
index += path.Length;
}
Result<DropEffect> result = instance.OnDrop(point, files);
if (result case .Ok(out effect))
@@ -430,8 +447,8 @@ public class DragDropEvent : Event, IEvent
public DropEffect OutDropEffect { get; set; }
// Do not keep references to this list or any of it's items outside of the event handler, they will not survive it.
public List<String> FileNames { get; set; }
/// Do not keep references to this list or any of it's items outside of the event handler, they will not survive it.
public Span<StringView> FileNames { get; set; }
// TODO: Keys, or perhaps just make the listeners query them...
@@ -449,3 +466,5 @@ namespace GlitchyEngine.Events
case DragDropEvent;
}
}
#endif
@@ -0,0 +1,79 @@
#if BF_PLATFORM_WINDOWS
using System;
using System.Collections;
using GlitchyEngine;
using GlitchyEngine.Math;
using GlitchyEditor.Platform.Windows;
using DirectX.Common;
namespace GlitchyEditor.Platform;
extension DragDropManager
{
private static DragDropEventTranslator _dragDropTarget;
public override static void Init()
{
HResult result = OleInitialize(null);
Log.EngineLogger.Assert(result case .S_OK);
_dragDropTarget = new .();
_dragDropTarget.Register();
}
public override static void Deinit()
{
_dragDropTarget.Unregister();
ReleaseRefAndNullify!(_dragDropTarget);
}
private static bool _isDragging;
public static void StartDragDrop()
{
if (_isDragging)
return;
_isDragging = true;
IDataObject* dataObject = null;
//HResult result = DoDragDrop(ref dataObject);
}
}
/// Registers a Windows drag'n'drop target that will fire Windows drop related events using the engine's event system.
class DragDropEventTranslator : IDropTargetImplBase
{
public override Result<DropEffect> OnDragEnter(int2 cursorPosition)
{
DragDropEvent event = scope DragDropEvent(.Enter, cursorPosition);
Application.Instance.OnEvent(event);
return event.OutDropEffect;
}
public override Result<DropEffect> OnDragOver(int2 cursorPosition)
{
DragDropEvent event = scope DragDropEvent(.Over, cursorPosition);
Application.Instance.OnEvent(event);
return event.OutDropEffect;
}
public override Result<void> OnDragLeave()
{
DragDropEvent event = scope DragDropEvent(.Leave, .(-1, -1));
Application.Instance.OnEvent(event);
return .Ok;
}
public override Result<DropEffect> OnDrop(int2 cursorPosition, Span<StringView> fileNames)
{
DragDropEvent event = scope DragDropEvent(.Drop, cursorPosition);
event.FileNames = fileNames;
Application.Instance.OnEvent(event);
return event.OutDropEffect;
}
}
#endif
+7
View File
@@ -32,6 +32,13 @@ namespace GlitchyEngine
value = null;
}
/// Releases the reference to value and nullifies it.
public static mixin ReleaseRefAndNullify<T>(T value) where T: RefCounted
{
value?.ReleaseRef();
value = null;
}
public static mixin DeleteContainerAndReleaseItems(var container)
{
if (container != null)