Implement new window dragging system

This commit is contained in:
2025-01-21 20:14:44 +02:00
parent b91837e783
commit 2944c640ca
23 changed files with 6250 additions and 627 deletions

View File

@@ -33,8 +33,23 @@
#endif
#elif PLATFORM_LINUX
#include "Engine/Platform/Linux/IncludeX11.h"
#include "Engine/Core/Collections/Dictionary.h"
#include <wayland/xdg-toplevel-drag-v1.h>
#include <wayland/xdg-shell.h>
extern Dictionary<wl_surface*, SDLWindow*> SurfaceToWindowMap;
extern SDLWindow* LastPointerWindow;
extern Int2 LastPointerPosition;
extern uint32 ImplicitGrabSerial;
extern xdg_toplevel_drag_manager_v1* DragManager;
extern wl_seat* WaylandSeat;
extern wl_data_device_manager* WaylandDataDeviceManager;
extern xdg_wm_base* WaylandXdgWmBase;
extern bool waylandDraggingActive;
#endif
extern Window* draggedWindow;
#define DefaultDPI 96
namespace
@@ -52,6 +67,11 @@ void GetRelativeWindowOffset(WindowType type, SDLWindow* parentWindow, Int2& pos
Int2 GetSDLWindowScreenPosition(const SDLWindow* window);
void SetSDLWindowScreenPosition(const SDLWindow* window, const int x, const int y);
bool IsPopupWindow(WindowType type)
{
return type == WindowType::Popup || type == WindowType::Tooltip;
}
class SDLDropFilesData : public IGuiData
{
public:
@@ -214,9 +234,9 @@ void* GetNativeWindowPointer(SDL_Window* window)
#if PLATFORM_WINDOWS
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
#elif PLATFORM_LINUX
windowPtr = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr);
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr);
if (windowPtr == nullptr)
windowPtr = (void*)SDL_GetNumberProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
windowPtr = (void*)SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
#elif PLATFORM_MAC
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
#elif PLATFORM_ANDROID
@@ -266,6 +286,9 @@ SDLWindow::~SDLWindow()
if (_window == nullptr)
return;
#if PLATFORM_LINUX
SurfaceToWindowMap.RemoveValue(this);
#endif
SDL_StopTextInput(_window);
SDL_DestroyWindow(_window);
@@ -419,8 +442,14 @@ void SDLWindow::HandleEvent(SDL_Event& event)
// X11 doesn't report any mouse events when mouse is over the caption area, send a simulated event instead...
Float2 mousePosition;
auto buttons = SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y);
if ((buttons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)) != 0)
SDLPlatform::CheckWindowDragging(this, WindowHitCodes::Caption);
if ((buttons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)) != 0 && draggedWindow == nullptr)
{
// TODO: verify mouse position, window focus
bool result = false;
OnLeftButtonHit(WindowHitCodes::Caption, result);
if (result)
draggedWindow = this;
}
}
#endif
return;
@@ -543,10 +572,49 @@ void SDLWindow::HandleEvent(SDL_Event& event)
}
return;
}
#if false
case SDL_EVENT_DROP_BEGIN:
#if true
case SDL_EVENT_CLIPBOARD_UPDATE:
{
Focus();
LOG(Info, "SDL_EVENT_CLIPBOARD_UPDATE");
return;
}
case SDL_EVENT_DROP_BEGIN:
case SDL_EVENT_DROP_POSITION:
case SDL_EVENT_DROP_FILE:
case SDL_EVENT_DROP_TEXT:
case SDL_EVENT_DROP_COMPLETE:
{
//LOG(Info, "SDL_EVENT_DROP_BEGIN");
static Float2 dragStartPosition = Float2::Zero;
auto dpiScale = GetDpiScale();
//const Float2 mousePos = ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale});
const Float2 mousePos = dragStartPosition + Float2(event.drop.x * dpiScale, event.drop.y * dpiScale);
DragDropEffect effect = DragDropEffect::None;
auto daata = event.drop.data;
SDLDropTextData dropData;
if (event.type == SDL_EVENT_DROP_BEGIN)
{
dragStartPosition = Platform::GetMousePosition();
OnDragEnter(&dropData, mousePos, effect);
}
else if (event.type == SDL_EVENT_DROP_POSITION)
{
Input::Mouse->OnMouseMove(mousePos, this);
OnDragOver(&dropData, mousePos, effect);
}
else if (event.type == SDL_EVENT_DROP_FILE)
OnDragDrop(&dropData, mousePos, effect);
else if (event.type == SDL_EVENT_DROP_TEXT)
OnDragDrop(&dropData, mousePos, effect);
else if (event.type == SDL_EVENT_DROP_COMPLETE)
OnDragLeave();
/*Focus();
Float2 mousePosition;
SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y);
mousePosition = ScreenToClient(mousePosition);
@@ -554,49 +622,68 @@ void SDLWindow::HandleEvent(SDL_Event& event)
DragDropEffect effect;
SDLDropTextData dropData;
OnDragEnter(&dropData, mousePosition, effect);
OnDragOver(&dropData, mousePosition, effect);
return;
OnDragOver(&dropData, mousePosition, effect);*/
break;
}
case SDL_EVENT_DROP_POSITION:
/*case SDL_EVENT_DROP_POSITION:
{
auto dpiScale = GetDpiScale();
//const Float2 mousePos = ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale});
const Float2 mousePos(event.drop.x * dpiScale, event.drop.y * dpiScale);
DragDropEffect effect = DragDropEffect::None;
auto daata = event.drop.data;
SDLDropTextData dropData;
OnDragOver(&dropData, Float2(static_cast<float>(event.drop.x), static_cast<float>(event.drop.y)), effect);
return;
OnDragOver(&dropData, mousePos, effect);
break;
}
case SDL_EVENT_DROP_FILE:
{
SDLDropFilesData dropData;
dropData.Files.Add(StringAnsi(event.drop.data).ToString()); // TODO: collect multiple files at once?
Focus();
Float2 mousePosition;
SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y);
mousePosition = ScreenToClient(mousePosition);
LOG(Info, "SDL_EVENT_DROP_FILE");
auto dpiScale = GetDpiScale();
const Float2 mousePos = ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale});
DragDropEffect effect = DragDropEffect::None;
OnDragDrop(&dropData, mousePosition, effect);
auto daata = event.drop.data;
SDLDropTextData dropData;
OnDragDrop(&dropData, mousePos, effect);
return;
}
case SDL_EVENT_DROP_TEXT:
{
SDLDropTextData dropData;
String str = StringAnsi(event.drop.data).ToString();
dropData.Text = StringView(str);
Focus();
Float2 mousePosition;
SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y);
mousePosition = ScreenToClient(mousePosition);
auto dpiScale = GetDpiScale();
const Float2 mousePos = ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale});
DragDropEffect effect = DragDropEffect::None;
OnDragDrop(&dropData, mousePosition, effect);
auto daata = event.drop.data;
auto str = String(event.drop.data);
LOG(Info, "SDL_EVENT_DROP_TEXT: {}", str);
SDLDropTextData dropData;
OnDragDrop(&dropData, mousePos, effect);
return;
}
case SDL_EVENT_DROP_COMPLETE:
{
LOG(Info, "SDL_EVENT_DROP_COMPLETE");
auto dpiScale = GetDpiScale();
const Float2 mousePos = ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale});
DragDropEffect effect = DragDropEffect::None;
auto daata = event.drop.data;
OnDragLeave();
if (SDLPlatform::UsesWayland())
{
//_dragOver = false;
}
return;
}
}*/
#endif
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
{
@@ -605,6 +692,44 @@ void SDLWindow::HandleEvent(SDL_Event& event)
#endif
break;
}
//#if PLATFORM_LINUX
case SDL_EVENT_MOUSE_BUTTON_DOWN:
{
LOG(Info, "SDL_EVENT_MOUSE_BUTTON_DOWN");
break;
}
case SDL_EVENT_MOUSE_BUTTON_UP:
{
#if PLATFORM_LINUX
if (SDLPlatform::UsesWayland() && waylandDraggingActive)
{
LOG(Info, "SDL_EVENT_MOUSE_BUTTON_UP, dragging");
// We are dragging a window, keep the button held down
return;
}
else
#endif
{
LOG(Info, "SDL_EVENT_MOUSE_BUTTON_UP: {}", GetTitle());
#if PLATFORM_WINDOWS
if (draggedWindow != nullptr && draggedWindow->_windowId != event.button.windowID)
{
// Send the button event to dragged window as well
Float2 mousePos = ClientToScreen({ event.button.x, event.button.y });
Float2 clientPos = draggedWindow->ScreenToClient(mousePos);
SDL_Event event2 = event;
event2.button.windowID = draggedWindow->_windowId;
event2.button.x = clientPos.X;
event2.button.y = clientPos.Y;
SDLInput::HandleEvent(draggedWindow, event2);
}
#endif
}
break;
}
//#endif
default:
break;
}
@@ -639,9 +764,9 @@ void SDLWindow::Show()
else if (_settings.Parent == nullptr)
BringToFront();
// Reused top-most windows (DockHintWindow) doesn't stay on top for some reason
if (_settings.IsTopmost && _settings.Type != WindowType::Tooltip)
SDL_SetWindowAlwaysOnTop(_window, true);
// Reused top-most windows doesn't stay on top for some reason
if (_settings.IsTopmost && !IsPopupWindow(_settings.Type))
SetIsAlwaysOnTop(true);
if (_isTrackingMouse)
{
@@ -662,6 +787,11 @@ void SDLWindow::Hide()
SDL_HideWindow(_window);
#if PLATFORM_LINUX
//if (SDLPlatform::UsesWayland() && _dragOver)
// StopDragging();
#endif
WindowBase::Hide();
}
@@ -760,10 +890,20 @@ bool SDLWindow::IsForegroundWindow() const
void SDLWindow::BringToFront(bool force)
{
#if PLATFORM_WINDOWS // FIXME
auto activateWhenRaised = SDL_GetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED);
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, "0");
SDL_RaiseWindow(_window);
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, activateWhenRaised);
#endif
if (SDLPlatform::UsesX11())
{
auto activateWhenRaised = SDL_GetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED);
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, "0");
SDL_RaiseWindow(_window);
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, activateWhenRaised);
SDL_SyncWindow(_window);
}
}
void SDLWindow::SetClientBounds(const Rectangle& clientArea)
@@ -777,11 +917,6 @@ void SDLWindow::SetClientBounds(const Rectangle& clientArea)
SDL_SetWindowSize(_window, newW, newH);
}
bool IsPopupWindow(WindowType type)
{
return type == WindowType::Popup || type == WindowType::Tooltip;
}
void GetRelativeWindowOffset(WindowType type, SDLWindow* parentWindow, Int2& positionOffset)
{
if (!IsPopupWindow(type))
@@ -848,7 +983,7 @@ void SDLWindow::SetIsFullscreen(bool isFullscreen)
if (!isFullscreen)
{
// The window is set to always-on-top for some reason when leaving fullscreen
SDL_SetWindowAlwaysOnTop(_window, false);
SetIsAlwaysOnTop(false);
}
WindowBase::SetIsFullscreen(isFullscreen);
@@ -1116,5 +1251,68 @@ void SDLWindow::UpdateCursor()
SDL_SetCursor(Cursors[index]);
}
//bool draggingActive = false;
DragDropEffect SDLWindow::DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow)
{
// TODO: this needs to be non-blocking in all platforms
LOG(Info, "StartDragging {}", offset);
Float2 dragOffset = offset;
if (_settings.HasBorder)
{
#if PLATFORM_LINUX
if (SDLPlatform::UsesWayland() && dragSourceWindow == this)
{
// Wayland includes the decorations in the client-space coordinates, adjust the offset for it.
// Assume the title decoration is 25px thick...
float topOffset = 25.0f;
dragOffset += Float2(0.0f, topOffset);
}
#endif
}
#if PLATFORM_LINUX
if (SDLPlatform::UsesWayland())
DoDragDropWayland(String("notawindow"), dragSourceWindow, dragOffset);
else
#endif
{
Show();
//draggingActive = true;
/*auto watch = [](void* userdata, SDL_Event* event) -> bool
{
if (event->window.type == SDL_EVENT_WINDOW_EXPOSED)
{
LOG(Info, "exposedo");
}
else
LOG(Info, "eventy");*/
/*SDLPlatform::Tick();
Engine::OnUpdate();//Scripting::Update(); // For docking updates
Engine::OnDraw();*//*
return true;
};
SDL_AddEventWatch(watch, nullptr);*/
/*while (draggingActive)
{
SDLPlatform::Tick();
Engine::OnUpdate();//Scripting::Update(); // For docking updates
Engine::OnDraw();
Platform::Sleep(1);
}*/
//SDL_RemoveEventWatch(watch, nullptr);
// The mouse up event was ignored earlier, release the button now
//Input::Mouse->OnMouseUp(Platform::GetMousePosition(), MouseButton::Left, this);
}
return DragDropEffect::None;
}
#endif