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/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; } } diff --git a/libs/s25main/ingameWindows/IngameWindow.cpp b/libs/s25main/ingameWindows/IngameWindow.cpp index 261b283c37..ad9b2b6efb 100644 --- a/libs/s25main/ingameWindows/IngameWindow.cpp +++ b/libs/s25main/ingameWindows/IngameWindow.cpp @@ -190,33 +190,36 @@ 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))) - { - // start moving - isMoving = true; - snapOffset_ = SnapOffset::all(0); - lastMousePos = mc.pos; - } 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; } -void IngameWindow::MouseLeftUp(const MouseCoords& mc) +bool IngameWindow::Msg_LeftUp(const MouseCoords& mc) { isMoving = false; 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 @@ -227,12 +230,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 +249,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 +283,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..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.MouseLeftUp(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.MouseLeftUp(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.MouseLeftUp(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.MouseLeftUp(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.MouseLeftUp(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.MouseLeftUp(evLDown); + wnd.Msg_LeftDown(evLDown); + wnd.Msg_LeftUp(evLUp); BOOST_TEST(!wnd.ShouldBeClosed()); BOOST_TEST(!wnd.IsMinimized()); BOOST_TEST(wnd.IsPinned()); @@ -577,12 +608,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 +621,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 +630,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 +647,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 +667,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);