Implement client-side window decorations for Editor windows

This commit is contained in:
2025-07-03 20:19:49 +03:00
parent 6296e1a9eb
commit aab0d772a4
16 changed files with 638 additions and 370 deletions

View File

@@ -269,6 +269,11 @@ String PlatformBase::GetDisplayServer()
return String::Empty;
}
bool PlatformBase::SupportsNativeDecorations()
{
return true;
}
#endif
bool PlatformBase::Is64BitApp()

View File

@@ -374,6 +374,11 @@ public:
/// </summary>
API_PROPERTY() static String GetDisplayServer();
/// <summary>
/// Returns true if system provides decorations for windows.
/// </summary>
API_PROPERTY() static bool SupportsNativeDecorations();
/// <summary>
/// Returns true if is running 64 bit application (otherwise 32 bit). It's compile-time constant.
/// </summary>

View File

@@ -35,6 +35,12 @@
#include <wayland/xdg-shell.h>
#include <wayland/xdg-toplevel-drag-v1.h>
namespace SDLImpl
{
extern String WaylandDisplayEnv;
extern String XDGCurrentDesktop;
}
class LinuxDropFilesData : public IGuiData
{
public:
@@ -1611,8 +1617,7 @@ bool SDLPlatform::InitInternal()
if (!CommandLine::Options.Headless.IsTrue() && waylandRequested)
{
// Ignore in X11 session
String waylandDisplayEnv;
if (!GetEnvironmentVariable(String("WAYLAND_DISPLAY"), waylandDisplayEnv))
if (!SDLImpl::WaylandDisplayEnv.IsEmpty())
{
WaylandImpl::WaylandDisplay = (wl_display*)SDL_GetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, nullptr);
if (WaylandImpl::WaylandDisplay != nullptr)
@@ -1721,11 +1726,15 @@ DragDropEffect SDLWindow::DoDragDrop(const StringView& data, const Float2& offse
if (SDLPlatform::UsesWayland())
{
Float2 dragOffset = offset;
if (_settings.HasBorder && dragSourceWindow == this)
if (SDLPlatform::SupportsNativeDecorations() && _settings.HasBorder && dragSourceWindow == this)
{
// Wayland includes the decorations in the client-space coordinates, adjust the offset for it.
// Assume the title decoration is 25px thick...
// Assume the title decoration based on the current desktop environment...
float topOffset = 25.0f;
if (SDLImpl::XDGCurrentDesktop.Compare(String("KDE"), StringSearchCase::IgnoreCase) == 0)
topOffset = 25.0f;
else if (SDLImpl::XDGCurrentDesktop.Compare(String("GNOME"), StringSearchCase::IgnoreCase) == 0)
topOffset = 48.0f;
dragOffset += Float2(0.0f, topOffset);
}

View File

@@ -27,25 +27,41 @@
#define DefaultDPI 96
namespace
namespace SDLImpl
{
int32 SystemDpi = 96;
String UserLocale("en");
bool WindowDecorationsSupported = true;
String WaylandDisplayEnv;
String XDGCurrentDesktop;
}
bool SDLPlatform::Init()
{
#if PLATFORM_LINUX
bool waylandSession = false;
if (!GetEnvironmentVariable(String("WAYLAND_DISPLAY"), SDLImpl::WaylandDisplayEnv))
waylandSession = true;
GetEnvironmentVariable(String("XDG_CURRENT_DESKTOP"), SDLImpl::XDGCurrentDesktop);
if (CommandLine::Options.X11.IsTrue())
{
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "x11", SDL_HINT_OVERRIDE);
waylandSession = false;
}
else if (CommandLine::Options.Wayland.IsTrue())
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
else
else if (waylandSession)
{
// Override the X11 preference when running in Wayland session
String waylandDisplayEnv;
if (!GetEnvironmentVariable(String("WAYLAND_DISPLAY"), waylandDisplayEnv))
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
}
// Workaround for libdecor in Gnome+Wayland causing freezes when interacting with the native decorations
if (waylandSession && SDLImpl::XDGCurrentDesktop.Compare(String("GNOME"), StringSearchCase::IgnoreCase) == 0)
{
SDL_SetHint(SDL_HINT_VIDEO_WAYLAND_ALLOW_LIBDECOR, "0");
SDLImpl::WindowDecorationsSupported = false;
}
#endif
@@ -95,9 +111,9 @@ bool SDLPlatform::Init()
if (language.StartsWith("en"))
{
if (country != nullptr)
UserLocale = String::Format(TEXT("{0}-{1}"), String(language), String(locales[i]->country));
SDLImpl::UserLocale = String::Format(TEXT("{0}-{1}"), String(language), String(locales[i]->country));
else
UserLocale = String(language);
SDLImpl::UserLocale = String(language);
break;
}
}
@@ -124,7 +140,7 @@ bool SDLPlatform::Init()
SDLInput::Init();
SDLWindow::Init();
SystemDpi = (int)(SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()) * DefaultDPI);
SDLImpl::SystemDpi = (int)(SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()) * DefaultDPI);
//SDL_StartTextInput(); // TODO: Call this only when text input is expected (shows virtual keyboard in some cases)
@@ -183,6 +199,11 @@ String SDLPlatform::GetDisplayServer()
#endif
}
bool SDLPlatform::SupportsNativeDecorations()
{
return SDLImpl::WindowDecorationsSupported;
}
BatteryInfo SDLPlatform::GetBatteryInfo()
{
BatteryInfo info;
@@ -213,12 +234,12 @@ BatteryInfo SDLPlatform::GetBatteryInfo()
int32 SDLPlatform::GetDpi()
{
return SystemDpi;
return SDLImpl::SystemDpi;
}
String SDLPlatform::GetUserLocaleName()
{
return UserLocale;
return SDLImpl::UserLocale;
}
void SDLPlatform::OpenUrl(const StringView& url)

View File

@@ -74,6 +74,7 @@ public:
static void LogInfo();
static void Tick();
static String GetDisplayServer();
static bool SupportsNativeDecorations();
static void SetHighDpiAwarenessEnabled(bool enable);
static BatteryInfo GetBatteryInfo();
static int32 GetDpi();

View File

@@ -25,7 +25,6 @@
#define NOGDI
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_hints.h>
#include <SDL3/SDL_properties.h>
#include <SDL3/SDL_video.h>
#undef CreateWindow
@@ -45,12 +44,12 @@ static_assert(false, "Unsupported Platform");
#define DefaultDPI 96
namespace WindowImpl
namespace SDLImpl
{
SDLWindow* LastEventWindow = nullptr;
static SDL_Cursor* Cursors[SDL_SYSTEM_CURSOR_COUNT] = { nullptr };
SDL_Cursor* Cursors[SDL_SYSTEM_CURSOR_COUNT] = { nullptr };
extern String XDGCurrentDesktop;
}
using namespace WindowImpl;
SDL_HitTestResult OnWindowHitTest(SDL_Window* win, const SDL_Point* area, void* data);
void GetRelativeWindowOffset(WindowType type, SDLWindow* parentWindow, Int2& positionOffset);
@@ -208,7 +207,7 @@ SDLWindow::SDLWindow(const CreateWindowSettings& settings)
}
#endif
LastEventWindow = this;
SDLImpl::LastEventWindow = this;
#if PLATFORM_LINUX
// Initialize using the shared Display instance from SDL
@@ -263,8 +262,8 @@ void* SDLWindow::GetX11Display() const
SDLWindow::~SDLWindow()
{
if (LastEventWindow == this)
LastEventWindow = nullptr;
if (SDLImpl::LastEventWindow == this)
SDLImpl::LastEventWindow = nullptr;
if (_window == nullptr)
return;
@@ -281,45 +280,48 @@ SDLWindow::~SDLWindow()
_visible = false;
}
WindowHitCodes SDLWindow::OnWindowHit(const Float2 point)
{
WindowHitCodes hit = WindowHitCodes::Client;
if (!IsFullscreen())
{
Float2 screenPosition = ClientToScreen(point);
bool handled = false;
OnHitTest(screenPosition, hit, handled);
if (!handled)
{
int margin = _settings.HasBorder ? 0 : 0;
auto size = GetClientSize();
//if (point.Y < 0)
// hit = WindowHitCodes::Caption;
if (point.Y < margin && point.X < margin)
hit = WindowHitCodes::TopLeft;
else if (point.Y < margin && point.X > size.X - margin)
hit = WindowHitCodes::TopRight;
else if (point.Y < margin)
hit = WindowHitCodes::Top;
else if (point.X < margin && point.Y > size.Y - margin)
hit = WindowHitCodes::BottomLeft;
else if (point.X < margin)
hit = WindowHitCodes::Left;
else if (point.X > size.X - margin && point.Y > size.Y - margin)
hit = WindowHitCodes::BottomRight;
else if (point.X > size.X - margin)
hit = WindowHitCodes::Right;
else if (point.Y > size.Y - margin)
hit = WindowHitCodes::Bottom;
else
hit = WindowHitCodes::Client;
}
}
return hit;
}
SDL_HitTestResult OnWindowHitTest(SDL_Window* win, const SDL_Point* area, void* data)
{
SDLWindow* window = (SDLWindow*)data;
if (window->IsFullscreen())
return SDL_HITTEST_NORMAL;
Float2 clientPosition = Float2(static_cast<float>(area->x), static_cast<float>(area->y));
Float2 screenPosition = window->ClientToScreen(clientPosition);
WindowHitCodes hit = WindowHitCodes::Client;
bool handled = false;
window->OnHitTest(screenPosition, hit, handled);
if (!handled)
{
int margin = window->GetSettings().HasBorder ? 0 : 0;
auto size = window->GetClientSize();
//if (clientPosition.Y < 0)
// return SDL_HITTEST_DRAGGABLE;
if (clientPosition.Y < margin && clientPosition.X < margin)
return SDL_HITTEST_RESIZE_TOPLEFT;
else if (clientPosition.Y < margin && clientPosition.X > size.X - margin)
return SDL_HITTEST_RESIZE_TOPRIGHT;
else if (clientPosition.Y < margin)
return SDL_HITTEST_RESIZE_TOP;
else if (clientPosition.X < margin && clientPosition.Y > size.Y - margin)
return SDL_HITTEST_RESIZE_BOTTOMLEFT;
else if (clientPosition.X < margin)
return SDL_HITTEST_RESIZE_LEFT;
else if (clientPosition.X > size.X - margin && clientPosition.Y > size.Y - margin)
return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
else if (clientPosition.X > size.X - margin)
return SDL_HITTEST_RESIZE_RIGHT;
else if (clientPosition.Y > size.Y - margin)
return SDL_HITTEST_RESIZE_BOTTOM;
else
return SDL_HITTEST_NORMAL;
}
SDLWindow* window = static_cast<SDLWindow*>(data);
const Float2 point(static_cast<float>(area->x), static_cast<float>(area->y));
WindowHitCodes hit = window->OnWindowHit(point);
switch (hit)
{
case WindowHitCodes::Caption:
@@ -350,9 +352,9 @@ SDLWindow* SDLWindow::GetWindowFromEvent(const SDL_Event& event)
SDL_Window* window = SDL_GetWindowFromEvent(&event);
if (window == nullptr)
return nullptr;
if (LastEventWindow == nullptr || window != LastEventWindow->_window)
LastEventWindow = GetWindowWithSDLWindow(window);
return LastEventWindow;
if (SDLImpl::LastEventWindow == nullptr || window != SDLImpl::LastEventWindow->_window)
SDLImpl::LastEventWindow = GetWindowWithSDLWindow(window);
return SDLImpl::LastEventWindow;
}
SDLWindow* SDLWindow::GetWindowWithSDLWindow(SDL_Window* window)
@@ -779,8 +781,8 @@ Float2 SDLWindow::ClientToScreen(const Float2& clientPos) const
void SDLWindow::FlashWindow()
{
#if PLATFORM_LINUX
// Flashing brings the window on top of other windows, disable it for now
if (SDLPlatform::UsesWayland())
// KDE bug: flashing brings the window on top of other windows, disable it for now...
if (SDLPlatform::UsesWayland() && SDLImpl::XDGCurrentDesktop.Compare(String("KDE"), StringSearchCase::IgnoreCase) == 0)
return;
#endif
SDL_FlashWindow(_window, SDL_FLASH_UNTIL_FOCUSED);
@@ -989,9 +991,9 @@ void SDLWindow::UpdateCursor()
break;
}
if (Cursors[index] == nullptr)
Cursors[index] = SDL_CreateSystemCursor(static_cast<SDL_SystemCursor>(index));
SDL_SetCursor(Cursors[index]);
if (SDLImpl::Cursors[index] == nullptr)
SDLImpl::Cursors[index] = SDL_CreateSystemCursor(static_cast<SDL_SystemCursor>(index));
SDL_SetCursor(SDLImpl::Cursors[index]);
}
void SDLWindow::SetIcon(TextureData& icon)

View File

@@ -69,6 +69,7 @@ public:
void* GetWaylandDisplay() const;
void* GetX11Display() const;
#endif
WindowHitCodes OnWindowHit(const Float2 point);
// [WindowBase]
void* GetNativePtr() const override;

View File

@@ -10,13 +10,16 @@
#if USE_EDITOR
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Log.h"
#include "Engine/Platform/IGuiData.h"
#include "Engine/Platform/Base/DragDropHelper.h"
#include "Engine/Input/Input.h"
#include "Engine/Input/Mouse.h"
#endif
#include "../Win32/IncludeWindowsHeaders.h"
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
#include <propidl.h>
#if USE_EDITOR
#include <oleidl.h>
#include <shellapi.h>