Add drag&drop support to macOS

This commit is contained in:
Wojtek Figat
2023-10-21 15:36:38 +02:00
parent dcbc917b7d
commit 21f2e59d12
2 changed files with 107 additions and 4 deletions

View File

@@ -6,6 +6,12 @@
#include "Engine/Platform/Apple/AppleUtils.h" #include "Engine/Platform/Apple/AppleUtils.h"
#include "Engine/Platform/WindowsManager.h" #include "Engine/Platform/WindowsManager.h"
#include "Engine/Platform/IGuiData.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/Core/Log.h"
#include "Engine/Input/Input.h" #include "Engine/Input/Input.h"
#include "Engine/Input/Mouse.h" #include "Engine/Input/Mouse.h"
@@ -15,6 +21,29 @@
#include <AppKit/AppKit.h> #include <AppKit/AppKit.h>
#include <QuartzCore/CAMetalLayer.h> #include <QuartzCore/CAMetalLayer.h>
#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) inline bool IsWindowInvalid(Window* win)
{ {
WindowsManager::WindowsLocker.Lock(); WindowsManager::WindowsLocker.Lock();
@@ -323,7 +352,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
@end @end
@interface MacViewImpl : NSView @interface MacViewImpl : NSView <NSDraggingSource, NSPasteboardItemDataProvider>
{ {
MacWindow* Window; MacWindow* Window;
NSTrackingArea* TrackingArea; NSTrackingArea* TrackingArea;
@@ -632,6 +661,34 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
Window->OnDragLeave(); 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 @end
MacWindow::MacWindow(const CreateWindowSettings& settings) MacWindow::MacWindow(const CreateWindowSettings& settings)
@@ -682,6 +739,7 @@ MacWindow::MacWindow(const CreateWindowSettings& settings)
[window setAcceptsMouseMovedEvents:YES]; [window setAcceptsMouseMovedEvents:YES];
[window setDelegate:window]; [window setDelegate:window];
_window = window; _window = window;
_view = view;
if (settings.AllowDragAndDrop) if (settings.AllowDragAndDrop)
{ {
[view registerForDraggedTypes:@[NSPasteboardTypeFileURL, NSPasteboardTypeString]]; [view registerForDraggedTypes:@[NSPasteboardTypeFileURL, NSPasteboardTypeString]];
@@ -705,6 +763,7 @@ MacWindow::~MacWindow()
[window close]; [window close];
[window release]; [window release];
_window = nullptr; _window = nullptr;
_view = nullptr;
} }
void MacWindow::CheckForResize(float width, float height) void MacWindow::CheckForResize(float width, float height)
@@ -938,8 +997,46 @@ void MacWindow::SetTitle(const StringView& title)
DragDropEffect MacWindow::DoDragDrop(const StringView& data) DragDropEffect MacWindow::DoDragDrop(const StringView& data)
{ {
// TODO: implement using beginDraggingSession and NSDraggingSource NSWindow* window = (NSWindow*)_window;
return DragDropEffect::None; 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<DoDragDropJob>();
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) void MacWindow::SetCursor(CursorType type)

View File

@@ -14,8 +14,10 @@ class FLAXENGINE_API MacWindow : public WindowBase
{ {
private: private:
void* _window; void* _window = nullptr;
void* _view = nullptr;
bool _isMouseOver = false; bool _isMouseOver = false;
String _dragText;
public: public:
@@ -24,6 +26,10 @@ public:
void CheckForResize(float width, float height); void CheckForResize(float width, float height);
void SetIsMouseOver(bool value); void SetIsMouseOver(bool value);
const String& GetDragText() const
{
return _dragText;
}
public: public: