Fix various issues with child window positioning

This commit is contained in:
2024-12-25 19:45:10 +02:00
committed by Ari Vuollet
parent df02c70e31
commit 188e4313f9
8 changed files with 185 additions and 190 deletions

View File

@@ -261,6 +261,7 @@ namespace FlaxEditor.GUI.ContextMenu
desc.IsTopmost = true; desc.IsTopmost = true;
desc.Type = WindowType.Popup; desc.Type = WindowType.Popup;
desc.Parent = parentWin.Window; desc.Parent = parentWin.Window;
desc.Title = "ContextMenu";
desc.HasSizingFrame = false; desc.HasSizingFrame = false;
OnWindowCreating(ref desc); OnWindowCreating(ref desc);
_window = Platform.CreateWindow(ref desc); _window = Platform.CreateWindow(ref desc);

View File

@@ -364,7 +364,10 @@ public:
public: public:
Float2 GetMousePosition() const /// <summary>
/// Returns the previous known position of the mouse before entering relative mode.
/// </summary>
Float2 GetOldMousePosition() const
{ {
return oldPosition; return oldPosition;
} }
@@ -509,7 +512,7 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event)
{ {
// Use the previous visible mouse position here, the event or global // Use the previous visible mouse position here, the event or global
// mouse position would cause input to trigger in other editor windows. // mouse position would cause input to trigger in other editor windows.
mousePos = SDLInputImpl::Mouse->GetMousePosition(); mousePos = SDLInputImpl::Mouse->GetOldMousePosition();
} }
if (!event.button.down) if (!event.button.down)
@@ -531,7 +534,7 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event)
{ {
// Use the previous visible mouse position here, the event or global // Use the previous visible mouse position here, the event or global
// mouse position would cause input to trigger in other editor windows. // mouse position would cause input to trigger in other editor windows.
mousePos = SDLInputImpl::Mouse->GetMousePosition(); mousePos = SDLInputImpl::Mouse->GetOldMousePosition();
} }
Input::Mouse->OnMouseWheel(mousePos, delta, window); Input::Mouse->OnMouseWheel(mousePos, delta, window);

View File

@@ -918,11 +918,6 @@ bool SDLPlatform::UsesWayland()
return UseWayland; return UseWayland;
} }
bool SDLPlatform::UsesXWayland()
{
return false;
}
bool SDLPlatform::UsesX11() bool SDLPlatform::UsesX11()
{ {
return !UseWayland; return !UseWayland;

View File

@@ -64,13 +64,17 @@ bool SDLPlatform::Init()
SDL_SetHint(SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE, "0"); SDL_SetHint(SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE, "0");
SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "0"); // Already handled during platform initialization SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "0"); // Already handled during platform initialization
SDL_SetHint("SDL_BORDERLESS_RESIZABLE_STYLE", "1"); // Allow borderless windows to be resizable on Windows SDL_SetHint("SDL_BORDERLESS_RESIZABLE_STYLE", "1"); // Allow borderless windows to be resizable on Windows
//SDL_SetHint("SDL_BORDERLESS_WINDOWED_STYLE", "1");
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION, "0"); SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION, "0");
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, "1"); // Needed for tracking mode SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, "1"); // Needed for tracking mode
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, "1"); SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, "1"); // Is this needed?
//SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1"); // Disables raw mouse input //SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1"); // Disables raw mouse input
SDL_SetHint(SDL_HINT_WINDOWS_RAW_KEYBOARD, "1"); SDL_SetHint(SDL_HINT_WINDOWS_RAW_KEYBOARD, "1");
SDL_SetHint(SDL_HINT_VIDEO_WAYLAND_SCALE_TO_DISPLAY, "1");
// Disable SDL clipboard support // Disable SDL clipboard support
SDL_SetEventEnabled(SDL_EVENT_CLIPBOARD_UPDATE, false); SDL_SetEventEnabled(SDL_EVENT_CLIPBOARD_UPDATE, false);
@@ -316,11 +320,6 @@ bool SDLPlatform::UsesWayland()
return false; return false;
} }
bool SDLPlatform::UsesXWayland()
{
return false;
}
bool SDLPlatform::UsesX11() bool SDLPlatform::UsesX11()
{ {
return false; return false;

View File

@@ -58,7 +58,6 @@ public:
static void* GetXDisplay(); static void* GetXDisplay();
#endif #endif
static bool UsesWayland(); static bool UsesWayland();
static bool UsesXWayland();
static bool UsesX11(); static bool UsesX11();
public: public:

View File

@@ -26,6 +26,8 @@
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h" #include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
#define STYLE_RESIZABLE (WS_THICKFRAME | WS_MAXIMIZEBOX)
#define BORDERLESS_MAXIMIZE_WORKAROUND 2
#if USE_EDITOR #if USE_EDITOR
#include <oleidl.h> #include <oleidl.h>
#endif #endif
@@ -39,10 +41,16 @@ namespace
{ {
SDLWindow* LastEventWindow = nullptr; SDLWindow* LastEventWindow = nullptr;
static SDL_Cursor* Cursors[SDL_SYSTEM_CURSOR_COUNT] = { nullptr }; static SDL_Cursor* Cursors[SDL_SYSTEM_CURSOR_COUNT] = { nullptr };
#if BORDERLESS_MAXIMIZE_WORKAROUND == 2
int SkipMaximizeEventsCount = 0;
#endif
} }
void* GetNativeWindowPointer(SDL_Window* window); void* GetNativeWindowPointer(SDL_Window* window);
SDL_HitTestResult OnWindowHitTest(SDL_Window* win, const SDL_Point* area, void* data); SDL_HitTestResult OnWindowHitTest(SDL_Window* win, const SDL_Point* area, void* data);
void GetRelativeWindowOffset(WindowType type, SDLWindow* parentWindow, Int2& positionOffset);
Int2 GetSDLWindowScreenPosition(const SDLWindow* window);
void SetSDLWindowScreenPosition(const SDLWindow* window, const int x, const int y);
class SDLDropFilesData : public IGuiData class SDLDropFilesData : public IGuiData
{ {
@@ -86,17 +94,11 @@ SDLWindow::SDLWindow(const CreateWindowSettings& settings)
, _handle(nullptr) , _handle(nullptr)
, _cachedClientRectangle(Rectangle()) , _cachedClientRectangle(Rectangle())
#if PLATFORM_LINUX #if PLATFORM_LINUX
, _forcedFocus(false)
, _dragOver(false) , _dragOver(false)
#endif #endif
{ {
int32 x = Math::TruncToInt(settings.Position.X); Int2 clientSize(Math::TruncToInt(settings.Size.X), Math::TruncToInt(settings.Size.Y));
int32 y = Math::TruncToInt(settings.Position.Y); _clientSize = Float2(clientSize);
int32 clientWidth = Math::TruncToInt(settings.Size.X);
int32 clientHeight = Math::TruncToInt(settings.Size.Y);
int32 windowWidth = clientWidth;
int32 windowHeight = clientHeight;
_clientSize = Float2((float)clientWidth, (float)clientHeight);
if (SDLPlatform::UsesWayland()) if (SDLPlatform::UsesWayland())
{ {
@@ -124,56 +126,22 @@ SDLWindow::SDLWindow(const CreateWindowSettings& settings)
flags |= SDL_WINDOW_ALWAYS_ON_TOP; flags |= SDL_WINDOW_ALWAYS_ON_TOP;
if (_settings.SupportsTransparency) if (_settings.SupportsTransparency)
flags |= SDL_WINDOW_TRANSPARENT; flags |= SDL_WINDOW_TRANSPARENT;
//flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY;
if (_settings.Parent == nullptr && (_settings.Type == WindowType::Tooltip || _settings.Type == WindowType::Popup)) // Disable parenting of child windows as those are always on top of the parent window and never show up in taskbar
{ //if (_settings.Parent != nullptr && (_settings.Type != WindowType::Tooltip && _settings.Type != WindowType::Popup))
// Creating a popup window on some platforms brings the parent window on top. // _settings.Parent = nullptr;
// Use the currently focused window as the parent instead to avoid losing focus of it
WindowsManager::WindowsLocker.Lock();
for (auto win : WindowsManager::Windows)
{
if (win->IsForegroundWindow())
{
if (win->_settings.Type == WindowType::Tooltip || win->_settings.Type == WindowType::Popup)
{
auto focusedParent = win->_settings.Parent;
while (focusedParent != nullptr)
{
if (focusedParent->_settings.Parent == nullptr)
{
_settings.Parent = focusedParent;
break;
}
focusedParent = focusedParent->_settings.Parent;
}
}
else
_settings.Parent = win;
break;
}
}
WindowsManager::WindowsLocker.Unlock();
if (_settings.Parent == nullptr) // The window position needs to be relative to the parent window
_settings.Parent = Engine::MainWindow; Int2 relativePosition(Math::TruncToInt(settings.Position.X), Math::TruncToInt(settings.Position.Y));
} GetRelativeWindowOffset(_settings.Type, _settings.Parent, relativePosition);
// The SDL window position is always relative to the parent window
if (_settings.Parent != nullptr)
{
auto parentPosition = _settings.Parent->ClientToScreen(Float2::Zero);
x -= Math::TruncToInt(parentPosition.X);
y -= Math::TruncToInt(parentPosition.Y);
}
SDL_PropertiesID props = SDL_CreateProperties(); SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags);
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, settings.Title.ToStringAnsi().Get()); SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, settings.Title.ToStringAnsi().Get());
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, x); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, relativePosition.X);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, y); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, relativePosition.Y);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, windowWidth); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, clientSize.X);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, windowHeight); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, clientSize.Y);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN, true); SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN, true);
if ((flags & SDL_WINDOW_TOOLTIP) != 0) if ((flags & SDL_WINDOW_TOOLTIP) != 0)
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN, true); SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN, true);
@@ -183,6 +151,7 @@ SDLWindow::SDLWindow(const CreateWindowSettings& settings)
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, _settings.Parent->_window); SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, _settings.Parent->_window);
_window = SDL_CreateWindowWithProperties(props); _window = SDL_CreateWindowWithProperties(props);
SDL_DestroyProperties(props);
if (_window == nullptr) if (_window == nullptr)
Platform::Fatal(String::Format(TEXT("Cannot create SDL window: {0}"), String(SDL_GetError()))); Platform::Fatal(String::Format(TEXT("Cannot create SDL window: {0}"), String(SDL_GetError())));
@@ -190,27 +159,13 @@ SDLWindow::SDLWindow(const CreateWindowSettings& settings)
_handle = GetNativeWindowPointer(_window); _handle = GetNativeWindowPointer(_window);
ASSERT(_handle != nullptr); ASSERT(_handle != nullptr);
#if PLATFORM_LINUX
if (SDLPlatform::UsesWayland())
{
// Input focus is not set initially for Wayland windows, assume the window is focused until a focus event is received
if (_settings.AllowInput && (SDL_GetWindowFlags(_window) & SDL_WINDOW_INPUT_FOCUS) != 0)
_forcedFocus = (flags & SDL_WINDOW_INPUT_FOCUS) != 0;
}
#endif
SDL_DisplayID display = SDL_GetDisplayForWindow(_window); SDL_DisplayID display = SDL_GetDisplayForWindow(_window);
_dpiScale = SDL_GetDisplayContentScale(display); _dpiScale = SDL_GetWindowDisplayScale(_window);
_dpi = (int)(_dpiScale * DefaultDPI); _dpi = Math::TruncToInt(_dpiScale * DefaultDPI);
SDL_SetWindowMinimumSize(_window, (int)_settings.MinimumSize.X, (int)_settings.MinimumSize.Y);
SDL_SetWindowMaximumSize(_window, (int)_settings.MaximumSize.X, (int)_settings.MaximumSize.Y);
SDL_Rect rect;
SDL_GetWindowPosition(_window, &rect.x, &rect.y);
SDL_GetWindowSizeInPixels(_window, &rect.w, &rect.h);
_cachedClientRectangle = Rectangle((float)rect.x, (float)rect.y, (float)rect.w, (float)rect.h);
SDL_SetWindowMinimumSize(_window, Math::TruncToInt(_settings.MinimumSize.X), Math::TruncToInt(_settings.MinimumSize.Y));
SDL_SetWindowMaximumSize(_window, Math::TruncToInt(_settings.MaximumSize.X), Math::TruncToInt(_settings.MaximumSize.Y));
SDL_SetWindowHitTest(_window, &OnWindowHitTest, this); SDL_SetWindowHitTest(_window, &OnWindowHitTest, this);
InitSwapChain(); InitSwapChain();
@@ -272,6 +227,11 @@ void* GetNativeWindowPointer(SDL_Window* window)
return windowPtr; return windowPtr;
} }
SDL_Window* SDLWindow::GetSDLWindow() const
{
return _window;
}
#if PLATFORM_LINUX #if PLATFORM_LINUX
void* SDLWindow::GetWaylandSurfacePtr() const void* SDLWindow::GetWaylandSurfacePtr() const
@@ -474,7 +434,7 @@ 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... // X11 doesn't report any mouse events when mouse is over the caption area, send a simulated event instead...
Float2 mousePosition; Float2 mousePosition;
auto buttons = SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y); auto buttons = SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y);
if ((buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0) if ((buttons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)) != 0)
SDLPlatform::CheckWindowDragging(this, WindowHitCodes::Caption); SDLPlatform::CheckWindowDragging(this, WindowHitCodes::Caption);
} }
#endif #endif
@@ -495,34 +455,31 @@ void SDLWindow::HandleEvent(SDL_Event& event)
_minimized = false; _minimized = false;
_maximized = true; _maximized = true;
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS && BORDERLESS_MAXIMIZE_WORKAROUND == 2
if (SkipMaximizeEventsCount > 0)
{
SkipMaximizeEventsCount--;
return;
}
if (!_settings.HasBorder && _settings.HasSizingFrame) if (!_settings.HasBorder && _settings.HasSizingFrame)
{ {
// Borderless window doesn't maximize properly, manually force the window into correct location and size // Restore the window back to previous state
SDL_Rect rect; SDL_RestoreWindow(_window);
SDL_DisplayID display = SDL_GetDisplayForWindow(_window);
SDL_GetDisplayUsableBounds(display, &rect); // Excludes taskbar etc.
// Check if the window actually clips past the display work region // Remove the resizable flags from borderless windows and maximize the window again
auto pos = GetPosition(); auto style = ::GetWindowLong((HWND)_handle, GWL_STYLE);
auto size = GetClientSize(); style &= ~STYLE_RESIZABLE;
if (pos.X < rect.x || pos.Y < rect.y || size.X > rect.w || size.Y > rect.h) ::SetWindowLong((HWND)_handle, GWL_STYLE, style);
{
// Disable resizable flag so SDL updates the client rectangle to expected values during WM_NCCALCSIZE
SDL_SetWindowResizable(_window, false);
SDL_MaximizeWindow(_window);
// Set the internal floating window rectangle to expected position SDL_MaximizeWindow(_window);
SetClientBounds(Rectangle((float)rect.x, (float)rect.y, (float)rect.w, (float)rect.h));
// Flush and handle the events again // Re-enable the resizable borderless flags
::SetWindowPos((HWND)_handle, HWND_TOP, rect.x, rect.y, rect.w, rect.h, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER); style = ::GetWindowLong((HWND)_handle, GWL_STYLE) | STYLE_RESIZABLE;
SDL_PumpEvents(); ::SetWindowLong((HWND)_handle, GWL_STYLE, style);
// Restore previous values // The next SDL_EVENT_WINDOW_RESTORED and SDL_EVENT_WINDOW_MAXIMIZED events should be ignored
SDL_SetWindowResizable(_window, true); SkipMaximizeEventsCount = 2;
SetClientBounds(_cachedClientRectangle);
}
} }
#endif #endif
CheckForWindowResize(); CheckForWindowResize();
@@ -530,6 +487,14 @@ void SDLWindow::HandleEvent(SDL_Event& event)
} }
case SDL_EVENT_WINDOW_RESTORED: case SDL_EVENT_WINDOW_RESTORED:
{ {
#if BORDERLESS_MAXIMIZE_WORKAROUND == 2
if (SkipMaximizeEventsCount > 0)
{
SkipMaximizeEventsCount--;
return;
}
#endif
if (_maximized) if (_maximized)
{ {
_maximized = false; _maximized = false;
@@ -558,9 +523,6 @@ void SDLWindow::HandleEvent(SDL_Event& event)
} }
case SDL_EVENT_WINDOW_FOCUS_GAINED: case SDL_EVENT_WINDOW_FOCUS_GAINED:
{ {
#if PLATFORM_LINUX
_forcedFocus = false;
#endif
OnGotFocus(); OnGotFocus();
SDL_StartTextInput(_window); SDL_StartTextInput(_window);
const SDL_Rect* currentClippingRect = SDL_GetWindowMouseRect(_window); const SDL_Rect* currentClippingRect = SDL_GetWindowMouseRect(_window);
@@ -573,9 +535,6 @@ void SDLWindow::HandleEvent(SDL_Event& event)
} }
case SDL_EVENT_WINDOW_FOCUS_LOST: case SDL_EVENT_WINDOW_FOCUS_LOST:
{ {
#if PLATFORM_LINUX
_forcedFocus = false;
#endif
SDL_StopTextInput(_window); SDL_StopTextInput(_window);
const SDL_Rect* currentClippingRect = SDL_GetWindowMouseRect(_window); const SDL_Rect* currentClippingRect = SDL_GetWindowMouseRect(_window);
if (currentClippingRect != nullptr) if (currentClippingRect != nullptr)
@@ -585,16 +544,15 @@ void SDLWindow::HandleEvent(SDL_Event& event)
} }
case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
{ {
SDL_DisplayID display = SDL_GetDisplayForWindow(_window); float scale = SDL_GetWindowDisplayScale(_window);
float scale = SDL_GetDisplayContentScale(display); if (scale > 0.0f && _dpiScale != scale)
if (scale > 0.0f)
{ {
float oldScale = _dpiScale; float oldScale = _dpiScale;
_dpiScale = scale; _dpiScale = scale;
_dpi = (int)(_dpiScale * DefaultDPI); _dpi = static_cast<int>(_dpiScale * DefaultDPI);
int w = static_cast<int>(_cachedClientRectangle.GetWidth() * (scale / oldScale));
int w = (int)(_cachedClientRectangle.GetWidth() * (scale / oldScale)); int h = static_cast<int>(_cachedClientRectangle.GetHeight() * (scale / oldScale));
int h = (int)(_cachedClientRectangle.GetHeight() * (scale / oldScale)); _cachedClientRectangle.Size = Float2(static_cast<float>(w), static_cast<float>(h));
SDL_SetWindowSize(_window, w, h); SDL_SetWindowSize(_window, w, h);
// TODO: Recalculate fonts // TODO: Recalculate fonts
} }
@@ -671,12 +629,6 @@ void SDLWindow::HandleEvent(SDL_Event& event)
if (SDLInput::HandleEvent(this, event)) if (SDLInput::HandleEvent(this, event))
return; return;
} }
// ignored
if (event.type == SDL_EVENT_WINDOW_ICCPROF_CHANGED)
return;
//LOG(Info, "Unhandled SDL event: {0}", event.type);
} }
void* SDLWindow::GetNativePtr() const void* SDLWindow::GetNativePtr() const
@@ -741,7 +693,19 @@ void SDLWindow::Maximize()
if (!_settings.AllowMaximize) if (!_settings.AllowMaximize)
return; return;
#if PLATFORM_WINDOWS && BORDERLESS_MAXIMIZE_WORKAROUND == 1
// Workaround for "SDL_BORDERLESS_RESIZABLE_STYLE" hint not working as expected when maximizing windows
auto style = ::GetWindowLong((HWND)_handle, GWL_STYLE);
style &= ~STYLE_RESIZABLE;
::SetWindowLong((HWND)_handle, GWL_STYLE, style);
SDL_MaximizeWindow(_window); SDL_MaximizeWindow(_window);
style = ::GetWindowLong((HWND)_handle, GWL_STYLE) | STYLE_RESIZABLE;
::SetWindowLong((HWND)_handle, GWL_STYLE, style);
#else
SDL_MaximizeWindow(_window);
#endif
} }
void SDLWindow::SetBorderless(bool isBorderless, bool maximized) void SDLWindow::SetBorderless(bool isBorderless, bool maximized)
@@ -761,7 +725,9 @@ void SDLWindow::SetBorderless(bool isBorderless, bool maximized)
{ {
SDL_SetWindowBordered(_window, !isBorderless ? true : false); SDL_SetWindowBordered(_window, !isBorderless ? true : false);
if (maximized) if (maximized)
SDL_MaximizeWindow(_window); {
Maximize();
}
else else
Focus(); Focus();
} }
@@ -769,7 +735,9 @@ void SDLWindow::SetBorderless(bool isBorderless, bool maximized)
{ {
SDL_SetWindowBordered(_window, !isBorderless ? true : false); SDL_SetWindowBordered(_window, !isBorderless ? true : false);
if (maximized) if (maximized)
SDL_MaximizeWindow(_window); {
Maximize();
}
else else
Focus(); Focus();
} }
@@ -779,7 +747,19 @@ void SDLWindow::SetBorderless(bool isBorderless, bool maximized)
void SDLWindow::Restore() void SDLWindow::Restore()
{ {
#if PLATFORM_WINDOWS && BORDERLESS_MAXIMIZE_WORKAROUND == 1
// Workaround for "SDL_BORDERLESS_RESIZABLE_STYLE" hint not working as expected when maximizing windows
auto style = ::GetWindowLong((HWND)_handle, GWL_STYLE);
style &= ~STYLE_RESIZABLE;
::SetWindowLong((HWND)_handle, GWL_STYLE, style);
SDL_RestoreWindow(_window); SDL_RestoreWindow(_window);
style = ::GetWindowLong((HWND)_handle, GWL_STYLE) | STYLE_RESIZABLE;
::SetWindowLong((HWND)_handle, GWL_STYLE, style);
#else
SDL_RestoreWindow(_window);
#endif
} }
bool SDLWindow::IsClosed() const bool SDLWindow::IsClosed() const
@@ -789,10 +769,6 @@ bool SDLWindow::IsClosed() const
bool SDLWindow::IsForegroundWindow() const bool SDLWindow::IsForegroundWindow() const
{ {
#if PLATFORM_LINUX
if (_forcedFocus)
return true;
#endif
SDL_WindowFlags flags = SDL_GetWindowFlags(_window); SDL_WindowFlags flags = SDL_GetWindowFlags(_window);
return (flags & SDL_WINDOW_INPUT_FOCUS) != 0; return (flags & SDL_WINDOW_INPUT_FOCUS) != 0;
} }
@@ -807,8 +783,55 @@ void SDLWindow::BringToFront(bool force)
void SDLWindow::SetClientBounds(const Rectangle& clientArea) void SDLWindow::SetClientBounds(const Rectangle& clientArea)
{ {
SDL_SetWindowPosition(_window, (int)clientArea.GetLeft(), (int)clientArea.GetTop()); int newX = static_cast<int>(clientArea.GetLeft());
SDL_SetWindowSize(_window, (int)clientArea.GetWidth(), (int)clientArea.GetHeight()); int newY = static_cast<int>(clientArea.GetTop());
int newW = static_cast<int>(clientArea.GetWidth());
int newH = static_cast<int>(clientArea.GetHeight());
SetSDLWindowScreenPosition(this, newX, newY);
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))
return;
SDLWindow* window = parentWindow;
while (window != nullptr)
{
Int2 parentPosition;
SDL_GetWindowPosition(window->GetSDLWindow(), &parentPosition.X, &parentPosition.Y);
positionOffset -= parentPosition;
if (!IsPopupWindow(window->GetSettings().Type))
break;
window = window->GetSettings().Parent;
}
}
Int2 GetSDLWindowScreenPosition(const SDLWindow* window)
{
Int2 relativeOffset(0, 0);
GetRelativeWindowOffset(window->GetSettings().Type, window->GetSettings().Parent, relativeOffset);
Int2 position;
SDL_GetWindowPosition(window->GetSDLWindow(), &position.X, &position.Y);
return position - relativeOffset;
}
void SetSDLWindowScreenPosition(const SDLWindow* window, const int x, const int y)
{
Int2 relativePosition(x, y);
GetRelativeWindowOffset(window->GetSettings().Type, window->GetSettings().Parent, relativePosition);
SDL_SetWindowPosition(window->GetSDLWindow(), relativePosition.X, relativePosition.Y);
} }
void SDLWindow::SetPosition(const Float2& position) void SDLWindow::SetPosition(const Float2& position)
@@ -816,25 +839,22 @@ void SDLWindow::SetPosition(const Float2& position)
Int2 topLeftBorder; Int2 topLeftBorder;
SDL_GetWindowBordersSize(_window, &topLeftBorder.Y, &topLeftBorder.X, nullptr, nullptr); SDL_GetWindowBordersSize(_window, &topLeftBorder.Y, &topLeftBorder.X, nullptr, nullptr);
// The position is relative to the parent window Int2 screenPosition(static_cast<int>(position.X), static_cast<int>(position.Y));
Int2 relativePosition(static_cast<int>(position.X), static_cast<int>(position.Y)); screenPosition += topLeftBorder;
relativePosition += topLeftBorder;
SDLWindow* parent = (_settings.Type == WindowType::Tooltip || _settings.Type == WindowType::Popup) ? _settings.Parent : nullptr; if (false && SDLPlatform::UsesX11())
while (parent != nullptr)
{ {
Int2 parentPosition; // TODO: is this needed?
SDL_GetWindowPosition(parent->_window, &parentPosition.X, &parentPosition.Y); auto monitorBounds = Platform::GetMonitorBounds(Float2::Minimum);
relativePosition -= parentPosition; screenPosition += Int2(monitorBounds.GetTopLeft());
parent = parent->_settings.Parent;
} }
SDL_SetWindowPosition(_window, relativePosition.X, relativePosition.Y); SetSDLWindowScreenPosition(this, screenPosition.X, screenPosition.Y);
} }
void SDLWindow::SetClientPosition(const Float2& position) void SDLWindow::SetClientPosition(const Float2& position)
{ {
SDL_SetWindowPosition(_window, static_cast<int>(position.X), static_cast<int>(position.Y)); SetSDLWindowScreenPosition(this, static_cast<int>(position.X), static_cast<int>(position.Y));
} }
void SDLWindow::SetIsFullscreen(bool isFullscreen) void SDLWindow::SetIsFullscreen(bool isFullscreen)
@@ -867,19 +887,9 @@ Float2 SDLWindow::GetPosition() const
Int2 topLeftBorder; Int2 topLeftBorder;
SDL_GetWindowBordersSize(_window, &topLeftBorder.Y, &topLeftBorder.X, nullptr, nullptr); SDL_GetWindowBordersSize(_window, &topLeftBorder.Y, &topLeftBorder.X, nullptr, nullptr);
// The position is relative to the parent window Int2 position = GetSDLWindowScreenPosition(this);
Int2 position;
SDL_GetWindowPosition(_window, &position.X, &position.Y);
position -= topLeftBorder; position -= topLeftBorder;
SDLWindow* parent = (_settings.Type == WindowType::Tooltip || _settings.Type == WindowType::Popup) ? _settings.Parent : nullptr;
while (parent != nullptr)
{
Int2 parentPosition;
SDL_GetWindowPosition(parent->_window, &parentPosition.X, &parentPosition.Y);
position += parentPosition;
parent = parent->_settings.Parent;
}
return Float2(static_cast<float>(position.X), static_cast<float>(position.Y)); return Float2(static_cast<float>(position.X), static_cast<float>(position.Y));
} }
@@ -903,37 +913,13 @@ Float2 SDLWindow::GetClientSize() const
Float2 SDLWindow::ScreenToClient(const Float2& screenPos) const Float2 SDLWindow::ScreenToClient(const Float2& screenPos) const
{ {
// The position is relative to the parent window Int2 position = GetSDLWindowScreenPosition(this);
Int2 position;
SDL_GetWindowPosition(_window, &position.X, &position.Y);
SDLWindow* parent = (_settings.Type == WindowType::Tooltip || _settings.Type == WindowType::Popup) ? _settings.Parent : nullptr;
while (parent != nullptr)
{
Int2 parentPosition;
SDL_GetWindowPosition(parent->_window, &parentPosition.X, &parentPosition.Y);
position += parentPosition;
parent = parent->_settings.Parent;
}
return screenPos - Float2(static_cast<float>(position.X), static_cast<float>(position.Y)); return screenPos - Float2(static_cast<float>(position.X), static_cast<float>(position.Y));
} }
Float2 SDLWindow::ClientToScreen(const Float2& clientPos) const Float2 SDLWindow::ClientToScreen(const Float2& clientPos) const
{ {
// The position is relative to the parent window Int2 position = GetSDLWindowScreenPosition(this);
Int2 position;
SDL_GetWindowPosition(_window, &position.X, &position.Y);
SDLWindow* parent = (_settings.Type == WindowType::Tooltip || _settings.Type == WindowType::Popup) ? _settings.Parent : nullptr;
while (parent != nullptr)
{
Int2 parentPosition;
SDL_GetWindowPosition(parent->_window, &parentPosition.X, &parentPosition.Y);
position += parentPosition;
parent = parent->_settings.Parent;
}
return clientPos + Float2(static_cast<float>(position.X), static_cast<float>(position.Y)); return clientPos + Float2(static_cast<float>(position.X), static_cast<float>(position.Y));
} }
@@ -955,11 +941,13 @@ float SDLWindow::GetOpacity() const
void SDLWindow::SetOpacity(const float opacity) void SDLWindow::SetOpacity(const float opacity)
{ {
SDL_SetWindowOpacity(_window, opacity); if (!SDL_SetWindowOpacity(_window, opacity))
LOG(Warning, "SDL_SetWindowOpacity failed: {0}", String(SDL_GetError()));
} }
void SDLWindow::Focus() void SDLWindow::Focus()
{ {
#if PLATFORM_WINDOWS
auto activateWhenRaised = SDL_GetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED); auto activateWhenRaised = SDL_GetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED);
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, "1"); SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, "1");
@@ -971,6 +959,9 @@ void SDLWindow::Focus()
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, activateWhenRaised); SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, activateWhenRaised);
//SDL_SetHint(SDL_HINT_FORCE_RAISEWINDOW, forceRaiseWindow); //SDL_SetHint(SDL_HINT_FORCE_RAISEWINDOW, forceRaiseWindow);
#else
SDL_RaiseWindow(_window);
#endif
} }
String SDLWindow::GetTitle() const String SDLWindow::GetTitle() const

View File

@@ -34,7 +34,6 @@ private:
#endif #endif
#if PLATFORM_LINUX #if PLATFORM_LINUX
bool _dragOver; bool _dragOver;
bool _forcedFocus;
#endif #endif
SDL_Window* _window; SDL_Window* _window;
uint32 _windowId; uint32 _windowId;
@@ -68,6 +67,7 @@ private:
public: public:
SDL_Window* GetSDLWindow() const;
#if PLATFORM_LINUX #if PLATFORM_LINUX
void* GetWaylandSurfacePtr() const; void* GetWaylandSurfacePtr() const;
void* GetWaylandDisplay() const; void* GetWaylandDisplay() const;

View File

@@ -17,6 +17,11 @@ namespace FlaxEngine.GUI
private string _currentText; private string _currentText;
private Window _window; private Window _window;
/// <summary>
/// The mouse offset from top-left corner of tooltip.
/// </summary>
private static readonly Float2 TooltipOffset = new Float2(15, 10);
/// <summary> /// <summary>
/// The horizontal alignment of the text. /// The horizontal alignment of the text.
/// </summary> /// </summary>
@@ -81,7 +86,7 @@ namespace FlaxEngine.GUI
_showTarget = target; _showTarget = target;
//WrapPosition(ref locationSS); //WrapPosition(ref locationSS);
WrapPosition(ref mousePos, 10); WrapPosition(ref mousePos, 10);
locationSS = mousePos + new Float2(15, 10); locationSS = mousePos + TooltipOffset;
// Create window // Create window
var desc = CreateWindowSettings.Default; var desc = CreateWindowSettings.Default;
@@ -99,8 +104,10 @@ namespace FlaxEngine.GUI
desc.AllowDragAndDrop = false; desc.AllowDragAndDrop = false;
desc.IsTopmost = true; desc.IsTopmost = true;
desc.Type = WindowType.Tooltip; desc.Type = WindowType.Tooltip;
desc.Title = "Tooltip";
desc.HasSizingFrame = false; desc.HasSizingFrame = false;
desc.ShowAfterFirstPaint = true; desc.ShowAfterFirstPaint = true;
desc.Parent = parentWin.RootWindow.Window;
_window = Platform.CreateWindow(ref desc); _window = Platform.CreateWindow(ref desc);
if (_window == null) if (_window == null)
throw new InvalidOperationException("Failed to create tooltip window."); throw new InvalidOperationException("Failed to create tooltip window.");
@@ -228,7 +235,7 @@ namespace FlaxEngine.GUI
// Position tooltip when mouse moves // Position tooltip when mouse moves
WrapPosition(ref mousePos, 10); WrapPosition(ref mousePos, 10);
if (_window) if (_window)
_window.Position = mousePos + new Float2(15, 10); _window.Position = mousePos + TooltipOffset;
} }
base.Update(deltaTime); base.Update(deltaTime);