diff --git a/GlitchyEditor/src/EditorApp.bf b/GlitchyEditor/src/EditorApp.bf index fc3e29b..f6dc2e2 100644 --- a/GlitchyEditor/src/EditorApp.bf +++ b/GlitchyEditor/src/EditorApp.bf @@ -5,6 +5,8 @@ using GlitchyEditor.Assets; using GlitchyEditor.Assets.Importers; using GlitchyEditor.Assets.Processors; using GlitchyEditor.Assets.Exporters; +using DirectX.Common; +using GlitchyEditor.Platform.Windows; namespace GlitchyEditor { @@ -16,8 +18,18 @@ namespace GlitchyEditor { Log.ClientLogger = new EditorLogger(); Log.EngineLogger = new EditorLogger() { IsEngineLogger = true }; + + // TODO: Windows only + HResult result = OleInitialize(null); + Log.EngineLogger.Assert(result case .S_OK); PushLayer(new EditorLayer(args, _contentManager)); + + } + + public ~this() + { + OleUninitialize(); } protected override IContentManager InitContentManager() diff --git a/GlitchyEditor/src/EditorLayer.bf b/GlitchyEditor/src/EditorLayer.bf index 0060fb5..d0babcb 100644 --- a/GlitchyEditor/src/EditorLayer.bf +++ b/GlitchyEditor/src/EditorLayer.bf @@ -15,6 +15,7 @@ using System.IO; using GlitchyEngine.Core; using GlitchyEditor.Assets; using GlitchyEngine.Scripting; +using GlitchyEditor.Platform.Windows; namespace GlitchyEditor { @@ -115,13 +116,50 @@ 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 OnDragEnter(int2 cursorPosition) + { + DragDropEvent event = scope DragDropEvent(.Enter, cursorPosition); + Application.Instance.OnEvent(event); + return event.OutDropEffect; + } + + public override Result OnDragOver(int2 cursorPosition) + { + DragDropEvent event = scope DragDropEvent(.Over, cursorPosition); + Application.Instance.OnEvent(event); + return event.OutDropEffect; + } + + public override Result OnDragLeave() + { + DragDropEvent event = scope DragDropEvent(.Over, .(-1, -1)); + Application.Instance.OnEvent(event); + return .Ok; + } + + public override Result OnDrop(int2 cursorPosition) + { + DragDropEvent event = scope DragDropEvent(.Drop, cursorPosition); + Application.Instance.OnEvent(event); + return event.OutDropEffect; + } + } + + private GlobalDragDropTarget _dragDropTarget ~ _?.ReleaseRef(); + [AllowAppend] public this(String[] args, EditorContentManager contentManager) : base("Editor") { - Application.Get().Window.IsVSync = true; + Application.Instance.Window.IsVSync = true; ScriptEngine.ApplicationInfo.IsEditor = true; + _dragDropTarget = new .(); + _dragDropTarget.Register(); + _contentManager = contentManager; InitGraphics(); @@ -675,6 +713,13 @@ namespace GlitchyEditor { ImGui.TextUnformatted("Project Name:"); ImGui.InputText("##projectName", &projectNameBuffer, projectNameBuffer.Count - 1); + + StringView projectName = StringView(&projectNameBuffer); + + if (projectName.IsWhiteSpace) + { + ImGui.TextColored(.(1, 0, 0, 1), "Project Name required!"); + } ImGui.NewLine(); @@ -693,7 +738,6 @@ namespace GlitchyEditor } } - StringView projectName = StringView(&projectNameBuffer); StringView directory = StringView(&projectDirectoryBuffer); String target = scope String(); @@ -703,9 +747,9 @@ namespace GlitchyEditor ImGui.Text($"The Project will be in:\n{target}"); - if (Directory.Exists(target)) + if (Directory.Exists(target) && !Directory.IsEmpty(target)) { - ImGui.TextColored(.(1, 0, 0, 1), "The directory is not empty!"); + ImGui.TextColored(.(1, 1, 0, 1), "The directory is not empty!"); } ImGui.NewLine(); @@ -1521,6 +1565,7 @@ namespace GlitchyEditor dispatcher.Dispatch(scope (e) => OnWindowResize(e)); dispatcher.Dispatch(scope (e) => OnKeyPressed(e)); dispatcher.Dispatch(scope (e) => OnMouseScrolled(e)); + dispatcher.Dispatch(scope (e) => OnDragDrop(e)); } private bool OnWindowResize(WindowResizeEvent e) @@ -1611,6 +1656,31 @@ namespace GlitchyEditor return false; } + private bool _isDraggingFromOutside; + private bool _droppedFromOutside; + private DropEffect _effectWhenDropping = .None; + + private bool OnDragDrop(DragDropEvent e) + { + Log.EngineLogger.Info($"D'n'D: {e.DragDropType} {e.CursorPosition.X} {e.CursorPosition.Y}"); + + switch (e.DragDropType) + { + case .Enter: + _isDraggingFromOutside = true; + _droppedFromOutside = false; + case .Over: + case .Leave: + _isDraggingFromOutside = false; + case .Drop: + _droppedFromOutside = true; + } + + e.OutDropEffect = _effectWhenDropping; + + return true; + } + #endregion } } diff --git a/GlitchyEditor/src/Platform/Windows/DropTarget.bf b/GlitchyEditor/src/Platform/Windows/DropTarget.bf new file mode 100644 index 0000000..fd1d14a --- /dev/null +++ b/GlitchyEditor/src/Platform/Windows/DropTarget.bf @@ -0,0 +1,274 @@ +using System; +using DirectX.Common; +using DirectX.Math; +using GlitchyEngine; +using GlitchyEngine.Events; +using static System.Windows; +using GlitchyEngine.Math; + +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] + public static extern HResult OleInitialize(void* reserved); + [Import("Ole32.lib"), CLink] + public static extern void OleUninitialize(); + [Import("Ole32.lib"), CLink] + public static extern HResult RegisterDragDrop(HWnd windowHandle, IDropTarget* dropTarget); +} + +[CRepr] +struct IDropTarget : IUnknown +{ + public static new Guid IID => .("00000122-0000-0000-C000-000000000046"); + + [CRepr] + public struct VTable : IUnknown.VTable + { + public function [CallingConvention(.Stdcall)] HResult(IDropTarget* self, /*IDataObject*/ IUnknown* dataObject, uint32 grfKeyState, int2 point, ref DropEffect effect) DragEnter; + public function [CallingConvention(.Stdcall)] HResult(IDropTarget* self, uint32 grfKeyState, int2 point, ref DropEffect effect) DragOver; + public function [CallingConvention(.Stdcall)] HResult(IDropTarget* self) DragLeave; + public function [CallingConvention(.Stdcall)] HResult(IDropTarget* self, /*IDataObject*/ IUnknown* dataObject, uint32 grfKeyState, int2 point, ref DropEffect effect) Drop; + } + + public new VTable* VT => (VTable*)mVT; + + public HResult DragEnter(/*IDataObject*/ IUnknown* dataObject, uint32 grfKeyState, int2 point, ref DropEffect effect) mut + { + return VT.DragEnter(&this, dataObject, grfKeyState, point, ref effect); + } + + public HResult DragOver(uint32 grfKeyState, int2 point, ref DropEffect effect) mut + { + return VT.DragOver(&this, grfKeyState, point, ref effect); + } + + public HResult DragLeave() mut + { + return VT.DragLeave(&this); + } + + public HResult Drop(/*IDataObject*/ IUnknown* dataObject, uint32 grfKeyState, int2 point, ref DropEffect effect) mut + { + return VT.Drop(&this, dataObject, grfKeyState, point, ref effect); + } +} + +[CRepr] +enum DropEffect +{ + None = 0, + Copy = 1, + Move = 2, + Link = 4, + Scroll = 0x80000000, +} + +abstract class IDropTargetImplBase : RefCounted +{ + [CRepr] + private struct IDropTargetImpl : IDropTarget + { + public void* ClassPtr; + } + + IDropTargetImpl impl; + + IDropTarget.VTable vTable; + + public this() + { + impl = .(); + impl.[Friend]mVT = &vTable; + impl.ClassPtr = Internal.UnsafeCastToPtr(this); + + vTable.QueryInterface = => QueryInterfaceImpl; + vTable.AddRef = => AddRefImpl; + vTable.Release = => ReleaseImpl; + + vTable.DragEnter = => DragEnterImpl; + vTable.DragOver = => DragOverImpl; + vTable.DragLeave = => DragLeaveImpl; + vTable.Drop = => DropImpl; + } + + public void Register() + { + HResult result = RegisterDragDrop(Application.Instance.Window.[Friend]_windowHandle, &impl); + Log.EngineLogger.Assert(result not case .E_OUTOFMEMORY, "Failed to register drag drop handler (E_OUTOFMEMORY). Make sure you called OleInitialize and not CoInitialize[Ex]"); + Log.EngineLogger.Assert(result case .S_OK); + } + + private static IDropTargetImplBase GetInstance(IUnknown* self) + { + IDropTargetImpl* impl = (.)self; + + return (.)Internal.UnsafeCastToObject(impl.ClassPtr); + } + + private static HResult QueryInterfaceImpl(IUnknown* self, ref Guid riid, void** output) + { + Self instance = GetInstance(self); + + HResult result = .E_NOINTERFACE; + *output = null; + + if (riid == IUnknown.IID || riid == IDropTarget.IID) + { + *output = (IUnknown*)&instance.impl; + instance.AddRef(); + result = .S_OK; + } + + return result; + } + + private static uint32 AddRefImpl(IUnknown* self) + { + Self instance = GetInstance(self); + + instance.AddRef(); + return (uint32)instance.RefCount; + } + + private static uint32 ReleaseImpl(IUnknown* self) + { + Self instance = GetInstance(self); + + uint32 count = (uint32)instance.ReleaseRefNoDelete(); + + if (count == 0) + delete instance; + + return count; + } + + // TODO: Data object? KeyState? + public abstract Result OnDragEnter(int2 cursorPosition); + public abstract Result OnDragOver(int2 cursorPosition); + public abstract Result OnDragLeave(); + public abstract Result OnDrop(int2 cursorPosition); + + [CallingConvention(.Stdcall)] + private static HResult DragEnterImpl(IDropTarget* self, /*IDataObject*/ IUnknown* dataObject, uint32 grfKeyState, int2 point, ref DropEffect effect) + { + Self instance = GetInstance(self); + + Result result = instance.OnDragEnter(point); + + if (result case .Ok(out effect)) + return .S_OK; + + return .E_FAIL; + } + + [CallingConvention(.Stdcall)] + private static HResult DragOverImpl(IDropTarget* self, uint32 grfKeyState, int2 point, ref DropEffect effect) + { + Self instance = GetInstance(self); + Result result = instance.OnDragOver(point); + + if (result case .Ok(out effect)) + return .S_OK; + + return .E_FAIL; + } + + [CallingConvention(.Stdcall)] + private static HResult DragLeaveImpl(IDropTarget* self) + { + Self instance = GetInstance(self); + Result result = instance.OnDragLeave(); + + if (result case .Ok) + return .S_OK; + + return .E_FAIL; + } + + [CallingConvention(.Stdcall)] + private static HResult DropImpl(IDropTarget* self, /*IDataObject*/ IUnknown* dataObject, uint32 grfKeyState, int2 point, ref DropEffect effect) + { + Self instance = GetInstance(self); + Result result = instance.OnDrop(point); + + if (result case .Ok(out effect)) + return .S_OK; + + return .E_FAIL; + } +} + +struct S +{ + bool b1; + int32 i32; + bool b2; + int64 i64; + int32 i322; +} + +struct SB +{ + int64 i64; + int32 i32; + int32 i322; + bool b1; + bool b2; +} + +struct SC +{ + bool b1; + //filler 56 + int32 i32; + //filler 32 + bool b2; + //filler 56 + int64 i64; + int32 i322; + //filler 32 +} + +public enum DragDropType +{ + Enter, + Over, + Leave, + Drop +} + +public class DragDropEvent : Event, IEvent +{ + public override EventType EventType => .DragDropEvent; + + public override StringView Name => "DragDropEvent"; + + public override EventCategory Category => .Application; + + public static EventType StaticType => .DragDropEvent; + + public DragDropType DragDropType { get; private set; } + public int2 CursorPosition { get; private set; } + + public DropEffect OutDropEffect { get; set; } + + // TODO: Keys, or perhaps just make the listeners query them... + + public this(DragDropType dragDropType, int2 cursorPosition) + { + DragDropType = dragDropType; + CursorPosition = cursorPosition; + } +} + +namespace GlitchyEngine.Events +{ + public extension EventType + { + case DragDropEvent; + } +} diff --git a/GlitchyEngine/vendor/directx b/GlitchyEngine/vendor/directx index a973c39..65f5bbb 160000 --- a/GlitchyEngine/vendor/directx +++ b/GlitchyEngine/vendor/directx @@ -1 +1 @@ -Subproject commit a973c396eaebe3e1fe3c701babca883534f6d4b5 +Subproject commit 65f5bbbf01483f8c7699c8a1043cbbe6d6e16d9c