diff --git a/Source/Engine/Platform/Mac/MacWindow.cpp b/Source/Engine/Platform/Mac/MacWindow.cpp index 3129ef883..08a4e0d2f 100644 --- a/Source/Engine/Platform/Mac/MacWindow.cpp +++ b/Source/Engine/Platform/Mac/MacWindow.cpp @@ -6,6 +6,12 @@ #include "Engine/Platform/Apple/AppleUtils.h" #include "Engine/Platform/WindowsManager.h" #include "Engine/Platform/IGuiData.h" +#if USE_EDITOR +#include "Engine/Platform/CriticalSection.h" +#include "Engine/Threading/ThreadPoolTask.h" +#include "Engine/Threading/ThreadPool.h" +#include "Engine/Engine/Engine.h" +#endif #include "Engine/Core/Log.h" #include "Engine/Input/Input.h" #include "Engine/Input/Mouse.h" @@ -15,6 +21,29 @@ #include #include +#if USE_EDITOR +// Data for drawing window while doing drag&drop on Mac (engine is paused during platform tick) +CriticalSection MacDragLocker; +NSDraggingSession* MacDragSession = nullptr; +class DoDragDropJob* MacDragJob = nullptr; + +class DoDragDropJob : public ThreadPoolTask +{ +public: + int64 ExitFlag = 0; + + bool Run() override + { + while (Platform::AtomicRead(&ExitFlag) == 0) + { + Engine::OnDraw(); + Platform::Sleep(20); + } + return false; + } +}; +#endif + inline bool IsWindowInvalid(Window* win) { WindowsManager::WindowsLocker.Lock(); @@ -323,7 +352,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r) @end -@interface MacViewImpl : NSView +@interface MacViewImpl : NSView { MacWindow* Window; NSTrackingArea* TrackingArea; @@ -632,6 +661,34 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r) Window->OnDragLeave(); } +- (NSDragOperation)draggingSession:(NSDraggingSession*)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context +{ + if (IsWindowInvalid(Window)) return NSDragOperationNone; + return NSDragOperationMove; +} + +- (void)draggingSession:(NSDraggingSession*)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation +{ +#if USE_EDITOR + // Stop background worker once the drag ended + MacDragLocker.Lock(); + if (MacDragSession && MacDragSession == session) + { + Platform::AtomicStore(&MacDragJob->ExitFlag, 1); + MacDragJob->Wait(); + MacDragSession = nullptr; + MacDragJob = nullptr; + } + MacDragLocker.Unlock(); +#endif +} + +- (void)pasteboard:(nullable NSPasteboard*)pasteboard item:(NSPasteboardItem*)item provideDataForType:(NSPasteboardType)type +{ + if (IsWindowInvalid(Window)) return; + [pasteboard setString:(NSString*)AppleUtils::ToString(Window->GetDragText()) forType:NSPasteboardTypeString]; +} + @end MacWindow::MacWindow(const CreateWindowSettings& settings) @@ -682,6 +739,7 @@ MacWindow::MacWindow(const CreateWindowSettings& settings) [window setAcceptsMouseMovedEvents:YES]; [window setDelegate:window]; _window = window; + _view = view; if (settings.AllowDragAndDrop) { [view registerForDraggedTypes:@[NSPasteboardTypeFileURL, NSPasteboardTypeString]]; @@ -705,6 +763,7 @@ MacWindow::~MacWindow() [window close]; [window release]; _window = nullptr; + _view = nullptr; } void MacWindow::CheckForResize(float width, float height) @@ -938,8 +997,46 @@ void MacWindow::SetTitle(const StringView& title) DragDropEffect MacWindow::DoDragDrop(const StringView& data) { - // TODO: implement using beginDraggingSession and NSDraggingSource - return DragDropEffect::None; + NSWindow* window = (NSWindow*)_window; + MacViewImpl* view = (MacViewImpl*)_view; + _dragText = data; + + // Create mouse drag event + NSEvent* event = [NSEvent + mouseEventWithType:NSEventTypeLeftMouseDragged + location:window.mouseLocationOutsideOfEventStream + modifierFlags:0 + timestamp:NSApp.currentEvent.timestamp + windowNumber:window.windowNumber + context:nil + eventNumber:0 + clickCount:1 + pressure:1.0]; + + // Create drag item + NSPasteboardItem* pasteItem = [NSPasteboardItem new]; + [pasteItem setDataProvider:view forTypes:[NSArray arrayWithObjects:NSPasteboardTypeString, nil]]; + NSDraggingItem* dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:pasteItem]; + [dragItem setDraggingFrame:NSMakeRect(event.locationInWindow.x, event.locationInWindow.y, 1, 1) contents:nil]; + + // Start dragging session + NSDraggingSession* draggingSession = [view beginDraggingSessionWithItems:[NSArray arrayWithObject:dragItem] event:event source:view]; + DragDropEffect result = DragDropEffect::None; + +#if USE_EDITOR + // Create background worker that will keep updating GUI (perform rendering) + MacDragLocker.Lock(); + ASSERT(!MacDragSession && !MacDragJob); + MacDragSession = draggingSession; + MacDragJob = New(); + Task::StartNew(MacDragJob); + MacDragLocker.Unlock(); + while (MacDragJob->GetState() == TaskState::Queued) + Platform::Sleep(1); + // TODO: maybe wait for the drag end to return result? +#endif + + return result; } void MacWindow::SetCursor(CursorType type) diff --git a/Source/Engine/Platform/Mac/MacWindow.h b/Source/Engine/Platform/Mac/MacWindow.h index 8850f80ba..7c3f091b4 100644 --- a/Source/Engine/Platform/Mac/MacWindow.h +++ b/Source/Engine/Platform/Mac/MacWindow.h @@ -14,8 +14,10 @@ class FLAXENGINE_API MacWindow : public WindowBase { private: - void* _window; + void* _window = nullptr; + void* _view = nullptr; bool _isMouseOver = false; + String _dragText; public: @@ -24,6 +26,10 @@ public: void CheckForResize(float width, float height); void SetIsMouseOver(bool value); + const String& GetDragText() const + { + return _dragText; + } public: