From 696be807bd2d912cd56eff338086d1e933b977e9 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Wed, 25 Dec 2024 19:45:10 +0200 Subject: [PATCH] Fix various issues with child window positioning --- .../Editor/GUI/ContextMenu/ContextMenuBase.cs | 1 + Source/Engine/Platform/SDL/SDLInput.cpp | 9 +- .../Engine/Platform/SDL/SDLPlatform.Linux.cpp | 5 - Source/Engine/Platform/SDL/SDLPlatform.cpp | 11 +- Source/Engine/Platform/SDL/SDLPlatform.h | 1 - Source/Engine/Platform/SDL/SDLWindow.cpp | 335 +++++++++--------- Source/Engine/Platform/SDL/SDLWindow.h | 2 +- Source/Engine/UI/GUI/Tooltip.cs | 11 +- 8 files changed, 185 insertions(+), 190 deletions(-) diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs index 72244c47c..dffbd346b 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs @@ -220,6 +220,7 @@ namespace FlaxEditor.GUI.ContextMenu desc.IsTopmost = true; desc.Type = WindowType.Popup; desc.Parent = parentWin.Window; + desc.Title = "ContextMenu"; desc.HasSizingFrame = false; OnWindowCreating(ref desc); _window = Platform.CreateWindow(ref desc); diff --git a/Source/Engine/Platform/SDL/SDLInput.cpp b/Source/Engine/Platform/SDL/SDLInput.cpp index eb0754c22..54d2d7df2 100644 --- a/Source/Engine/Platform/SDL/SDLInput.cpp +++ b/Source/Engine/Platform/SDL/SDLInput.cpp @@ -364,7 +364,10 @@ public: public: - Float2 GetMousePosition() const + /// + /// Returns the previous known position of the mouse before entering relative mode. + /// + Float2 GetOldMousePosition() const { 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 // mouse position would cause input to trigger in other editor windows. - mousePos = SDLInputImpl::Mouse->GetMousePosition(); + mousePos = SDLInputImpl::Mouse->GetOldMousePosition(); } 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 // 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); diff --git a/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp b/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp index 76659408d..2ae493280 100644 --- a/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp +++ b/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp @@ -918,11 +918,6 @@ bool SDLPlatform::UsesWayland() return UseWayland; } -bool SDLPlatform::UsesXWayland() -{ - return false; -} - bool SDLPlatform::UsesX11() { return !UseWayland; diff --git a/Source/Engine/Platform/SDL/SDLPlatform.cpp b/Source/Engine/Platform/SDL/SDLPlatform.cpp index cb3d0b659..13d48051a 100644 --- a/Source/Engine/Platform/SDL/SDLPlatform.cpp +++ b/Source/Engine/Platform/SDL/SDLPlatform.cpp @@ -64,13 +64,17 @@ bool SDLPlatform::Init() SDL_SetHint(SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE, "0"); 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_WINDOWED_STYLE", "1"); + 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_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_WINDOWS_RAW_KEYBOARD, "1"); + SDL_SetHint(SDL_HINT_VIDEO_WAYLAND_SCALE_TO_DISPLAY, "1"); + // Disable SDL clipboard support SDL_SetEventEnabled(SDL_EVENT_CLIPBOARD_UPDATE, false); @@ -316,11 +320,6 @@ bool SDLPlatform::UsesWayland() return false; } -bool SDLPlatform::UsesXWayland() -{ - return false; -} - bool SDLPlatform::UsesX11() { return false; diff --git a/Source/Engine/Platform/SDL/SDLPlatform.h b/Source/Engine/Platform/SDL/SDLPlatform.h index d67495688..528ab6f61 100644 --- a/Source/Engine/Platform/SDL/SDLPlatform.h +++ b/Source/Engine/Platform/SDL/SDLPlatform.h @@ -58,7 +58,6 @@ public: static void* GetXDisplay(); #endif static bool UsesWayland(); - static bool UsesXWayland(); static bool UsesX11(); public: diff --git a/Source/Engine/Platform/SDL/SDLWindow.cpp b/Source/Engine/Platform/SDL/SDLWindow.cpp index 3f046020d..f6a648f54 100644 --- a/Source/Engine/Platform/SDL/SDLWindow.cpp +++ b/Source/Engine/Platform/SDL/SDLWindow.cpp @@ -26,6 +26,8 @@ #if PLATFORM_WINDOWS #include "Engine/Platform/Win32/IncludeWindowsHeaders.h" +#define STYLE_RESIZABLE (WS_THICKFRAME | WS_MAXIMIZEBOX) +#define BORDERLESS_MAXIMIZE_WORKAROUND 2 #if USE_EDITOR #include #endif @@ -39,10 +41,16 @@ namespace { SDLWindow* LastEventWindow = nullptr; static SDL_Cursor* Cursors[SDL_SYSTEM_CURSOR_COUNT] = { nullptr }; +#if BORDERLESS_MAXIMIZE_WORKAROUND == 2 + int SkipMaximizeEventsCount = 0; +#endif } void* GetNativeWindowPointer(SDL_Window* window); 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 { @@ -86,17 +94,11 @@ SDLWindow::SDLWindow(const CreateWindowSettings& settings) , _handle(nullptr) , _cachedClientRectangle(Rectangle()) #if PLATFORM_LINUX - , _forcedFocus(false) , _dragOver(false) #endif { - int32 x = Math::TruncToInt(settings.Position.X); - int32 y = Math::TruncToInt(settings.Position.Y); - 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); + Int2 clientSize(Math::TruncToInt(settings.Size.X), Math::TruncToInt(settings.Size.Y)); + _clientSize = Float2(clientSize); if (SDLPlatform::UsesWayland()) { @@ -124,56 +126,22 @@ SDLWindow::SDLWindow(const CreateWindowSettings& settings) flags |= SDL_WINDOW_ALWAYS_ON_TOP; if (_settings.SupportsTransparency) flags |= SDL_WINDOW_TRANSPARENT; - //flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY; - 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. - // 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(); + // 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)) + // _settings.Parent = nullptr; - if (_settings.Parent == nullptr) - _settings.Parent = Engine::MainWindow; - } - - // 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); - } + // The window position needs to be relative to the parent window + Int2 relativePosition(Math::TruncToInt(settings.Position.X), Math::TruncToInt(settings.Position.Y)); + GetRelativeWindowOffset(_settings.Type, _settings.Parent, relativePosition); SDL_PropertiesID props = SDL_CreateProperties(); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags); 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_Y_NUMBER, y); - SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, windowWidth); - SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, windowHeight); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, relativePosition.X); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, relativePosition.Y); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, clientSize.X); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, clientSize.Y); SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN, true); if ((flags & SDL_WINDOW_TOOLTIP) != 0) 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); _window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); if (_window == nullptr) 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); 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); - _dpiScale = SDL_GetDisplayContentScale(display); - _dpi = (int)(_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); + _dpiScale = SDL_GetWindowDisplayScale(_window); + _dpi = Math::TruncToInt(_dpiScale * DefaultDPI); + 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); InitSwapChain(); @@ -272,6 +227,11 @@ void* GetNativeWindowPointer(SDL_Window* window) return windowPtr; } +SDL_Window* SDLWindow::GetSDLWindow() const +{ + return _window; +} + #if PLATFORM_LINUX 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... Float2 mousePosition; 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); } #endif @@ -495,34 +455,31 @@ void SDLWindow::HandleEvent(SDL_Event& event) _minimized = false; _maximized = true; -#if PLATFORM_WINDOWS +#if PLATFORM_WINDOWS && BORDERLESS_MAXIMIZE_WORKAROUND == 2 + if (SkipMaximizeEventsCount > 0) + { + SkipMaximizeEventsCount--; + return; + } + if (!_settings.HasBorder && _settings.HasSizingFrame) { - // Borderless window doesn't maximize properly, manually force the window into correct location and size - SDL_Rect rect; - SDL_DisplayID display = SDL_GetDisplayForWindow(_window); - SDL_GetDisplayUsableBounds(display, &rect); // Excludes taskbar etc. + // Restore the window back to previous state + SDL_RestoreWindow(_window); - // Check if the window actually clips past the display work region - auto pos = GetPosition(); - auto size = GetClientSize(); - if (pos.X < rect.x || pos.Y < rect.y || size.X > rect.w || size.Y > rect.h) - { - // Disable resizable flag so SDL updates the client rectangle to expected values during WM_NCCALCSIZE - SDL_SetWindowResizable(_window, false); - SDL_MaximizeWindow(_window); + // Remove the resizable flags from borderless windows and maximize the window again + auto style = ::GetWindowLong((HWND)_handle, GWL_STYLE); + style &= ~STYLE_RESIZABLE; + ::SetWindowLong((HWND)_handle, GWL_STYLE, style); - // Set the internal floating window rectangle to expected position - SetClientBounds(Rectangle((float)rect.x, (float)rect.y, (float)rect.w, (float)rect.h)); + SDL_MaximizeWindow(_window); - // Flush and handle the events again - ::SetWindowPos((HWND)_handle, HWND_TOP, rect.x, rect.y, rect.w, rect.h, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER); - SDL_PumpEvents(); + // Re-enable the resizable borderless flags + style = ::GetWindowLong((HWND)_handle, GWL_STYLE) | STYLE_RESIZABLE; + ::SetWindowLong((HWND)_handle, GWL_STYLE, style); - // Restore previous values - SDL_SetWindowResizable(_window, true); - SetClientBounds(_cachedClientRectangle); - } + // The next SDL_EVENT_WINDOW_RESTORED and SDL_EVENT_WINDOW_MAXIMIZED events should be ignored + SkipMaximizeEventsCount = 2; } #endif CheckForWindowResize(); @@ -530,6 +487,14 @@ void SDLWindow::HandleEvent(SDL_Event& event) } case SDL_EVENT_WINDOW_RESTORED: { +#if BORDERLESS_MAXIMIZE_WORKAROUND == 2 + if (SkipMaximizeEventsCount > 0) + { + SkipMaximizeEventsCount--; + return; + } +#endif + if (_maximized) { _maximized = false; @@ -558,9 +523,6 @@ void SDLWindow::HandleEvent(SDL_Event& event) } case SDL_EVENT_WINDOW_FOCUS_GAINED: { -#if PLATFORM_LINUX - _forcedFocus = false; -#endif OnGotFocus(); SDL_StartTextInput(_window); const SDL_Rect* currentClippingRect = SDL_GetWindowMouseRect(_window); @@ -573,9 +535,6 @@ void SDLWindow::HandleEvent(SDL_Event& event) } case SDL_EVENT_WINDOW_FOCUS_LOST: { -#if PLATFORM_LINUX - _forcedFocus = false; -#endif SDL_StopTextInput(_window); const SDL_Rect* currentClippingRect = SDL_GetWindowMouseRect(_window); if (currentClippingRect != nullptr) @@ -585,16 +544,15 @@ void SDLWindow::HandleEvent(SDL_Event& event) } case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: { - SDL_DisplayID display = SDL_GetDisplayForWindow(_window); - float scale = SDL_GetDisplayContentScale(display); - if (scale > 0.0f) + float scale = SDL_GetWindowDisplayScale(_window); + if (scale > 0.0f && _dpiScale != scale) { float oldScale = _dpiScale; _dpiScale = scale; - _dpi = (int)(_dpiScale * DefaultDPI); - - int w = (int)(_cachedClientRectangle.GetWidth() * (scale / oldScale)); - int h = (int)(_cachedClientRectangle.GetHeight() * (scale / oldScale)); + _dpi = static_cast(_dpiScale * DefaultDPI); + int w = static_cast(_cachedClientRectangle.GetWidth() * (scale / oldScale)); + int h = static_cast(_cachedClientRectangle.GetHeight() * (scale / oldScale)); + _cachedClientRectangle.Size = Float2(static_cast(w), static_cast(h)); SDL_SetWindowSize(_window, w, h); // TODO: Recalculate fonts } @@ -671,12 +629,6 @@ void SDLWindow::HandleEvent(SDL_Event& event) if (SDLInput::HandleEvent(this, event)) return; } - - // ignored - if (event.type == SDL_EVENT_WINDOW_ICCPROF_CHANGED) - return; - - //LOG(Info, "Unhandled SDL event: {0}", event.type); } void* SDLWindow::GetNativePtr() const @@ -741,7 +693,19 @@ void SDLWindow::Maximize() if (!_settings.AllowMaximize) 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); + + 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) @@ -761,7 +725,9 @@ void SDLWindow::SetBorderless(bool isBorderless, bool maximized) { SDL_SetWindowBordered(_window, !isBorderless ? true : false); if (maximized) - SDL_MaximizeWindow(_window); + { + Maximize(); + } else Focus(); } @@ -769,7 +735,9 @@ void SDLWindow::SetBorderless(bool isBorderless, bool maximized) { SDL_SetWindowBordered(_window, !isBorderless ? true : false); if (maximized) - SDL_MaximizeWindow(_window); + { + Maximize(); + } else Focus(); } @@ -779,7 +747,19 @@ void SDLWindow::SetBorderless(bool isBorderless, bool maximized) 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); + + style = ::GetWindowLong((HWND)_handle, GWL_STYLE) | STYLE_RESIZABLE; + ::SetWindowLong((HWND)_handle, GWL_STYLE, style); +#else + SDL_RestoreWindow(_window); +#endif } bool SDLWindow::IsClosed() const @@ -789,10 +769,6 @@ bool SDLWindow::IsClosed() const bool SDLWindow::IsForegroundWindow() const { -#if PLATFORM_LINUX - if (_forcedFocus) - return true; -#endif SDL_WindowFlags flags = SDL_GetWindowFlags(_window); return (flags & SDL_WINDOW_INPUT_FOCUS) != 0; } @@ -807,8 +783,55 @@ void SDLWindow::BringToFront(bool force) void SDLWindow::SetClientBounds(const Rectangle& clientArea) { - SDL_SetWindowPosition(_window, (int)clientArea.GetLeft(), (int)clientArea.GetTop()); - SDL_SetWindowSize(_window, (int)clientArea.GetWidth(), (int)clientArea.GetHeight()); + int newX = static_cast(clientArea.GetLeft()); + int newY = static_cast(clientArea.GetTop()); + int newW = static_cast(clientArea.GetWidth()); + int newH = static_cast(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) @@ -816,25 +839,22 @@ void SDLWindow::SetPosition(const Float2& position) Int2 topLeftBorder; SDL_GetWindowBordersSize(_window, &topLeftBorder.Y, &topLeftBorder.X, nullptr, nullptr); - // The position is relative to the parent window - Int2 relativePosition(static_cast(position.X), static_cast(position.Y)); - relativePosition += topLeftBorder; + Int2 screenPosition(static_cast(position.X), static_cast(position.Y)); + screenPosition += topLeftBorder; - SDLWindow* parent = (_settings.Type == WindowType::Tooltip || _settings.Type == WindowType::Popup) ? _settings.Parent : nullptr; - while (parent != nullptr) + if (false && SDLPlatform::UsesX11()) { - Int2 parentPosition; - SDL_GetWindowPosition(parent->_window, &parentPosition.X, &parentPosition.Y); - relativePosition -= parentPosition; - parent = parent->_settings.Parent; + // TODO: is this needed? + auto monitorBounds = Platform::GetMonitorBounds(Float2::Minimum); + screenPosition += Int2(monitorBounds.GetTopLeft()); } - - SDL_SetWindowPosition(_window, relativePosition.X, relativePosition.Y); + + SetSDLWindowScreenPosition(this, screenPosition.X, screenPosition.Y); } void SDLWindow::SetClientPosition(const Float2& position) { - SDL_SetWindowPosition(_window, static_cast(position.X), static_cast(position.Y)); + SetSDLWindowScreenPosition(this, static_cast(position.X), static_cast(position.Y)); } void SDLWindow::SetIsFullscreen(bool isFullscreen) @@ -867,19 +887,9 @@ Float2 SDLWindow::GetPosition() const Int2 topLeftBorder; SDL_GetWindowBordersSize(_window, &topLeftBorder.Y, &topLeftBorder.X, nullptr, nullptr); - // The position is relative to the parent window - Int2 position; - SDL_GetWindowPosition(_window, &position.X, &position.Y); + Int2 position = GetSDLWindowScreenPosition(this); 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(position.X), static_cast(position.Y)); } @@ -903,37 +913,13 @@ Float2 SDLWindow::GetClientSize() const Float2 SDLWindow::ScreenToClient(const Float2& screenPos) const { - // The position is relative to the parent window - 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; - } - + Int2 position = GetSDLWindowScreenPosition(this); return screenPos - Float2(static_cast(position.X), static_cast(position.Y)); } Float2 SDLWindow::ClientToScreen(const Float2& clientPos) const { - // The position is relative to the parent window - 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; - } - + Int2 position = GetSDLWindowScreenPosition(this); return clientPos + Float2(static_cast(position.X), static_cast(position.Y)); } @@ -955,11 +941,13 @@ float SDLWindow::GetOpacity() const 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() { +#if PLATFORM_WINDOWS auto activateWhenRaised = SDL_GetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED); 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_FORCE_RAISEWINDOW, forceRaiseWindow); +#else + SDL_RaiseWindow(_window); +#endif } String SDLWindow::GetTitle() const diff --git a/Source/Engine/Platform/SDL/SDLWindow.h b/Source/Engine/Platform/SDL/SDLWindow.h index 3cb3d4b02..416bd48d2 100644 --- a/Source/Engine/Platform/SDL/SDLWindow.h +++ b/Source/Engine/Platform/SDL/SDLWindow.h @@ -34,7 +34,6 @@ private: #endif #if PLATFORM_LINUX bool _dragOver; - bool _forcedFocus; #endif SDL_Window* _window; uint32 _windowId; @@ -68,6 +67,7 @@ private: public: + SDL_Window* GetSDLWindow() const; #if PLATFORM_LINUX void* GetWaylandSurfacePtr() const; void* GetWaylandDisplay() const; diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 282dc97f2..cc8c211b3 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -17,6 +17,11 @@ namespace FlaxEngine.GUI private string _currentText; private Window _window; + /// + /// The mouse offset from top-left corner of tooltip. + /// + private static readonly Float2 TooltipOffset = new Float2(15, 10); + /// /// The horizontal alignment of the text. /// @@ -81,7 +86,7 @@ namespace FlaxEngine.GUI _showTarget = target; //WrapPosition(ref locationSS); WrapPosition(ref mousePos, 10); - locationSS = mousePos + new Float2(15, 10); + locationSS = mousePos + TooltipOffset; // Create window var desc = CreateWindowSettings.Default; @@ -99,8 +104,10 @@ namespace FlaxEngine.GUI desc.AllowDragAndDrop = false; desc.IsTopmost = true; desc.Type = WindowType.Tooltip; + desc.Title = "Tooltip"; desc.HasSizingFrame = false; desc.ShowAfterFirstPaint = true; + desc.Parent = parentWin.RootWindow.Window; _window = Platform.CreateWindow(ref desc); if (_window == null) throw new InvalidOperationException("Failed to create tooltip window."); @@ -227,7 +234,7 @@ namespace FlaxEngine.GUI // Position tooltip when mouse moves WrapPosition(ref mousePos, 10); if (_window) - _window.Position = mousePos + new Float2(15, 10); + _window.Position = mousePos + TooltipOffset; } base.Update(deltaTime);