From 9f80fd025bbcaf66b25a64796e8f34b335c8c1ec Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Tue, 20 Feb 2024 14:34:28 -0500 Subject: [PATCH 1/8] Move radar and console message drawing to intDisplayWidgets And get rid of the bRender3DOnly global --- src/display3d.cpp | 17 ----------------- src/display3d.h | 1 - src/hci.cpp | 13 +++++++++++++ src/loop.cpp | 1 - 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/display3d.cpp b/src/display3d.cpp index 685d27bff27..6f32364d1c0 100644 --- a/src/display3d.cpp +++ b/src/display3d.cpp @@ -160,7 +160,6 @@ static gfx_api::buffer* pScreenTriangleVBO = nullptr; // To get the real camera position, still need to add Vector3i(player.p.x, 0, player.p.z). static Vector3i actualCameraPosition; -bool bRender3DOnly; static bool bRangeDisplay = false; static SDWORD rangeCenterX, rangeCenterY, rangeRadius; static bool bDrawProximitys = true; @@ -1031,22 +1030,6 @@ void draw3DScene() drawDroidAndStructureSelections(); - if (!bRender3DOnly) - { - if (radarVisible()) - { - pie_SetFogStatus(false); - gfx_api::context::get().debugStringMarker("Draw 3D scene - radar"); - drawRadar(); - pie_SetFogStatus(true); - } - - /* Ensure that any text messages are displayed at bottom of screen */ - pie_SetFogStatus(false); - displayConsoleMessages(); - bRender3DOnly = true; - } - pie_SetFogStatus(false); iV_SetTextColour(WZCOL_TEXT_BRIGHT); diff --git a/src/display3d.h b/src/display3d.h index 7c7a464caf8..d03feec0313 100644 --- a/src/display3d.h +++ b/src/display3d.h @@ -118,7 +118,6 @@ void display3dScreenSizeDidChange(unsigned int oldWidth, unsigned int oldHeight, extern SDWORD mouseTileX, mouseTileY; extern Vector2i mousePos; -extern bool bRender3DOnly; extern bool showGateways; extern bool showPath; extern const Vector2i visibleTiles; diff --git a/src/hci.cpp b/src/hci.cpp index fc360158d05..a01dd98085f 100644 --- a/src/hci.cpp +++ b/src/hci.cpp @@ -75,6 +75,7 @@ #include "keybind.h" #include "qtscript.h" #include "chat.h" +#include "radar.h" #include "hci/build.h" #include "hci/research.h" #include "hci/manufacture.h" @@ -1941,6 +1942,18 @@ void intDisplayWidgets() } } + if (!gameUpdatePaused()) + { + if (radarVisible()) + { + gfx_api::context::get().debugStringMarker("Draw 3D scene - radar"); + drawRadar(); + } + + /* Ensure that any text messages are displayed at bottom of screen */ + displayConsoleMessages(); + } + widgDisplayScreen(psWScreen); if (bLoadSaveUp) diff --git a/src/loop.cpp b/src/loop.cpp index 71d505b1917..eb34098cc89 100644 --- a/src/loop.cpp +++ b/src/loop.cpp @@ -321,7 +321,6 @@ static GAMECODE renderLoop() { processMouseClickInput(); } - bRender3DOnly = false; displayWorld(); } wzPerfBegin(PERF_GUI, "User interface"); From 086e3b4a15453104129acc8138e28efc0b947215 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Tue, 20 Feb 2024 20:29:33 -0500 Subject: [PATCH 2/8] addSideText: Transparent to mouse / clicks --- src/frontend.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend.cpp b/src/frontend.cpp index 4229f5bcb68..a0dda42d935 100644 --- a/src/frontend.cpp +++ b/src/frontend.cpp @@ -3834,6 +3834,8 @@ std::shared_ptr addSideText(WIDGET* psParent, UDWORD id, UDWORD PosX, U }; auto psLabel = std::make_shared(&sLabInit); + psLabel->setTransparentToClicks(true); + psLabel->setTransparentToMouse(true); psParent->attach(psLabel); return psLabel; } From 7e483d043bfd1827b8f130a275cba581085e294e Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Tue, 20 Feb 2024 20:38:38 -0500 Subject: [PATCH 3/8] widget: Adjust child widget z-order handling Processing child widget clicks / input should match visual expectations based on the drawing z-order. i.e. Attaching a widget will now, by default, explicitly add it on top of all previously-added child widgets, and this means it will get to process clicks first (if overlapping an earlier-added widget) and will get drawn last (thus visually appearing "on top", if overlapping a previously-added child widget). WIDGET::attach now takes an optional zPos parameter which can be set to Back if there is a desire to attach a child widget at the bottom of the z-order instead of the top. --- lib/widget/widgbase.h | 7 ++++++- lib/widget/widget.cpp | 21 ++++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/widget/widgbase.h b/lib/widget/widgbase.h index 95b77b1a54d..7d8914dd41d 100644 --- a/lib/widget/widgbase.h +++ b/lib/widget/widgbase.h @@ -341,7 +341,12 @@ class WIDGET: public std::enable_shared_from_this void setGeometry(WzRect const &r); virtual void setGeometryFromScreenRect(WzRect const &r); - void attach(const std::shared_ptr &widget); + enum class ChildZPos { + Front, + Back + }; + + void attach(const std::shared_ptr &widget, ChildZPos zPos = ChildZPos::Front); /** * @deprecated use `void WIDGET::attach(const std::shared_ptr &widget)` instead **/ diff --git a/lib/widget/widget.cpp b/lib/widget/widget.cpp index fbbcc96591e..07b003bc969 100644 --- a/lib/widget/widget.cpp +++ b/lib/widget/widget.cpp @@ -566,12 +566,22 @@ void WIDGET::setCustomHitTest(const WIDGET_HITTEST_FUNC& newCustomHitTestFunc) customHitTest = newCustomHitTestFunc; } -void WIDGET::attach(const std::shared_ptr &widget) +void WIDGET::attach(const std::shared_ptr &widget, ChildZPos zPos /*= ChildZPos::Front*/) { ASSERT_OR_RETURN(, widget != nullptr && widget->parentWidget.expired(), "Bad attach."); widget->parentWidget = shared_from_this(); widget->setScreenPointer(screenPointer.lock()); - childWidgets.push_back(widget); + switch (zPos) + { + case ChildZPos::Front: + // insert at the end of the list of children, and thus the top of the z-order (childWidgets order is bottom -> top) + childWidgets.push_back(widget); + break; + case ChildZPos::Back: + // insert at the very beginning of the list of children + childWidgets.insert(childWidgets.begin(), widget); + break; + } } void WIDGET::detach(const std::shared_ptr &widget) @@ -1180,8 +1190,10 @@ bool WIDGET::processClickRecursive(W_CONTEXT *psContext, WIDGET_KEY key, bool wa if (!skipProcessingChildren) { // Process subwidgets. - for (auto const &psCurr: childWidgets) + // (Enumerate the child widgets in decreasing z-order (i.e. "top-down")) + for (auto i = childWidgets.size(); i--;) { + auto const &psCurr = childWidgets[i]; if (!psCurr->visible() || !psCurr->hitTest(shiftedContext.mx, shiftedContext.my)) { continue; // Skip any hidden widgets, or widgets the click missed. @@ -1422,6 +1434,9 @@ void WIDGET::displayRecursive(WidgetGraphicsContext const &context) } // Display the widgets on this widget. + // NOTE: Draw them in the opposite order that clicks are processed, so behavior matches the visual. + // i.e. Since processClickRecursive handles processing children in decreasing z-order (i.e. "top-down") + // we want to draw things in list order (bottom-up) so the "top-most" is drawn last for (auto const &child: childWidgets) { if (child->visible()) From b8de7b7bd3b188b0bf4924ae14d4bc4952765fa5 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Tue, 20 Feb 2024 22:18:08 -0500 Subject: [PATCH 4/8] Fix dropdown backing overlay z-order --- lib/widget/dropdown.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widget/dropdown.cpp b/lib/widget/dropdown.cpp index b0c7d65082c..1ed73d21bf8 100644 --- a/lib/widget/dropdown.cpp +++ b/lib/widget/dropdown.cpp @@ -169,7 +169,7 @@ void DropdownWidget::open() widgRegisterOverlayScreenOnTopOfScreen(dropdownWidget->overlayScreen, dropdownWidget->screenPointer.lock()); dropdownWidget->itemsList->setGeometry(dropdownWidget->screenPosX(), dropdownWidget->calculateDropdownListScreenPosY(), dropdownWidget->width(), dropdownWidget->itemsList->height()); dropdownWidget->overlayScreen->psForm->attach(dropdownWidget->itemsList); - dropdownWidget->overlayScreen->psForm->attach(std::make_shared([pWeakThis]() { if (auto dropdownWidget = pWeakThis.lock()) { dropdownWidget->close(); } })); + dropdownWidget->overlayScreen->psForm->attach(std::make_shared([pWeakThis]() { if (auto dropdownWidget = pWeakThis.lock()) { dropdownWidget->close(); } }), WIDGET::ChildZPos::Back); } }); } From 5a18393c4de3fb4de1d219c2614d4380dfb5a0f4 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Tue, 20 Feb 2024 22:18:23 -0500 Subject: [PATCH 5/8] Fix addLoadSave banner label z-order --- src/loadsave.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/loadsave.cpp b/src/loadsave.cpp index 2f72a548179..86d2f2a7190 100644 --- a/src/loadsave.cpp +++ b/src/loadsave.cpp @@ -282,6 +282,19 @@ bool addLoadSave(LOADSAVE_MODE savemode, const char *title) sFormInit.UserData = bLoad; widgAddForm(psRequestScreen, &sFormInit); + // Add Banner Label + W_LABINIT sLabInit; + sLabInit.formID = LOADSAVE_BANNER; + sLabInit.FontID = font_large; + sLabInit.id = LOADSAVE_LABEL; + sLabInit.style = WLAB_ALIGNCENTRE; + sLabInit.x = 0; + sLabInit.y = 0; + sLabInit.width = LOADSAVE_W - (2 * LOADSAVE_HGAP); //LOADSAVE_W; + sLabInit.height = LOADSAVE_BANNER_DEPTH; //This looks right -Q + sLabInit.pText = WzString::fromUtf8(title); + widgAddLabel(psRequestScreen, &sLabInit); + // add cancel. W_BUTINIT sButInit; sButInit.formID = LOADSAVE_BANNER; @@ -297,19 +310,6 @@ bool addLoadSave(LOADSAVE_MODE savemode, const char *title) sButInit.pDisplay = intDisplayImageHilight; widgAddButton(psRequestScreen, &sButInit); - // Add Banner Label - W_LABINIT sLabInit; - sLabInit.formID = LOADSAVE_BANNER; - sLabInit.FontID = font_large; - sLabInit.id = LOADSAVE_LABEL; - sLabInit.style = WLAB_ALIGNCENTRE; - sLabInit.x = 0; - sLabInit.y = 0; - sLabInit.width = LOADSAVE_W - (2 * LOADSAVE_HGAP); //LOADSAVE_W; - sLabInit.height = LOADSAVE_BANNER_DEPTH; //This looks right -Q - sLabInit.pText = WzString::fromUtf8(title); - widgAddLabel(psRequestScreen, &sLabInit); - // add slots sButInit = W_BUTINIT(); sButInit.formID = LOADSAVE_FORM; From 962caa8a465187109845bdfbf442f6ad326801a4 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Fri, 23 Feb 2024 13:19:32 -0500 Subject: [PATCH 6/8] Partial refactoring of radar into WIDGET --- src/display.cpp | 94 ++--------------------- src/display.h | 2 - src/hci.cpp | 22 ++++-- src/hci.h | 2 + src/init.cpp | 2 + src/loop.cpp | 6 -- src/radar.cpp | 193 ++++++++++++++++++++++++++++++++++++++++++++++++ src/radar.h | 7 +- 8 files changed, 225 insertions(+), 103 deletions(-) diff --git a/src/display.cpp b/src/display.cpp index 94392b09f20..3fd3a3451fe 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -131,13 +131,11 @@ static float scrollSpeedLeftRight; //use two directions and add them because its static float scrollStepLeftRight; static float scrollSpeedUpDown; static float scrollStepUpDown; -static bool mouseOverRadar = false; static bool mouseOverConsole = false; static bool ignoreOrder = false; static bool ignoreRMBC = true; static DROID *psSelectedVtol; static DROID *psDominantSelected; -static bool bRadarDragging = false; static bool cameraAccel = true; bool rotActive = false; @@ -273,11 +271,6 @@ static void shakeUpdate() } } -bool isMouseOverRadar() -{ - return mouseOverRadar; -} - bool getCameraAccel() { return cameraAccel; @@ -335,73 +328,6 @@ void setDrawShadows(bool val) pie_setShadows(val); } -void ProcessRadarInput() -{ - int PosX, PosY; - int x = mouseX(); - int y = mouseY(); - UDWORD temp1, temp2; - - /* Only allow jump-to-area-of-map if radar is on-screen */ - mouseOverRadar = false; - if (radarVisible()) - { - if (CoordInRadar(x, y)) - { - mouseOverRadar = true; - - if (mousePressed(MOUSE_ORDER)) - { - x = mousePressPos_DEPRECATED(MOUSE_ORDER).x; - y = mousePressPos_DEPRECATED(MOUSE_ORDER).y; - - /* If we're tracking a droid, then cancel that */ - CalcRadarPosition(x, y, &PosX, &PosY); - if (mouseOverRadar && selectedPlayer < MAX_PLAYERS) - { - // MARKER - // Send all droids to that location - orderSelectedLoc(selectedPlayer, (PosX * TILE_UNITS) + TILE_UNITS / 2, - (PosY * TILE_UNITS) + TILE_UNITS / 2, ctrlShiftDown()); // ctrlShiftDown() = ctrl clicked a destination, add an order - - } - CheckScrollLimits(); - audio_PlayTrack(ID_SOUND_MESSAGEEND); - } - - - if (mouseDrag(MOUSE_SELECT, &temp1, &temp2) && !rotActive) - { - CalcRadarPosition(x, y, &PosX, &PosY); - setViewPos(PosX, PosY, true); - bRadarDragging = true; - if (ctrlShiftDown()) - { - playerPos.r.y = 0; - } - } - else if (mousePressed(MOUSE_SELECT)) - { - x = mousePressPos_DEPRECATED(MOUSE_SELECT).x; - y = mousePressPos_DEPRECATED(MOUSE_SELECT).y; - - CalcRadarPosition(x, y, &PosX, &PosY); - - if (war_GetRadarJump()) - { - /* Go instantly */ - setViewPos(PosX, PosY, true); - } - else - { - /* Pan to it */ - requestRadarTrack(PosX * TILE_UNITS, PosY * TILE_UNITS); - } - } - } - } -} - // reset the input state void resetInput() { @@ -493,7 +419,7 @@ void processInput() static bool OverRadarAndNotDragging() { - return mouseOverRadar && dragBox3D.status != DRAG_DRAGGING && wallDrag.status != DRAG_DRAGGING; + return isMouseOverRadar() && dragBox3D.status != DRAG_DRAGGING && wallDrag.status != DRAG_DRAGGING; } static void CheckFinishedDrag(SELECTION_TYPE selection) @@ -629,7 +555,7 @@ static void HandleDrag() { UDWORD dragX = 0, dragY = 0; - if (mouseDrag(MOUSE_LMB, &dragX, &dragY) && !mouseOverRadar && !mouseDown(MOUSE_RMB)) + if (mouseDrag(MOUSE_LMB, &dragX, &dragY) && !isMouseOverRadar() && !mouseDown(MOUSE_RMB)) { dragBox3D.x1 = dragX; dragBox3D.x2 = mouseX(); @@ -707,7 +633,6 @@ void processMouseClickInput() dragBox3D.status = DRAG_INACTIVE; // Pretty sure we wan't set walldrag status here aswell. wallDrag.status = DRAG_INACTIVE; - bRadarDragging = false; if (bRightClickOrders) { dealWithLMB(); @@ -723,30 +648,24 @@ void processMouseClickInput() } } - if (!mouseDrag(MOUSE_SELECT, (UDWORD *)&rotX, (UDWORD *)&rotY) && bRadarDragging) - { - bRadarDragging = false; - } - /* Right mouse click kills a building placement */ if (!rotActive && mouseReleased(MOUSE_RMB) && (buildState == BUILD3D_POS || buildState == BUILD3D_VALID)) { /* Stop the placement */ kill3DBuilding(); - bRadarDragging = false; } if (mouseReleased(MOUSE_RMB)) { cancelDeliveryRepos(); } - if (mouseDrag(MOUSE_ROTATE, (UDWORD *)&rotX, (UDWORD *)&rotY) && !rotActive && !bRadarDragging && !getRadarTrackingStatus()) + if (mouseDrag(MOUSE_ROTATE, (UDWORD *)&rotX, (UDWORD *)&rotY) && !rotActive && !isRadarDragging() && !getRadarTrackingStatus()) { rotationVerticalTracker->startTracking((UWORD)playerPos.r.x); rotationHorizontalTracker->startTracking((UWORD)playerPos.r.y); // negative values caused problems with float conversion rotActive = true; } - if (mouseDrag(MOUSE_PAN, (UDWORD *)&panMouseX, (UDWORD *)&panMouseY) && !rotActive && !panActive && !bRadarDragging && !getRadarTrackingStatus()) + if (mouseDrag(MOUSE_PAN, (UDWORD *)&panMouseX, (UDWORD *)&panMouseY) && !rotActive && !panActive && !isRadarDragging() && !getRadarTrackingStatus()) { panXTracker->startTracking(playerPos.p.x); panZTracker->startTracking(playerPos.p.z); @@ -1253,7 +1172,6 @@ void displayWorld() pos.y = playerPos.r.y; pos.z = playerPos.r.z; camInformOfRotation(&pos); - bRadarDragging = false; } draw3DScene(); @@ -2014,7 +1932,7 @@ void dealWithLMB() STRUCTURE *psStructure; /* Don't process if in game options are on screen */ - if (mouseOverRadar || + if (isMouseOverRadar() || InGameOpUp == true || widgGetFromID(psWScreen, INTINGAMEOP)) { return; @@ -2182,7 +2100,7 @@ static void dealWithRMB() DROID *psDroid; STRUCTURE *psStructure; - if (mouseOverRadar || InGameOpUp == true || widgGetFromID(psWScreen, INTINGAMEOP)) + if (isMouseOverRadar() || InGameOpUp == true || widgGetFromID(psWScreen, INTINGAMEOP)) { return; } diff --git a/src/display.h b/src/display.h index 717796a63dc..ada98591388 100644 --- a/src/display.h +++ b/src/display.h @@ -54,8 +54,6 @@ void clearSelection(); // deal with selecting a droid void dealWithDroidSelect(DROID *psDroid, bool bDragBox); -bool isMouseOverRadar(); - void setInvertMouseStatus(bool val); bool getInvertMouseStatus(); diff --git a/src/hci.cpp b/src/hci.cpp index a01dd98085f..a02eb91dfb9 100644 --- a/src/hci.cpp +++ b/src/hci.cpp @@ -1020,6 +1020,13 @@ void intRefreshGroupsUI() IntGroupsRefreshPending = true; } +bool intAddRadarWidget() +{ + auto radarWidget = getRadarWidget(); + ASSERT_OR_RETURN(false, radarWidget != nullptr, "Failed to get radar widget?"); + psWScreen->psForm->attach(radarWidget, WIDGET::ChildZPos::Back); + return true; +} // see if a delivery point is selected static FLAG_POSITION *intFindSelectedDelivPoint() @@ -1942,18 +1949,21 @@ void intDisplayWidgets() } } + bool desiredRadarVisibility = false; if (!gameUpdatePaused()) { - if (radarVisible()) - { - gfx_api::context::get().debugStringMarker("Draw 3D scene - radar"); - drawRadar(); - } - + desiredRadarVisibility = radarVisible(); + /* Ensure that any text messages are displayed at bottom of screen */ displayConsoleMessages(); } + auto radarWidget = getRadarWidget(); + if (radarWidget && (desiredRadarVisibility != radarWidget->visible())) + { + (desiredRadarVisibility) ? radarWidget->show() : radarWidget->hide(); + } + widgDisplayScreen(psWScreen); if (bLoadSaveUp) diff --git a/src/hci.h b/src/hci.h index c2ce0c9f49c..32ddb202717 100644 --- a/src/hci.h +++ b/src/hci.h @@ -268,6 +268,8 @@ extern iIMDShape *pNewDesignIMD; /* Initialise the in game interface */ bool intInitialise(); +bool intAddRadarWidget(); + // Check of coordinate is in the build menu bool CoordInBuild(int x, int y); diff --git a/src/init.cpp b/src/init.cpp index 92e188e62d0..58eb9016073 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1879,6 +1879,8 @@ bool stageThreeInitialise() challengeActive = true; } + // add radar to interface screen, and resize + intAddRadarWidget(); resizeRadar(); setAllPauseStates(false); diff --git a/src/loop.cpp b/src/loop.cpp index eb34098cc89..da0ac6e0de9 100644 --- a/src/loop.cpp +++ b/src/loop.cpp @@ -308,12 +308,6 @@ static GAMECODE renderLoop() { if (!gameUpdatePaused()) { - if (dragBox3D.status != DRAG_DRAGGING - && wallDrag.status != DRAG_DRAGGING - && intRetVal != INT_INTERCEPT) - { - ProcessRadarInput(); - } processInput(); //no key clicks or in Intelligence Screen diff --git a/src/radar.cpp b/src/radar.cpp index 373c1d6a335..e7f83e20fd6 100644 --- a/src/radar.cpp +++ b/src/radar.cpp @@ -30,6 +30,8 @@ #include "lib/ivis_opengl/piestate.h" #include "lib/ivis_opengl/piefunc.h" #include "lib/ivis_opengl/bitimage.h" +#include "lib/sound/audio.h" +#include "lib/sound/audio_id.h" #include "lib/gamelib/gtime.h" #include "advvis.h" #include "objects.h" @@ -50,6 +52,7 @@ #include "intdisplay.h" #include "texture.h" #include "warzoneconfig.h" +#include "order.h" #ifndef GLM_ENABLE_EXPERIMENTAL #define GLM_ENABLE_EXPERIMENTAL #endif @@ -71,6 +74,31 @@ static iV_Image radarBitmap; static UDWORD *radarOverlayBuffer = nullptr; static Vector3i playerpos = {0, 0, 0}; +class RadarWidget : public WIDGET { +public: + RadarWidget(); + +protected: + void run(W_CONTEXT *) override; + void display(int xOffset, int yOffset) override; + bool hitTest(int x, int y) override; + void clicked(W_CONTEXT *, WIDGET_KEY = WKEY_PRIMARY) override; + void released(W_CONTEXT *, WIDGET_KEY = WKEY_PRIMARY) override; + void highlight(W_CONTEXT *) override; + void highlightLost() override; + +public: + bool hasHighlight() const { return m_hasHighlight; } + bool isRadarDragging() const { return m_handlingDrag; } + +private: + std::array, 3> m_clickPosition; + bool m_hasHighlight = false; + bool m_handlingDrag = false; +}; + +static std::shared_ptr pRadarWidget; + PIELIGHT clanColours[] = { // see frontend2.png for team color order. @@ -191,6 +219,14 @@ static void radarSize(int ZoomLevel) radarCenterY = pie_GetVideoBufferHeight() - BASE_GAP * 4 - static_cast(radarHeight) / 2; } debug(LOG_WZ, "radar=(%u,%u) tex=(%zu,%zu) size=(%zu,%zu)", radarCenterX, radarCenterY, radarTexWidth, radarTexHeight, radarWidth, radarHeight); + + if (pRadarWidget) + { + auto radarDiag = static_cast(ceil(sqrt(double((radarWidth + 1) * (radarWidth + 1)) + double((radarHeight + 1) * (radarHeight + 1))))); + int radarX0 = radarCenterX - (radarDiag / 2); + int radarY0 = radarCenterY - (radarDiag / 2); + pRadarWidget->setGeometry(radarX0, radarY0, radarDiag, radarDiag); + } } void radarInitVars() @@ -210,6 +246,7 @@ bool InitRadar() colRadarAlly = WZCOL_YELLOW; colRadarEnemy = WZCOL_RED; colRadarMe = WZCOL_WHITE; + pRadarWidget = std::make_shared(); return true; } @@ -248,6 +285,8 @@ bool ShutdownRadar() free(radarOverlayBuffer); radarOverlayBuffer = nullptr; frameSkip = 0; + psWScreen->psForm->detach(pRadarWidget); + pRadarWidget.reset(); return true; } @@ -777,3 +816,157 @@ void radarColour(UDWORD tileNumber, uint8_t r, uint8_t g, uint8_t b) tileColours[tileNumber].byte.b = b; tileColours[tileNumber].byte.a = 255; } + + +// MARK: - RadarWidget + +std::shared_ptr getRadarWidget() +{ + return pRadarWidget; +} + +RadarWidget::RadarWidget() +: WIDGET() +{ } + +#define WKEY_ORDER (getRightClickOrders()?WKEY_SECONDARY:WKEY_PRIMARY) +#define WKEY_SELECT (getRightClickOrders()?WKEY_PRIMARY:WKEY_SECONDARY) +#define MOUSE_SELECT (getRightClickOrders()?MOUSE_LMB:MOUSE_RMB) + +void RadarWidget::run(W_CONTEXT *) +{ + int PosX, PosY; + UDWORD temp1, temp2; + + if (m_hasHighlight) + { + // process drag events + if (mouseDrag(MOUSE_SELECT, &temp1, &temp2)) + { + if (!getRotActive()) + { + int x = mouseX(); + int y = mouseY(); + CalcRadarPosition(x, y, &PosX, &PosY); + setViewPos(PosX, PosY, true); + m_handlingDrag = true; + if (ctrlShiftDown()) + { + playerPos.r.y = 0; + } + } + } + else + { + m_handlingDrag = false; + } + } + else + { + if (m_handlingDrag && !mouseDrag(MOUSE_SELECT, &temp1, &temp2)) + { + m_handlingDrag = false; + } + } +} + +void RadarWidget::display(int xOffset, int yOffset) +{ + if (!radarVisible()) { return; } + + gfx_api::context::get().debugStringMarker("Draw 3D scene - radar"); + drawRadar(); +} + +bool RadarWidget::hitTest(int x, int y) +{ + // NOTE: CoordInRadar expects x, y in *screen* coordinates - this is _currently_ the case as a byproduct of how the radar widget is added to the UI screen + return WIDGET::hitTest(x, y) && CoordInRadar(x, y); +} + +void RadarWidget::clicked(W_CONTEXT *context, WIDGET_KEY key) +{ + int x = context->xOffset + context->mx; + int y = context->yOffset + context->my; + + m_clickPosition[key] = Vector2i(x, y); +} + +void RadarWidget::highlight(W_CONTEXT *) +{ + m_hasHighlight = true; +} + +void RadarWidget::highlightLost() +{ + m_hasHighlight = false; + for (auto& keyClickPos : m_clickPosition) + { + keyClickPos.reset(); + } +} + +void RadarWidget::released(W_CONTEXT *context, WIDGET_KEY key) +{ + int PosX, PosY; + int x = context->xOffset + context->mx; + int y = context->yOffset + context->my; + + if (key == WKEY_ORDER) + { + if (m_clickPosition[WKEY_ORDER].has_value()) + { + x = m_clickPosition[WKEY_ORDER].value().x; + y = m_clickPosition[WKEY_ORDER].value().y; + + /* If we're tracking a droid, then cancel that */ + CalcRadarPosition(x, y, &PosX, &PosY); + if (selectedPlayer < MAX_PLAYERS) + { + // MARKER + // Send all droids to that location + orderSelectedLoc(selectedPlayer, (PosX * TILE_UNITS) + TILE_UNITS / 2, + (PosY * TILE_UNITS) + TILE_UNITS / 2, ctrlShiftDown()); // ctrlShiftDown() = ctrl clicked a destination, add an order + + } + CheckScrollLimits(); + audio_PlayTrack(ID_SOUND_MESSAGEEND); + } + } + + if (key == WKEY_SELECT && !m_handlingDrag) + { + if (m_clickPosition[WKEY_SELECT].has_value()) + { + x = m_clickPosition[WKEY_SELECT].value().x; + y = m_clickPosition[WKEY_SELECT].value().y; + + CalcRadarPosition(x, y, &PosX, &PosY); + + if (war_GetRadarJump()) + { + /* Go instantly */ + setViewPos(PosX, PosY, true); + } + else + { + /* Pan to it */ + requestRadarTrack(PosX * TILE_UNITS, PosY * TILE_UNITS); + } + } + } + + m_clickPosition[key].reset(); +} + +bool isMouseOverRadar() +{ + if (!pRadarWidget) { return false; } + return pRadarWidget->hasHighlight(); +} + +bool isRadarDragging() +{ + if (!pRadarWidget) { return false; } + return pRadarWidget->isRadarDragging(); +} diff --git a/src/radar.h b/src/radar.h index 49d9e21c23b..dd8de93dc4a 100644 --- a/src/radar.h +++ b/src/radar.h @@ -28,6 +28,8 @@ #ifndef __INCLUDED_SRC_RADAR_H__ #define __INCLUDED_SRC_RADAR_H__ +#include "lib/widget/widget.h" + void radarColour(UDWORD tileNumber, uint8_t r, uint8_t g, uint8_t b); ///< Set radar colour for given terrain type. #define MAX_RADARZOOM (64) @@ -42,7 +44,6 @@ void drawRadar(); ///< Draw the minimap on the screen. void CalcRadarPosition(int mX, int mY, int *PosX, int *PosY); ///< Given a position within the radar, returns a world coordinate. void SetRadarZoom(uint8_t ZoomLevel); ///< Set current zoom level. 1.0 is 1:1 resolution. uint8_t GetRadarZoom(); ///< Get current zoom level. -bool CoordInRadar(int x, int y); ///< Is screen coordinate inside minimap? /** Different mini-map draw modes. */ enum RADAR_DRAW_MODE @@ -62,6 +63,10 @@ extern bool radarRotationArrow; void radarInitVars(); ///< Recalculate minimap variables. For initialization code only. +std::shared_ptr getRadarWidget(); +bool isMouseOverRadar(); +bool isRadarDragging(); + extern PIELIGHT clanColours[]; /** @} */ From 4ab1cbe84221cb19949736d16fbe4f92826c76e0 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Sun, 25 Feb 2024 15:05:23 -0500 Subject: [PATCH 7/8] tip.cpp: Default to placing tooltips *above* the widget --- lib/widget/tip.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/widget/tip.cpp b/lib/widget/tip.cpp index 8d344045fe8..564300b8edf 100644 --- a/lib/widget/tip.cpp +++ b/lib/widget/tip.cpp @@ -161,11 +161,11 @@ static void refreshTip(std::shared_ptr mouseOverWidget) height += displayCache.wzTip.back().belowBase(); } auto x = clip(mouseOverWidget->screenPosX() + mouseOverWidget->width() / 2, 0, screenWidth - width - 1); - auto y = std::max(mouseOverWidget->screenPosY() + mouseOverWidget->height() + TIP_VGAP, 0); - if (y + height >= (int)screenHeight) + auto y = std::min(mouseOverWidget->screenPosY() - height - TIP_VGAP, (int)screenHeight); + if (y < 0) { - /* Position the tip above the button */ - y = mouseOverWidget->screenPosY() - height - TIP_VGAP; + /* Position the tip below the button */ + y = std::max(mouseOverWidget->screenPosY() + mouseOverWidget->height() + TIP_VGAP, 0); } tipRect = WzRect(x - 1, y - 1, width + 2, height + 2); From 9dbb4e8348174eca8193063ca5fb3898ab27ee4f Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Mon, 26 Feb 2024 13:27:32 -0500 Subject: [PATCH 8/8] Make WIDGET::hitTest() const --- lib/widget/form.cpp | 2 +- lib/widget/form.h | 2 +- lib/widget/table.cpp | 2 +- lib/widget/table.h | 2 +- lib/widget/widgbase.h | 4 ++-- lib/widget/widget.cpp | 2 +- src/hci.cpp | 2 +- src/multiint.cpp | 2 +- src/musicmanager.cpp | 24 ++++++++---------------- src/notifications.cpp | 6 ++---- src/radar.cpp | 4 ++-- src/spectatorwidgets.cpp | 2 +- 12 files changed, 22 insertions(+), 32 deletions(-) diff --git a/lib/widget/form.cpp b/lib/widget/form.cpp index ef25c9490b3..ae133c659ae 100644 --- a/lib/widget/form.cpp +++ b/lib/widget/form.cpp @@ -300,7 +300,7 @@ void W_FORM::screenSizeDidChange(int oldWidth, int oldHeight, int newWidth, int WIDGET::screenSizeDidChange(oldWidth, oldHeight, newWidth, newHeight); } -bool W_FORM::hitTest(int x, int y) +bool W_FORM::hitTest(int x, int y) const { if (!minimizable || formState != FormState::MINIMIZED) { diff --git a/lib/widget/form.h b/lib/widget/form.h index ffada9a603b..7eb3448cd17 100644 --- a/lib/widget/form.h +++ b/lib/widget/form.h @@ -51,7 +51,7 @@ class W_FORM : public WIDGET void display(int xOffset, int yOffset) override; void screenSizeDidChange(int oldWidth, int oldHeight, int newWidth, int newHeight) override; - bool hitTest(int x, int y) override; + bool hitTest(int x, int y) const override; bool processClickRecursive(W_CONTEXT *psContext, WIDGET_KEY key, bool wasPressed) override; void displayRecursive(WidgetGraphicsContext const &context) override; using WIDGET::displayRecursive; diff --git a/lib/widget/table.cpp b/lib/widget/table.cpp index 74c19b15e66..6c4a6db9904 100644 --- a/lib/widget/table.cpp +++ b/lib/widget/table.cpp @@ -156,7 +156,7 @@ void TableRow::displayRecursive(WidgetGraphicsContext const& context) pie_UniTransBoxFill(x0, y0, x0 + width(), y0 + height(), disabledColor); } -bool TableRow::hitTest(int x, int y) +bool TableRow::hitTest(int x, int y) const { if (disabledRow) { return false; } return W_BUTTON::hitTest(x, y); diff --git a/lib/widget/table.h b/lib/widget/table.h index f277f9f4ad1..4c5456c7bd7 100644 --- a/lib/widget/table.h +++ b/lib/widget/table.h @@ -60,7 +60,7 @@ class TableRow: public W_BUTTON protected: virtual void display(int, int) override; virtual void displayRecursive(WidgetGraphicsContext const& context) override; - virtual bool hitTest(int x, int y) override; + virtual bool hitTest(int x, int y) const override; public: virtual bool processClickRecursive(W_CONTEXT *psContext, WIDGET_KEY key, bool wasPressed) override; protected: diff --git a/lib/widget/widgbase.h b/lib/widget/widgbase.h index 7d8914dd41d..eb18c82015f 100644 --- a/lib/widget/widgbase.h +++ b/lib/widget/widgbase.h @@ -70,7 +70,7 @@ typedef std::function WIDGET_CALCLAYOUT_FUNC; typedef std::function WIDGET_ONDELETE_FUNC; /* The optional hit-testing function, used for custom hit-testing within the outer bounding rectangle */ -typedef std::function WIDGET_HITTEST_FUNC; +typedef std::function WIDGET_HITTEST_FUNC; /* The different base types of widget */ @@ -218,7 +218,7 @@ class WIDGET: public std::enable_shared_from_this virtual void display(int, int) {} virtual void geometryChanged() {} - virtual bool hitTest(int x, int y); + virtual bool hitTest(int x, int y) const; public: virtual unsigned getState(); diff --git a/lib/widget/widget.cpp b/lib/widget/widget.cpp index 07b003bc969..b43ddb1d9c2 100644 --- a/lib/widget/widget.cpp +++ b/lib/widget/widget.cpp @@ -1157,7 +1157,7 @@ void WIDGET::runRecursive(W_CONTEXT *psContext) } } -bool WIDGET::hitTest(int x, int y) +bool WIDGET::hitTest(int x, int y) const { // default hit-testing bounding rect (based on the widget's x, y, width, height) bool hitTestResult = dim.contains(x, y); diff --git a/src/hci.cpp b/src/hci.cpp index a02eb91dfb9..79eff9572d8 100644 --- a/src/hci.cpp +++ b/src/hci.cpp @@ -340,7 +340,7 @@ void setReticuleButtonDimensions(W_BUTTON &button, const WzString &filename) button.setGeometry(button.x(), button.y(), image->Width / 2, image->Height / 2); // add a custom hit-testing function that uses a tighter bounding ellipse - button.setCustomHitTest([](WIDGET *psWidget, int x, int y) -> bool { + button.setCustomHitTest([](const WIDGET *psWidget, int x, int y) -> bool { // determine center of ellipse contained within the bounding rect float centerX = ((psWidget->x()) + (psWidget->x() + psWidget->width())) / 2.f; diff --git a/src/multiint.cpp b/src/multiint.cpp index 6bbea0894f7..3f6bb9d4290 100644 --- a/src/multiint.cpp +++ b/src/multiint.cpp @@ -4565,7 +4565,7 @@ void WzMultiplayerOptionsTitleUI::openPlayerSlotSwapChooser(uint32_t playerIndex // Now create a dummy row for the row being swapped, and display beneath auto playerRow = WzPlayerRow::make(playerIndex, titleUI); - playerRow->setCustomHitTest([](WIDGET *psWidget, int x, int y) -> bool { return false; }); // ensure clicks on this display-only row have no effect + playerRow->setTransparentToMouse(true); // ensure clicks on this display-only row have no effect swapContextForm->attach(playerRow); playerRow->setCalcLayout([swapContextFormPadding, textHeight](WIDGET *psWidget) { psWidget->setGeometry(PLAYERBOX_X0, swapContextFormPadding + textHeight + 4, MULTIOP_PLAYERWIDTH, MULTIOP_PLAYERHEIGHT); diff --git a/src/musicmanager.cpp b/src/musicmanager.cpp index 72aa13ad87e..2e2609939b8 100644 --- a/src/musicmanager.cpp +++ b/src/musicmanager.cpp @@ -489,8 +489,7 @@ static void UpdateTrackDetailsBox(TrackDetailsForm *pTrackDetailsBox) psNowPlaying->setFont(font_regular, WZCOL_TEXT_MEDIUM); psNowPlaying->setString((selectedTrack) ? (WzString::fromUtf8(_("NOW PLAYING")) + ":") : ""); psNowPlaying->setTextAlignment(WLAB_ALIGNTOPLEFT); - // set a custom hit-testing function that ignores all mouse input / clicks - psNowPlaying->setCustomHitTest([](WIDGET *psWidget, int x, int y) -> bool { return false; }); + psNowPlaying->setTransparentToMouse(true); // Add Selected Track name if (!psSelectedTrackName) @@ -501,8 +500,7 @@ static void UpdateTrackDetailsBox(TrackDetailsForm *pTrackDetailsBox) psSelectedTrackName->setFont(font_regular_bold, WZCOL_TEXT_BRIGHT); psSelectedTrackName->setString((selectedTrack) ? WzString::fromUtf8(selectedTrack->title) : ""); psSelectedTrackName->setTextAlignment(WLAB_ALIGNTOPLEFT); - // set a custom hit-testing function that ignores all mouse input / clicks - psSelectedTrackName->setCustomHitTest([](WIDGET *psWidget, int x, int y) -> bool { return false; }); + psSelectedTrackName->setTransparentToMouse(true); // Add Selected Track author details if (!psSelectedTrackAuthorName) @@ -513,8 +511,7 @@ static void UpdateTrackDetailsBox(TrackDetailsForm *pTrackDetailsBox) psSelectedTrackAuthorName->setFont(font_regular, WZCOL_TEXT_BRIGHT); psSelectedTrackAuthorName->setString((selectedTrack) ? WzString::fromUtf8(selectedTrack->author) : ""); psSelectedTrackAuthorName->setTextAlignment(WLAB_ALIGNTOPLEFT); - // set a custom hit-testing function that ignores all mouse input / clicks - psSelectedTrackAuthorName->setCustomHitTest([](WIDGET *psWidget, int x, int y) -> bool { return false; }); + psSelectedTrackAuthorName->setTransparentToMouse(true); // album info xPosStart int albumInfoXPosStart = psNowPlaying->x() + psNowPlaying->width() + 10 + WZ_TRACKDETAILS_IMAGE_SIZE + 10; @@ -529,8 +526,7 @@ static void UpdateTrackDetailsBox(TrackDetailsForm *pTrackDetailsBox) psSelectedTrackAlbumName->setFont(font_small, WZCOL_TEXT_BRIGHT); psSelectedTrackAlbumName->setString((pAlbum) ? (WzString::fromUtf8(_("Album")) + ": " + WzString::fromUtf8(pAlbum->title)) : ""); psSelectedTrackAlbumName->setTextAlignment(WLAB_ALIGNTOPLEFT); - // set a custom hit-testing function that ignores all mouse input / clicks - psSelectedTrackAlbumName->setCustomHitTest([](WIDGET *psWidget, int x, int y) -> bool { return false; }); + psSelectedTrackAlbumName->setTransparentToMouse(true); if (!psSelectedTrackAlbumDate) { @@ -540,8 +536,7 @@ static void UpdateTrackDetailsBox(TrackDetailsForm *pTrackDetailsBox) psSelectedTrackAlbumDate->setFont(font_small, WZCOL_TEXT_BRIGHT); psSelectedTrackAlbumDate->setString((pAlbum) ? (WzString::fromUtf8(_("Date")) + ": " + WzString::fromUtf8(pAlbum->date)) : ""); psSelectedTrackAlbumDate->setTextAlignment(WLAB_ALIGNTOPLEFT); - // set a custom hit-testing function that ignores all mouse input / clicks - psSelectedTrackAlbumDate->setCustomHitTest([](WIDGET *psWidget, int x, int y) -> bool { return false; }); + psSelectedTrackAlbumDate->setTransparentToMouse(true); if (!psSelectedTrackAlbumDescription) { @@ -552,8 +547,7 @@ static void UpdateTrackDetailsBox(TrackDetailsForm *pTrackDetailsBox) int heightOfTitleLabel = psSelectedTrackAlbumDescription->setFormattedString((pAlbum) ? WzString::fromUtf8(pAlbum->description) : "", maxWidthOfAlbumLabel, font_small); psSelectedTrackAlbumDescription->setGeometry(albumInfoXPosStart, 12 + 13 + 15, maxWidthOfAlbumLabel, heightOfTitleLabel); psSelectedTrackAlbumDescription->setTextAlignment(WLAB_ALIGNTOPLEFT); - // set a custom hit-testing function that ignores all mouse input / clicks - psSelectedTrackAlbumDescription->setCustomHitTest([](WIDGET *psWidget, int x, int y) -> bool { return false; }); + psSelectedTrackAlbumDescription->setTransparentToMouse(true); pTrackDetailsBox->loadImage((pAlbum) ? pAlbum->album_cover_filename : ""); } @@ -674,8 +668,7 @@ static void addTrackList(WIDGET *parent, bool ingame) title_header->setFontColour(WZCOL_TEXT_MEDIUM); title_header->setString(_("Title")); title_header->setTextAlignment(WLAB_ALIGNTOPLEFT); - // set a custom hit-testing function that ignores all mouse input / clicks - title_header->setCustomHitTest([](WIDGET *psWidget, int x, int y) -> bool { return false; }); + title_header->setTransparentToMouse(true); auto album_header = std::make_shared(); pHeader->attach(album_header); @@ -683,8 +676,7 @@ static void addTrackList(WIDGET *parent, bool ingame) album_header->setFontColour(WZCOL_TEXT_MEDIUM); album_header->setString(_("Album")); album_header->setTextAlignment(WLAB_ALIGNTOPLEFT); - // set a custom hit-testing function that ignores all mouse input / clicks - album_header->setCustomHitTest([](WIDGET *psWidget, int x, int y) -> bool { return false; }); + album_header->setTransparentToMouse(true); for (int musicModeIdx = 0; musicModeIdx < NUM_MUSICGAMEMODES; musicModeIdx++) { diff --git a/src/notifications.cpp b/src/notifications.cpp index 6be4650cc56..214d830467b 100644 --- a/src/notifications.cpp +++ b/src/notifications.cpp @@ -671,8 +671,7 @@ std::shared_ptr W_NOTIFICATION::make(WZ_Queued_Notification* req } label_title->setGeometry(titleStartXPos, titleStartYPos, maxTitleWidth, heightOfTitleLabel); label_title->setTextAlignment(WLAB_ALIGNTOPLEFT); - // set a custom hit-testing function that ignores all mouse input / clicks - label_title->setCustomHitTest([](WIDGET *psWidget, int x, int y) -> bool { return false; }); + label_title->setTransparentToMouse(true); int titleBottom = label_title->y() + label_title->height(); if (isModal) { @@ -693,8 +692,7 @@ std::shared_ptr W_NOTIFICATION::make(WZ_Queued_Notification* req int heightOfContentsLabel = label_contents->setFormattedString(WzString::fromUtf8(request->notification.contentText), maxContentsWidth, font_regular, WZ_NOTIFICATION_CONTENTS_LINE_SPACING); label_contents->setGeometry(label_contents->x(), label_contents->y(), maxContentsWidth, heightOfContentsLabel); label_contents->setTextAlignment(WLAB_ALIGNTOPLEFT); - // set a custom hit-testing function that ignores all mouse input / clicks - label_contents->setCustomHitTest([](WIDGET *psWidget, int x, int y) -> bool { return false; }); + label_contents->setTransparentToMouse(true); // Add action buttons std::string dismissLabel = _("Dismiss"); diff --git a/src/radar.cpp b/src/radar.cpp index e7f83e20fd6..956e339a4b0 100644 --- a/src/radar.cpp +++ b/src/radar.cpp @@ -81,7 +81,7 @@ class RadarWidget : public WIDGET { protected: void run(W_CONTEXT *) override; void display(int xOffset, int yOffset) override; - bool hitTest(int x, int y) override; + bool hitTest(int x, int y) const override; void clicked(W_CONTEXT *, WIDGET_KEY = WKEY_PRIMARY) override; void released(W_CONTEXT *, WIDGET_KEY = WKEY_PRIMARY) override; void highlight(W_CONTEXT *) override; @@ -878,7 +878,7 @@ void RadarWidget::display(int xOffset, int yOffset) drawRadar(); } -bool RadarWidget::hitTest(int x, int y) +bool RadarWidget::hitTest(int x, int y) const { // NOTE: CoordInRadar expects x, y in *screen* coordinates - this is _currently_ the case as a byproduct of how the radar widget is added to the UI screen return WIDGET::hitTest(x, y) && CoordInRadar(x, y); diff --git a/src/spectatorwidgets.cpp b/src/spectatorwidgets.cpp index 60ba98ccd15..475350808c1 100644 --- a/src/spectatorwidgets.cpp +++ b/src/spectatorwidgets.cpp @@ -124,7 +124,7 @@ bool specLayerInit(bool showButton /*= true*/) specStatsViewCreate(); }); }); - specStatsButton->setCustomHitTest([](WIDGET *psWidget, int x, int y) -> bool { + specStatsButton->setCustomHitTest([](const WIDGET *psWidget, int x, int y) -> bool { if (bDisplayMultiJoiningStatus) { // effectively: make this unclickable until the game actually starts