From 523919bb0cfa417a61fdf1be68a3d89aa70a7932 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sat, 27 Dec 2025 14:00:10 +0100 Subject: [PATCH 1/3] Refactor handling of mouse messages - Unify selection if active window. - Always use `Relay*` where possible - Stop handling if any control handled it --- libs/s25main/WindowManager.cpp | 367 +++----------------- libs/s25main/WindowManager.h | 11 +- libs/s25main/ingameWindows/IngameWindow.cpp | 22 +- libs/s25main/ingameWindows/IngameWindow.h | 8 +- libs/s25main/ingameWindows/iwObservate.cpp | 6 +- tests/s25Main/UI/testIngameWindow.cpp | 28 +- 6 files changed, 98 insertions(+), 344 deletions(-) diff --git a/libs/s25main/WindowManager.cpp b/libs/s25main/WindowManager.cpp index 567a88b801..a832a85c6e 100644 --- a/libs/s25main/WindowManager.cpp +++ b/libs/s25main/WindowManager.cpp @@ -92,29 +92,13 @@ void WindowManager::Draw() DrawCursor(); } -/** - * liefert ob der aktuelle Desktop den Focus besitzt oder nicht. - * - * @return liefert @p true bei aktivem Desktop, - * @p false wenn der Desktop nicht den Fokus besitzt. - */ -bool WindowManager::IsDesktopActive() +bool WindowManager::IsDesktopActive() const { if(curDesktop) return curDesktop->IsActive(); - return false; } -/** - * schickt eine Nachricht an das aktive Fenster bzw den aktiven Desktop. - * - * @param[in] msg Nachricht welche geschickt werden soll - * @param[in] id ID des Steuerelements - * @param[in] param Parameter der Nachricht - */ - -/// Sendet eine Tastaturnachricht an die Fenster. void WindowManager::RelayKeyboardMessage(KeyboardMsgHandler msg, const KeyEvent& ke) { // When there is no desktop, don't check it or any window @@ -154,23 +138,15 @@ void WindowManager::RelayKeyboardMessage(KeyboardMsgHandler msg, const KeyEvent& } } -/// Sendet eine Mausnachricht weiter an alle Fenster -void WindowManager::RelayMouseMessage(MouseMsgHandler msg, const MouseCoords& mc) +void WindowManager::RelayMouseMessage(MouseMsgHandler msg, const MouseCoords& mc, Window* window) { - // ist der Desktop gültig? - if(!curDesktop) - return; - // ist der Desktop aktiv? - if(curDesktop->IsActive()) + if(!window) + window = getActiveWindow(); + if(window) { - // Ja, dann Nachricht an Desktop weiterleiten - CALL_MEMBER_FN(*curDesktop, msg)(mc); - curDesktop->RelayMouseMessage(msg, mc); - } else if(!windows.empty()) - { - // Nein, dann Nachricht an letztes Fenster weiterleiten - CALL_MEMBER_FN(*windows.back(), msg)(mc); - windows.back()->RelayMouseMessage(msg, mc); + // If no sub-window/control handled the message, let the window itself handle it + if(!window->RelayMouseMessage(msg, mc)) + CALL_MEMBER_FN(*window, msg)(mc); } } @@ -248,166 +224,75 @@ IngameWindow* WindowManager::FindNonModalWindow(unsigned id) const return itWnd == windows.end() ? nullptr : itWnd->get(); } -/** - * Verarbeitung des Drückens der Linken Maustaste. - * - * @param[in] mc Mauskoordinaten Struktur - */ -void WindowManager::Msg_LeftDown(MouseCoords mc) +Window* WindowManager::findAndActivateWindow(const Position mousePos) { - // ist unser Desktop gültig? - if(!curDesktop) - return; - - // Sound abspielen - SoundEffectItem* sound = LOADER.GetSoundN("sound", 112); - if(sound) - sound->Play(255, false); - - // haben wir überhaupt fenster? - if(windows.empty()) - { - // nein, dann Desktop aktivieren - SetActiveWindow(*curDesktop); - - // ist der Maus-Klick-Fix aktiv? - if(!disable_mouse) - { - // nein, Msg_LeftDown aufrufen - curDesktop->Msg_LeftDown(mc); - - // und allen unten drunter auch Bescheid sagen - curDesktop->RelayMouseMessage(&Window::Msg_LeftDown, mc); - } - - // und raus - return; - } - - // ist das zuletzt aktiv gewesene Fenster Modal? - IngameWindow& lastActiveWnd = *windows.back(); - if(lastActiveWnd.IsModal()) + Window* activeWindow = nullptr; + if(!windows.empty()) { - if(!lastActiveWnd.IsActive()) - SetActiveWindow(lastActiveWnd); - - // ja es ist modal, ist der Maus-Klick-Fix aktiv? - if(!disable_mouse) - { - // nein, Msg_LeftDownaufrufen - lastActiveWnd.Msg_LeftDown(mc); - - // und allen unten drunter auch Bescheid sagen - lastActiveWnd.RelayMouseMessage(&Window::Msg_LeftDown, mc); - - // und noch MouseLeftDown vom Fenster aufrufen - lastActiveWnd.MouseLeftDown(mc); - } - - // und raus - return; + if(windows.back()->IsModal()) + activeWindow = windows.back().get(); + else + activeWindow = FindWindowAtPos(mousePos); } + if(!activeWindow) + activeWindow = curDesktop.get(); + if(activeWindow && !activeWindow->IsActive()) + SetActiveWindow(*activeWindow); + return activeWindow; +} - IngameWindow* foundWindow = FindWindowAtPos(mc.pos); - - // Haben wir ein Fenster gefunden gehabt? - if(foundWindow) - { - SetActiveWindow(*foundWindow); - - // ist der Maus-Klick-Fix aktiv? - if(!disable_mouse) - { - // nein, dann Msg_LeftDown aufrufen - foundWindow->Msg_LeftDown(mc); - - // und allen unten drunter auch Bescheid sagen - foundWindow->RelayMouseMessage(&Window::Msg_LeftDown, mc); - - // und noch MouseLeftDown vom Fenster aufrufen - foundWindow->MouseLeftDown(mc); - } - } else - { - SetActiveWindow(*curDesktop); +Window* WindowManager::getActiveWindow() const +{ + if(IsDesktopActive()) + return curDesktop.get(); + if(!windows.empty()) + return windows.back().get(); + return nullptr; +} - // ist der Maus-Klick-Fix aktiv? - if(!disable_mouse) - { - // nein, dann Msg_LeftDown aufrufen - curDesktop->Msg_LeftDown(mc); +void WindowManager::Msg_LeftDown(MouseCoords mc) +{ + // play click sound + SoundEffectItem* sound = LOADER.GetSoundN("sound", 112); + if(sound) + sound->Play(255, false); - // und allen unten drunter auch Bescheid sagen - curDesktop->RelayMouseMessage(&Window::Msg_LeftDown, mc); - } - } + Window* activeWindow = findAndActivateWindow(mc.pos); + // Ignore mouse message, e.g. right after switching desktop, to avoid unwanted clicks + if(!disable_mouse && activeWindow) + RelayMouseMessage(&Window::Msg_LeftDown, mc, activeWindow); } -/** - * Verarbeitung des Loslassens der Linken Maustaste. - * - * @param[in] mc Mauskoordinaten Struktur - */ void WindowManager::Msg_LeftUp(MouseCoords mc) { - // ist unser Desktop gültig? + // Any desktop active? if(!curDesktop) return; - // Ggf. Doppelklick untersuche - unsigned time_now = VIDEODRIVER.GetTickCount(); + // Check for double-click + const auto time_now = VIDEODRIVER.GetTickCount(); if(time_now - lastLeftClickTime < DOUBLE_CLICK_INTERVAL && mc.pos == lastLeftClickPos) - { mc.dbl_click = true; - } else + else { - // Werte wieder erneut speichern + // Just single click, store values for next possible double click lastLeftClickPos = mc.pos; lastLeftClickTime = time_now; } - // ist der Maus-Klick-Fix aktiv? + // Don't relay message if mouse is disabled (e.g. right after desktop switch) if(!disable_mouse) - { - // ist der Desktop aktiv? - if(curDesktop->IsActive()) - { - // ja, dann Msg_LeftUp aufrufen - curDesktop->Msg_LeftUp(mc); - - // und die Fenster darunter auch - curDesktop->RelayMouseMessage(&Window::Msg_LeftUp, mc); - } else if(!windows.empty()) - { - // ja, dann Msg_LeftUp aufrufen - IngameWindow& activeWnd = *windows.back(); - activeWnd.Msg_LeftUp(mc); - - // und den anderen Fenstern auch Bescheid geben - activeWnd.RelayMouseMessage(&Window::Msg_LeftUp, mc); - - // und noch MouseLeftUp vom Fenster aufrufen - activeWnd.MouseLeftUp(mc); - } - } - - // Maus-Klick-Fix deaktivieren - if(disable_mouse && !nextdesktop) + RelayMouseMessage(&Window::Msg_LeftUp, mc); + else if(!nextdesktop) disable_mouse = false; } -/** - * Verarbeitung des Drückens der Rechten Maustaste. - * - * @param[in] mc Mauskoordinaten Struktur - */ void WindowManager::Msg_RightDown(const MouseCoords& mc) { - // ist unser Desktop gültig? if(!curDesktop) return; - // Right-click closes (most) windows, so check that + // Right-click closes (most) windows, so handle that first if(!windows.empty()) { IngameWindow* foundWindow = FindWindowAtPos(mc.pos); @@ -426,36 +311,11 @@ void WindowManager::Msg_RightDown(const MouseCoords& mc) { if(!foundWindow->IsPinned()) foundWindow->Close(); - } else - { - SetActiveWindow(*foundWindow); - foundWindow->Msg_RightDown(mc); + return; } - return; } } - - // ist der Desktop aktiv? - if(curDesktop->IsActive()) - { - // ja, dann Msg_RightDown aufrufen - curDesktop->Msg_RightDown(mc); - - // und die Fenster darunter auch - curDesktop->RelayMouseMessage(&Window::Msg_RightDown, mc); - } else if(!windows.empty()) - { - // dann Nachricht an Fenster weiterleiten - windows.back()->RelayMouseMessage(&Window::Msg_RightDown, mc); - } - - SetActiveWindow(*curDesktop); - - // ja, dann Msg_RightDown aufrufen - curDesktop->Msg_RightDown(mc); - - // und die Fenster darunter auch - curDesktop->RelayMouseMessage(&Window::Msg_RightDown, mc); + RelayMouseMessage(&Window::Msg_RightDown, mc, findAndActivateWindow(mc.pos)); } void WindowManager::Msg_RightUp(const MouseCoords& mc) @@ -463,141 +323,20 @@ void WindowManager::Msg_RightUp(const MouseCoords& mc) RelayMouseMessage(&Window::Msg_RightUp, mc); } -/** - * Verarbeitung Mausrad hoch. - * - * @param[in] mc Mauskoordinaten Struktur - */ void WindowManager::Msg_WheelUp(const MouseCoords& mc) { - // ist unser Desktop gültig? - if(!curDesktop) - return; - - // haben wir überhaupt fenster? - if(windows.empty()) - { - SetActiveWindow(*curDesktop); - - // nein, Msg_LeftDown aufrufen - curDesktop->Msg_WheelUp(mc); - - // und allen unten drunter auch Bescheid sagen - curDesktop->RelayMouseMessage(&Window::Msg_WheelUp, mc); - - // und raus - return; - } - - // ist das zuletzt aktiv gewesene Fenster Modal? - IngameWindow& activeWnd = *windows.back(); - if(activeWnd.IsModal()) - { - // Msg_LeftDownaufrufen - activeWnd.Msg_WheelUp(mc); - - // und allen unten drunter auch Bescheid sagen - activeWnd.RelayMouseMessage(&Window::Msg_WheelUp, mc); - - // und raus - return; - } - - IngameWindow* foundWindow = FindWindowAtPos(mc.pos); - - if(foundWindow) - { - SetActiveWindow(*foundWindow); - - // dann Msg_WheelUp aufrufen - foundWindow->Msg_WheelUp(mc); - - // und allen unten drunter auch Bescheid sagen - foundWindow->RelayMouseMessage(&Window::Msg_WheelUp, mc); - } else - { - SetActiveWindow(*curDesktop); - - // nein, dann Msg_WheelUpDown aufrufen - curDesktop->Msg_WheelUp(mc); - - // und allen unten drunter auch Bescheid sagen - curDesktop->RelayMouseMessage(&Window::Msg_WheelUp, mc); - } + RelayMouseMessage(&Window::Msg_WheelUp, mc, findAndActivateWindow(mc.pos)); } -/** - * Verarbeitung Mausrad runter - * - * @param[in] mc Mauskoordinaten Struktur - */ void WindowManager::Msg_WheelDown(const MouseCoords& mc) { - if(!curDesktop) - return; - if(windows.empty()) - { - SetActiveWindow(*curDesktop); - curDesktop->Msg_WheelDown(mc); - curDesktop->RelayMouseMessage(&Window::Msg_WheelDown, mc); - // und raus - return; - } - IngameWindow& activeWnd = *windows.back(); - if(activeWnd.IsModal()) - { - activeWnd.Msg_WheelDown(mc); - activeWnd.RelayMouseMessage(&Window::Msg_WheelDown, mc); - return; - } - IngameWindow* foundWindow = FindWindowAtPos(mc.pos); - - if(foundWindow) - { - SetActiveWindow(*foundWindow); - foundWindow->Msg_WheelDown(mc); - foundWindow->RelayMouseMessage(&Window::Msg_WheelDown, mc); - } else - { - SetActiveWindow(*curDesktop); - curDesktop->Msg_WheelDown(mc); - curDesktop->RelayMouseMessage(&Window::Msg_WheelDown, mc); - } + RelayMouseMessage(&Window::Msg_WheelDown, mc, findAndActivateWindow(mc.pos)); } -/** - * Verarbeitung des Verschiebens der Maus. - * - * @param[in] mc Mauskoordinaten Struktur - */ void WindowManager::Msg_MouseMove(const MouseCoords& mc) { lastMousePos = mc.pos; - - // ist unser Desktop gültig? - if(!curDesktop) - return; - - // nein, ist unser Desktop aktiv? - if(curDesktop->IsActive()) - { - // ja, dann Msg_MouseMove aufrufen - curDesktop->Msg_MouseMove(mc); - - // und alles drunter auch benachrichtigen - curDesktop->RelayMouseMessage(&Window::Msg_MouseMove, mc); - } else if(!windows.empty()) - { - IngameWindow& activeWnd = *windows.back(); - // und MouseMove vom Fenster aufrufen - activeWnd.MouseMove(mc); - - // ja, dann Msg_MouseMove aufrufen - activeWnd.Msg_MouseMove(mc); - - // und alles drunter auch benachrichtigen - activeWnd.RelayMouseMessage(&Window::Msg_MouseMove, mc); - } + RelayMouseMessage(&Window::Msg_MouseMove, mc); } void WindowManager::Msg_KeyDown(const KeyEvent& ke) diff --git a/libs/s25main/WindowManager.h b/libs/s25main/WindowManager.h index 9927c84dfd..e2754fdd15 100644 --- a/libs/s25main/WindowManager.h +++ b/libs/s25main/WindowManager.h @@ -10,6 +10,7 @@ #include "s25util/Singleton.h" #include #include +#include #include #include @@ -44,13 +45,13 @@ class WindowManager : public Singleton, public VideoDriverLoaderI /// Zeichnet Desktop und alle Fenster. void Draw(); /// liefert ob der aktuelle Desktop den Focus besitzt oder nicht. - bool IsDesktopActive(); + bool IsDesktopActive() const; /// schickt eine Nachricht an das aktive Fenster bzw den aktiven Desktop. /// Sendet eine Tastaturnachricht an die Steuerelemente. void RelayKeyboardMessage(KeyboardMsgHandler msg, const KeyEvent& ke); /// Sendet eine Mausnachricht weiter an alle Steuerelemente - void RelayMouseMessage(MouseMsgHandler msg, const MouseCoords& mc); + void RelayMouseMessage(MouseMsgHandler msg, const MouseCoords& mc, Window* window = nullptr); /// Öffnet ein IngameWindow und fügt es zur Fensterliste hinzu. IngameWindow& DoShow(std::unique_ptr window, bool mouse = false); @@ -131,6 +132,12 @@ class WindowManager : public Singleton, public VideoDriverLoaderI private: class Tooltip; + /// Find the active window (desktop or ingame window) + /// If mc is given get the window at the mouse position unless a modal window is active + Window* findAndActivateWindow(Position mousePos); + /// Get the active window (desktop or ingame window) + Window* getActiveWindow() const; + void DrawCursor(); void DrawToolTip(); diff --git a/libs/s25main/ingameWindows/IngameWindow.cpp b/libs/s25main/ingameWindows/IngameWindow.cpp index 261b283c37..85907bcb39 100644 --- a/libs/s25main/ingameWindows/IngameWindow.cpp +++ b/libs/s25main/ingameWindows/IngameWindow.cpp @@ -190,7 +190,7 @@ void IngameWindow::SetPinned(bool pinned) windowSettings_->isPinned = isPinned_; } -void IngameWindow::MouseLeftDown(const MouseCoords& mc) +bool IngameWindow::Msg_LeftDown(const MouseCoords& mc) { // Check if the mouse is on the title bar if(IsPointInRect(mc.pos, GetButtonBounds(IwButton::Title))) @@ -199,6 +199,7 @@ void IngameWindow::MouseLeftDown(const MouseCoords& mc) isMoving = true; snapOffset_ = SnapOffset::all(0); lastMousePos = mc.pos; + return true; } else { // Check the buttons @@ -208,9 +209,10 @@ void IngameWindow::MouseLeftDown(const MouseCoords& mc) buttonStates_[btn] = ButtonState::Pressed; } } + return false; } -void IngameWindow::MouseLeftUp(const MouseCoords& mc) +bool IngameWindow::Msg_LeftUp(const MouseCoords& mc) { isMoving = false; @@ -227,12 +229,13 @@ void IngameWindow::MouseLeftUp(const MouseCoords& mc) { switch(btn) { - case IwButton::Close: Close(); break; + case IwButton::Close: Close(); return true; case IwButton::Title: if(SETTINGS.interface.enableWindowPinning && mc.dbl_click) { SetMinimized(!IsMinimized()); LOADER.GetSoundN("sound", 113)->Play(255, false); + return true; } break; case IwButton::PinOrMinimize: @@ -245,13 +248,14 @@ void IngameWindow::MouseLeftUp(const MouseCoords& mc) SetMinimized(!IsMinimized()); LOADER.GetSoundN("sound", 113)->Play(255, false); } - break; + return true; } } } + return false; } -void IngameWindow::MouseMove(const MouseCoords& mc) +bool IngameWindow::Msg_MouseMove(const MouseCoords& mc) { if(isMoving) { @@ -278,16 +282,20 @@ void IngameWindow::MouseMove(const MouseCoords& mc) snapOffset_ = GetPos() - newPosBounded; lastMousePos = mc.pos; + return true; } else { // Check the buttons for(const auto btn : helpers::enumRange()) { if(IsPointInRect(mc.pos, GetButtonBounds(btn))) - buttonStates_[btn] = mc.ldown ? ButtonState::Pressed : ButtonState::Hover; - else + { + if(!mc.ldown) + buttonStates_[btn] = ButtonState::Hover; + } else buttonStates_[btn] = ButtonState::Up; } + return false; } } diff --git a/libs/s25main/ingameWindows/IngameWindow.h b/libs/s25main/ingameWindows/IngameWindow.h index 80a3ce9ea8..37e1760426 100644 --- a/libs/s25main/ingameWindows/IngameWindow.h +++ b/libs/s25main/ingameWindows/IngameWindow.h @@ -96,12 +96,12 @@ class IngameWindow : public Window /// Modal windows cannot be minimized, are always active and stay on top of non-modal ones bool IsModal() const { return isModal_; } - void MouseLeftDown(const MouseCoords& mc); - void MouseLeftUp(const MouseCoords& mc); - void MouseMove(const MouseCoords& mc); - GUI_ID GetGUIID() const { return static_cast(Window::GetID()); } + bool Msg_LeftDown(const MouseCoords&) override; + bool Msg_LeftUp(const MouseCoords&) override; + bool Msg_MouseMove(const MouseCoords&) override; + protected: void Draw_() final; /// Called when not minimized before drawing the frame diff --git a/libs/s25main/ingameWindows/iwObservate.cpp b/libs/s25main/ingameWindows/iwObservate.cpp index eb099862a7..445928c51b 100644 --- a/libs/s25main/ingameWindows/iwObservate.cpp +++ b/libs/s25main/ingameWindows/iwObservate.cpp @@ -253,9 +253,9 @@ bool iwObservate::Msg_MouseMove(const MouseCoords& mc) view->MoveBy((mc.pos - scrollOrigin) * acceleration); VIDEODRIVER.SetMousePos(scrollOrigin); - } - - return (false); + return true; + } else + return IngameWindow::Msg_MouseMove(mc); } bool iwObservate::Msg_RightDown(const MouseCoords& mc) diff --git a/tests/s25Main/UI/testIngameWindow.cpp b/tests/s25Main/UI/testIngameWindow.cpp index b20d5eb675..3fc6f5d2f1 100644 --- a/tests/s25Main/UI/testIngameWindow.cpp +++ b/tests/s25Main/UI/testIngameWindow.cpp @@ -296,7 +296,7 @@ BOOST_AUTO_TEST_CASE(TitleBarButtons) BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); - wnd.MouseLeftUp(evLDown); + wnd.Msg_LeftUp(evLDown); BOOST_TEST(wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); @@ -313,7 +313,7 @@ BOOST_AUTO_TEST_CASE(TitleBarButtons) BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); - wnd.MouseLeftUp(evLDblDown); + wnd.Msg_LeftUp(evLDblDown); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); @@ -331,7 +331,7 @@ BOOST_AUTO_TEST_CASE(TitleBarButtons) BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); - wnd.MouseLeftUp(evLDown); + wnd.Msg_LeftUp(evLDown); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); @@ -354,7 +354,7 @@ BOOST_AUTO_TEST_CASE(TitleBarButtons) BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); - wnd.MouseLeftUp(evLDown); + wnd.Msg_LeftUp(evLDown); BOOST_TEST(wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); @@ -371,7 +371,7 @@ BOOST_AUTO_TEST_CASE(TitleBarButtons) BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); - wnd.MouseLeftUp(evLDblDown); + wnd.Msg_LeftUp(evLDblDown); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); @@ -390,7 +390,7 @@ BOOST_AUTO_TEST_CASE(TitleBarButtons) BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); - wnd.MouseLeftUp(evLDown); + wnd.Msg_LeftUp(evLDown); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(wnd.IsPinned()); @@ -577,12 +577,12 @@ BOOST_AUTO_TEST_CASE(WindowSnapping) { const DrawPoint mousePosRel = DrawPoint(wnd2.GetSize().x / 2, 4); const MouseCoords evLDown = makeLeftDown(wnd2.GetPos() + mousePosRel); - wnd2.MouseLeftDown(evLDown); + wnd2.Msg_LeftDown(evLDown); { const auto mc = makeLeftDown(evLDown.pos + DrawPoint(-80, 0)); VIDEODRIVER.SetMousePos(mc.pos); - wnd2.MouseMove(mc); + wnd2.Msg_MouseMove(mc); BOOST_TEST(wnd2.GetPos() == DrawPoint(120, 480)); // Not in snap range yet BOOST_TEST(VIDEODRIVER.GetMousePos() == (wnd2.GetPos() + mousePosRel)); } @@ -590,7 +590,7 @@ BOOST_AUTO_TEST_CASE(WindowSnapping) { const auto mc = makeLeftDown(evLDown.pos + DrawPoint(-90, 0)); VIDEODRIVER.SetMousePos(mc.pos); - wnd2.MouseMove(mc); + wnd2.Msg_MouseMove(mc); BOOST_TEST(wnd2.GetPos() == DrawPoint(100, 480)); // In snap range BOOST_TEST(VIDEODRIVER.GetMousePos() == mc.pos); // Cursor position is off by snap offset @@ -599,9 +599,9 @@ BOOST_AUTO_TEST_CASE(WindowSnapping) { const auto mc = makeLeftDown(evLDown.pos + DrawPoint(-90, 30)); - // Reset mouse position to ensure it's properly updated in MouseMove() + // Reset mouse position to ensure it's properly updated in Msg_MouseMove() VIDEODRIVER.SetMousePos(Position(0, 0)); - wnd2.MouseMove(mc); + wnd2.Msg_MouseMove(mc); BOOST_TEST(wnd2.GetPos() == DrawPoint(100, 500)); // Still in snap range BOOST_TEST(VIDEODRIVER.GetMousePos() == Position(mc.pos.x, wnd2.GetPos().y + mousePosRel.y)); // Cursor position is off by snap offset @@ -616,14 +616,14 @@ BOOST_AUTO_TEST_CASE(WindowSnapping) const DrawPoint mousePosRel = DrawPoint(wnd3.GetSize().x / 2, 4); const MouseCoords evLDown = makeLeftDown(wnd3.GetPos() + mousePosRel); - wnd3.MouseLeftDown(evLDown); + wnd3.Msg_LeftDown(evLDown); { wnd1.SetPos(DrawPoint(0, 0)); wnd2.SetPos(DrawPoint(5, 0)); const auto mc = makeLeftDown(evLDown.pos + DrawPoint(-90, 0)); VIDEODRIVER.SetMousePos(mc.pos); - wnd3.MouseMove(mc); + wnd3.Msg_MouseMove(mc); // In snap range of wnd1 and wnd2, but closest to wnd2 BOOST_TEST(wnd3.GetPos() == DrawPoint(wnd2.GetPos().x + wnd2.GetSize().x, 0)); BOOST_TEST(VIDEODRIVER.GetMousePos() == mc.pos); @@ -636,7 +636,7 @@ BOOST_AUTO_TEST_CASE(WindowSnapping) wnd2.SetPos(DrawPoint(0, 0)); const auto mc = makeLeftDown(evLDown.pos + DrawPoint(-90, 0)); VIDEODRIVER.SetMousePos(mc.pos); - wnd3.MouseMove(mc); + wnd3.Msg_MouseMove(mc); // In snap range of wnd1 and wnd2, but closest to wnd1 BOOST_TEST(wnd3.GetPos() == DrawPoint(wnd1.GetPos().x + wnd1.GetSize().x, 0)); BOOST_TEST(VIDEODRIVER.GetMousePos() == mc.pos); From 2dbf819488d293321e51b359d6967a5ee42c6791 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sat, 27 Dec 2025 14:19:53 +0100 Subject: [PATCH 2/3] Fix wrongly detected clicks on title bar of ingame windows When moving the mouse to the button while holding the left mouse button the click should not be registered, nor should the button show as pressed. --- libs/s25main/ingameWindows/IngameWindow.cpp | 27 ++++++------ tests/s25Main/UI/testIngameWindow.cpp | 49 +++++++++++++++++---- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/libs/s25main/ingameWindows/IngameWindow.cpp b/libs/s25main/ingameWindows/IngameWindow.cpp index 85907bcb39..ad9b2b6efb 100644 --- a/libs/s25main/ingameWindows/IngameWindow.cpp +++ b/libs/s25main/ingameWindows/IngameWindow.cpp @@ -192,21 +192,19 @@ void IngameWindow::SetPinned(bool pinned) bool IngameWindow::Msg_LeftDown(const MouseCoords& mc) { - // Check if the mouse is on the title bar - if(IsPointInRect(mc.pos, GetButtonBounds(IwButton::Title))) - { - // start moving - isMoving = true; - snapOffset_ = SnapOffset::all(0); - lastMousePos = mc.pos; - return true; - } else + for(const auto btn : helpers::enumRange()) { - // Check the buttons - for(const auto btn : helpers::enumRange()) + if(IsPointInRect(mc.pos, GetButtonBounds(btn))) { - if(IsPointInRect(mc.pos, GetButtonBounds(btn))) - buttonStates_[btn] = ButtonState::Pressed; + buttonStates_[btn] = ButtonState::Pressed; + if(btn == IwButton::Title) + { + // start moving + isMoving = true; + snapOffset_ = SnapOffset::all(0); + lastMousePos = mc.pos; + return true; + } } } return false; @@ -218,7 +216,10 @@ bool IngameWindow::Msg_LeftUp(const MouseCoords& mc) for(const auto btn : helpers::enumRange()) { + const auto clicked = buttonStates_[btn] == ButtonState::Pressed; buttonStates_[btn] = ButtonState::Up; + if(!clicked) + continue; if((btn == IwButton::Close && closeBehavior_ == CloseBehavior::Custom) // no close button || (isModal_ // modal windows cannot be pinned or minimized diff --git a/tests/s25Main/UI/testIngameWindow.cpp b/tests/s25Main/UI/testIngameWindow.cpp index 3fc6f5d2f1..cf2549d587 100644 --- a/tests/s25Main/UI/testIngameWindow.cpp +++ b/tests/s25Main/UI/testIngameWindow.cpp @@ -51,7 +51,7 @@ static MouseCoords makeLeftDown(const Position pos) static MouseCoords makeLeftDblClick(const Position pos) { MouseCoords mc(pos); - mc.ldown = mc.dbl_click = true; + mc.dbl_click = true; return mc; } @@ -292,11 +292,32 @@ BOOST_AUTO_TEST_CASE(TitleBarButtons) { IngameWindow wnd(id, IngameWindow::posLastOrCenter, Extent(100, 100), "Test Window", nullptr); const MouseCoords evLDown = makeLeftDown(wnd.GetPos() + DrawPoint(4, 4)); + const MouseCoords evLDownInsideWindow = makeLeftDown(wnd.GetPos() + wnd.GetSize() / 2); + const MouseCoords evLUp = MouseCoords(evLDown.pos); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); - wnd.Msg_LeftUp(evLDown); + + // Only react when clicking the button not when moving the mouse to the button + // while holding left mouse button + wnd.Msg_LeftDown(evLDownInsideWindow); + wnd.Msg_MouseMove(evLDown); + wnd.Msg_LeftUp(evLUp); + BOOST_TEST(!wnd.ShouldBeClosed()); + BOOST_TEST(!wnd.IsMinimized()); + BOOST_TEST(!wnd.IsPinned()); + // Similar when moving out of the button + wnd.Msg_LeftDown(evLDown); + wnd.Msg_MouseMove(evLDownInsideWindow); + wnd.Msg_MouseMove(evLDown); + wnd.Msg_LeftUp(evLUp); + BOOST_TEST(!wnd.ShouldBeClosed()); + BOOST_TEST(!wnd.IsMinimized()); + BOOST_TEST(!wnd.IsPinned()); + + wnd.Msg_LeftDown(evLDown); + wnd.Msg_LeftUp(evLUp); BOOST_TEST(wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); @@ -308,12 +329,14 @@ BOOST_AUTO_TEST_CASE(TitleBarButtons) BOOST_TEST_CONTEXT("Double-click on the window title has no effect") { IngameWindow wnd(id, IngameWindow::posLastOrCenter, Extent(100, 100), "Test Window", nullptr); - const MouseCoords evLDblDown = makeLeftDblClick(wnd.GetPos() + DrawPoint(wnd.GetSize().x / 2, 4)); + const MouseCoords evLDown = makeLeftDown(wnd.GetPos() + DrawPoint(wnd.GetSize().x / 2, 4)); + const MouseCoords evLDblClick = makeLeftDblClick(evLDown.pos); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); - wnd.Msg_LeftUp(evLDblDown); + wnd.Msg_LeftDown(evLDown); + wnd.Msg_LeftUp(evLDblClick); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); @@ -327,11 +350,13 @@ BOOST_AUTO_TEST_CASE(TitleBarButtons) { IngameWindow wnd(id, IngameWindow::posLastOrCenter, Extent(100, 100), "Test Window", nullptr); const MouseCoords evLDown = makeLeftDown(wnd.GetPos() + DrawPoint(wnd.GetSize().x, 0) + DrawPoint(-4, 4)); + const MouseCoords evLUp = MouseCoords(evLDown.pos); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); - wnd.Msg_LeftUp(evLDown); + wnd.Msg_LeftDown(evLDown); + wnd.Msg_LeftUp(evLUp); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); @@ -350,11 +375,13 @@ BOOST_AUTO_TEST_CASE(TitleBarButtons) { IngameWindow wnd(id, IngameWindow::posLastOrCenter, Extent(100, 100), "Test Window", nullptr); const MouseCoords evLDown = makeLeftDown(wnd.GetPos() + DrawPoint(4, 4)); + const MouseCoords evLUp = MouseCoords(evLDown.pos); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); - wnd.Msg_LeftUp(evLDown); + wnd.Msg_LeftDown(evLDown); + wnd.Msg_LeftUp(evLUp); BOOST_TEST(wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); @@ -366,12 +393,14 @@ BOOST_AUTO_TEST_CASE(TitleBarButtons) BOOST_TEST_CONTEXT("Double-click on the window title minimizes") { IngameWindow wnd(id, IngameWindow::posLastOrCenter, Extent(100, 100), "Test Window", nullptr); - const MouseCoords evLDblDown = makeLeftDblClick(wnd.GetPos() + DrawPoint(wnd.GetSize().x / 2, 4)); + const MouseCoords evLDown = makeLeftDown(wnd.GetPos() + DrawPoint(wnd.GetSize().x / 2, 4)); + const MouseCoords evLDblClick = makeLeftDblClick(evLDown.pos); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); - wnd.Msg_LeftUp(evLDblDown); + wnd.Msg_LeftDown(evLDown); + wnd.Msg_LeftUp(evLDblClick); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); @@ -386,11 +415,13 @@ BOOST_AUTO_TEST_CASE(TitleBarButtons) { IngameWindow wnd(id, IngameWindow::posLastOrCenter, Extent(100, 100), "Test Window", nullptr); const MouseCoords evLDown = makeLeftDown(wnd.GetPos() + DrawPoint(wnd.GetSize().x, 0) + DrawPoint(-4, 4)); + const MouseCoords evLUp = MouseCoords(evLDown.pos); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(!wnd.IsPinned()); - wnd.Msg_LeftUp(evLDown); + wnd.Msg_LeftDown(evLDown); + wnd.Msg_LeftUp(evLUp); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(wnd.IsPinned()); From c015ce8be3921d563065355d1421af970fdb62a9 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sat, 27 Dec 2025 15:28:46 +0100 Subject: [PATCH 3/3] Add option for window pinning to settings screen Also make layout more adaptive --- libs/s25main/desktops/dskOptions.cpp | 84 ++++++++++++++++++---------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/libs/s25main/desktops/dskOptions.cpp b/libs/s25main/desktops/dskOptions.cpp index 92bc6f6955..1b1fb403e2 100644 --- a/libs/s25main/desktops/dskOptions.cpp +++ b/libs/s25main/desktops/dskOptions.cpp @@ -36,6 +36,8 @@ #include namespace { +using Offset = DrawPoint; + enum { ID_btBack = dskMenuBase::ID_FIRST_FREE, @@ -71,6 +73,8 @@ enum ID_grpInvertScroll, ID_txtSmartCursor, ID_grpSmartCursor, + ID_txtWindowPinning, + ID_grpWindowPinning, ID_txtGFInfo, ID_grpGFInfo, ID_txtResolution, @@ -112,8 +116,13 @@ constexpr auto ID_btSubmitDebugAsk = 2; constexpr auto rowHeight = 30; constexpr auto sectionSpacing = 20; constexpr auto sectionSpacingCommon = 10; -constexpr auto tabButtonsStartPosition = DrawPoint(80, 510); -constexpr auto optionRowsStartPosition = DrawPoint(80, 80); +constexpr auto optionRowsStartPosition = DrawPoint(80, 75); +constexpr auto tabButtonsStartPosition = optionRowsStartPosition + Offset(0, static_cast(rowHeight * 15.5)); + +constexpr Offset ctrlOffset(200, -5); // Offset of control to its description text +constexpr Offset ctrlOffset2 = ctrlOffset + Offset(200, 0); // Offset of 2nd control to its description text +constexpr Extent ctrlSize(190, 22); +constexpr Extent ctrlSizeLarge = ctrlSize + Extent(ctrlOffset2 - ctrlOffset); } // namespace static VideoMode getAspectRatio(const VideoMode& vm) @@ -149,7 +158,7 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) _("Graphics"), NormalFont); mainGroup->AddTextButton(ID_btSound, DrawPoint(curPos.x + 440, curPos.y), Extent(200, 22), TextureColor::Green2, _("Sound/Music"), NormalFont); - curPos.y += rowHeight + sectionSpacingCommon; + curPos.y += rowHeight; AddTextButton(ID_btBack, DrawPoint(curPos.x + 220, curPos.y), Extent(200, 22), TextureColor::Red1, _("Back"), NormalFont); @@ -165,11 +174,6 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) // { curPos = optionRowsStartPosition; - using Offset = DrawPoint; - constexpr Offset ctrlOffset(200, -5); // Offset of control to its description text - constexpr Offset ctrlOffset2 = ctrlOffset + Offset(200, 0); // Offset of 2nd control to its description text - constexpr Extent ctrlSize(190, 22); - constexpr Extent ctrlSizeLarge = ctrlSize + Extent(ctrlOffset2 - ctrlOffset); groupCommon->AddText(ID_txtName, curPos, _("Name in Game:"), COLOR_YELLOW, FontStyle{}, NormalFont); ctrlEdit* name = @@ -177,22 +181,20 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) name->SetText(SETTINGS.lobby.name); const auto& currentPortrait = Portraits[SETTINGS.lobby.portraitIndex]; - groupCommon->AddImageButton(ID_btCommonPortrait, DrawPoint(500, curPos.y - 5), Extent(40, 54), TextureColor::Grey, + groupCommon->AddImageButton(ID_btCommonPortrait, DrawPoint(500, curPos.y + ctrlOffset.y), Extent(40, rowHeight * 2), + TextureColor::Grey, LOADER.GetImageN(currentPortrait.resourceId, currentPortrait.resourceIndex)); curPos.y += rowHeight; - groupCommon->AddText(ID_txtCommonPortrait, DrawPoint(80, curPos.y), _("Portrait:"), COLOR_YELLOW, FontStyle{}, - NormalFont); - combo = groupCommon->AddComboBox(ID_cbCommonPortrait, DrawPoint(280, curPos.y - 5), Extent(190, 20), - TextureColor::Grey, NormalFont, 100); + groupCommon->AddText(ID_txtCommonPortrait, curPos, _("Portrait:"), COLOR_YELLOW, FontStyle{}, NormalFont); + combo = + groupCommon->AddComboBox(ID_cbCommonPortrait, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, NormalFont, 100); for(unsigned i = 0; i < Portraits.size(); ++i) { combo->AddString(_(Portraits[i].name)); if(SETTINGS.lobby.portraitIndex == i) - { combo->SetSelection(i); - } } curPos.y += rowHeight; @@ -217,8 +219,9 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) groupCommon->AddTextButton(ID_btKeyboardLayout, curPos + ctrlOffset, ctrlSizeLarge, TextureColor::Grey, _("Keyboard layout"), NormalFont); - curPos.y += rowHeight + sectionSpacingCommon; + curPos.y += rowHeight; + curPos.y += sectionSpacingCommon; groupCommon->AddText(ID_txtPort, curPos, _("Local Port:"), COLOR_YELLOW, FontStyle{}, NormalFont); ctrlEdit* edtPort = groupCommon->AddEdit(ID_edtPort, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, NormalFont, 15); @@ -235,8 +238,9 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) ipv6->SetSelection(SETTINGS.server.ipv6); // Enable/disable the IPv6 field if necessary ipv6->GetCtrl(1)->SetEnabled(SETTINGS.proxy.type != ProxyType::Socks5); //-V807 - curPos.y += rowHeight + sectionSpacingCommon; + curPos.y += rowHeight; + curPos.y += sectionSpacingCommon; // Proxy server groupCommon->AddText(ID_txtProxy, curPos, _("Proxyserver:"), COLOR_YELLOW, FontStyle{}, NormalFont); ctrlEdit* proxy = groupCommon->AddEdit(ID_edtProxy, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, NormalFont); @@ -269,8 +273,9 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) case ProxyType::Socks4: combo->SetSelection(1); break; case ProxyType::Socks5: combo->SetSelection(2); break; } - curPos.y += rowHeight + sectionSpacingCommon; + curPos.y += rowHeight; + curPos.y += sectionSpacingCommon; groupCommon->AddText(ID_txtInvertScroll, curPos, _("Invert Mouse Pan:"), COLOR_YELLOW, FontStyle{}, NormalFont); ctrlOptionGroup* invertScroll = groupCommon->AddOptionGroup(ID_grpInvertScroll, GroupSelectType::Check); invertScroll->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("On"), NormalFont, @@ -288,8 +293,18 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Off"), NormalFont, _("Don't move cursor automatically\nUseful e.g. for split-screen / dual-mice multiplayer (see wiki)")); smartCursor->SetSelection(SETTINGS.global.smartCursor); - curPos.y += rowHeight + sectionSpacingCommon; + curPos.y += rowHeight; + + groupCommon->AddText(ID_txtWindowPinning, curPos, _("Window pinning"), COLOR_YELLOW, FontStyle{}, NormalFont); + ctrlOptionGroup* windowPinning = groupCommon->AddOptionGroup(ID_grpWindowPinning, GroupSelectType::Check); + windowPinning->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("On"), NormalFont, + _("Replace minimize button on windows by pin button avoiding closing the window with " + "ESC.\nMinimize by double-clicking the title bar.")); + windowPinning->AddTextButton(ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Off"), NormalFont); + windowPinning->SetSelection(SETTINGS.interface.enableWindowPinning); + curPos.y += rowHeight; + curPos.y += sectionSpacingCommon; groupCommon->AddText(ID_txtDebugData, curPos, _("Submit debug data:"), COLOR_YELLOW, FontStyle{}, NormalFont); mainGroup = groupCommon->AddOptionGroup(ID_grpDebugData, GroupSelectType::Check); mainGroup->AddTextButton(ID_btSubmitDebugOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("On"), @@ -312,24 +327,28 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) groupGraphics->AddText(ID_txtResolution, curPos, _("Fullscreen resolution:"), COLOR_YELLOW, FontStyle{}, NormalFont); groupGraphics->AddComboBox(ID_cbResolution, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, NormalFont, 150); - curPos.y += rowHeight + sectionSpacing; + curPos.y += rowHeight; + curPos.y += sectionSpacing; groupGraphics->AddText(ID_txtFullscreen, curPos, _("Mode:"), COLOR_YELLOW, FontStyle{}, NormalFont); mainGroup = groupGraphics->AddOptionGroup(ID_grpFullscreen, GroupSelectType::Check); mainGroup->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("Fullscreen"), NormalFont); mainGroup->AddTextButton(ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Windowed"), NormalFont); - curPos.y += rowHeight + sectionSpacing; + curPos.y += rowHeight; + curPos.y += sectionSpacing; groupGraphics->AddText(ID_txtFramerate, curPos, _("Limit Framerate:"), COLOR_YELLOW, FontStyle{}, NormalFont); groupGraphics->AddComboBox(ID_cbFramerate, curPos + ctrlOffset, ctrlSizeLarge, TextureColor::Grey, NormalFont, 150); - curPos.y += rowHeight + sectionSpacing; + curPos.y += rowHeight; + curPos.y += sectionSpacing; groupGraphics->AddText(ID_txtVBO, curPos, _("Vertex Buffer Objects:"), COLOR_YELLOW, FontStyle{}, NormalFont); mainGroup = groupGraphics->AddOptionGroup(ID_grpVBO, GroupSelectType::Check); mainGroup->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("On"), NormalFont); mainGroup->AddTextButton(ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Off"), NormalFont); - curPos.y += rowHeight + sectionSpacing; + curPos.y += rowHeight; + curPos.y += sectionSpacing; groupGraphics->AddText(ID_txtVideoDriver, curPos, _("Graphics Driver"), COLOR_YELLOW, FontStyle{}, NormalFont); combo = groupGraphics->AddComboBox(ID_cbVideoDriver, curPos + ctrlOffset, ctrlSizeLarge, TextureColor::Grey, NormalFont, 100); @@ -342,15 +361,17 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) if(video_driver.GetName() == SETTINGS.driver.video) combo->SetSelection(combo->GetNumItems() - 1); } - curPos.y += rowHeight + sectionSpacing; + curPos.y += rowHeight; + curPos.y += sectionSpacing; groupGraphics->AddText(ID_txtOptTextures, curPos, _("Optimized Textures:"), COLOR_YELLOW, FontStyle{}, NormalFont); mainGroup = groupGraphics->AddOptionGroup(ID_grpOptTextures, GroupSelectType::Check); mainGroup->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("On"), NormalFont); mainGroup->AddTextButton(ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Off"), NormalFont); - curPos.y += rowHeight + sectionSpacing; + curPos.y += rowHeight; + curPos.y += sectionSpacing; groupGraphics->AddText(ID_txtGuiScale, curPos, _("GUI Scale:"), COLOR_YELLOW, FontStyle{}, NormalFont); groupGraphics->AddComboBox(ID_cbGuiScale, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, NormalFont, 100); updateGuiScale(); @@ -369,14 +390,16 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) ctrlProgress* FXvolume = groupSound->AddProgress(ID_pgEffectsVol, curPos + volOffset, ctrlSize, TextureColor::Grey, 139, 138, 100); FXvolume->SetPosition((SETTINGS.sound.effectsVolume * 100) / 255); - curPos.y += rowHeight + sectionSpacing; + curPos.y += rowHeight; + curPos.y += sectionSpacing; groupSound->AddText(ID_txtBirdSounds, curPos, _("Bird sounds"), COLOR_YELLOW, FontStyle{}, NormalFont); mainGroup = groupSound->AddOptionGroup(ID_grpBirdSounds, GroupSelectType::Check); mainGroup->AddTextButton(ID_btOn, curPos + bt1Offset, ctrlSizeSmall, TextureColor::Grey, _("On"), NormalFont); mainGroup->AddTextButton(ID_btOff, curPos + bt2Offset, ctrlSizeSmall, TextureColor::Grey, _("Off"), NormalFont); - curPos.y += rowHeight + sectionSpacing; + curPos.y += rowHeight; + curPos.y += sectionSpacing; groupSound->AddText(ID_txtMusic, curPos, _("Music"), COLOR_YELLOW, FontStyle{}, NormalFont); mainGroup = groupSound->AddOptionGroup(ID_grpMusic, GroupSelectType::Check); mainGroup->AddTextButton(ID_btOn, curPos + bt1Offset, ctrlSizeSmall, TextureColor::Grey, _("On"), NormalFont); @@ -385,12 +408,14 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) ctrlProgress* Mvolume = groupSound->AddProgress(ID_pgMusicVol, curPos + volOffset, ctrlSize, TextureColor::Grey, 139, 138, 100); Mvolume->SetPosition((SETTINGS.sound.musicVolume * 100) / 255); //-V807 - curPos.y += rowHeight + sectionSpacing; + curPos.y += rowHeight; + curPos.y += sectionSpacing; groupSound->AddTextButton(ID_btMusicPlayer, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("Music player"), NormalFont); - curPos.y += rowHeight + sectionSpacing; + curPos.y += rowHeight; + curPos.y += sectionSpacing; groupSound->AddText(ID_txtAudioDriver, curPos, _("Sounddriver"), COLOR_YELLOW, FontStyle{}, NormalFont); combo = groupSound->AddComboBox(ID_cbAudioDriver, curPos + ctrlOffset, ctrlSizeLarge, TextureColor::Grey, NormalFont, 100); @@ -588,6 +613,7 @@ void dskOptions::Msg_Group_OptionGroupChange(const unsigned /*group_id*/, const SETTINGS.global.smartCursor = enabled; VIDEODRIVER.SetMouseWarping(enabled); break; + case ID_grpWindowPinning: SETTINGS.interface.enableWindowPinning = enabled; break; case ID_grpGFInfo: SETTINGS.global.showGFInfo = enabled; break; } }