From d7f9199da282939d3119dd6c7468404ce3b54994 Mon Sep 17 00:00:00 2001 From: ohlidalp Date: Wed, 29 Jan 2025 19:00:04 +0100 Subject: [PATCH 1/5] Fixes #3216 - Airplane dashboard not reacting to mouse _problem: _ wrong viewport dimensions used when calculating relative mouse position to query for overlay element. Broken in #3184. Before that, we queried both mouse position and viewport dimensions directly from OIS. I had to change that because of OIS bug, and now the dimension values are not updated correctly. _solution:_ Minimum viable fix - just keep the same dummy OIS event, just update the viewport dimensions. --- source/main/utils/InputEngine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/main/utils/InputEngine.cpp b/source/main/utils/InputEngine.cpp index bf4a5e47ba..5f7777fa87 100644 --- a/source/main/utils/InputEngine.cpp +++ b/source/main/utils/InputEngine.cpp @@ -609,6 +609,10 @@ OIS::MouseState InputEngine::getMouseState() // See commentary in `resetKeysAndMouseButtons()` // To work around, we keep internal button states and pay attention not to get them polluted by OIS. // ----------------------------------------------------------------------------------------------------- + + mouseState.width = (int)App::GetAppContext()->GetRenderWindow()->getWidth(); + mouseState.height = (int)App::GetAppContext()->GetRenderWindow()->getHeight(); + return mouseState; } From ba0bd2dabb9203a99ff6e8ce262deda66a970c2b Mon Sep 17 00:00:00 2001 From: ohlidalp Date: Wed, 29 Jan 2025 19:39:24 +0100 Subject: [PATCH 2/5] :wastebasket: Rem. dead code from `OverlayWrapper` --- source/main/gui/OverlayWrapper.cpp | 4 ---- source/main/gui/OverlayWrapper.h | 11 ----------- 2 files changed, 15 deletions(-) diff --git a/source/main/gui/OverlayWrapper.cpp b/source/main/gui/OverlayWrapper.cpp index 1eea00646c..05993458cc 100644 --- a/source/main/gui/OverlayWrapper.cpp +++ b/source/main/gui/OverlayWrapper.cpp @@ -356,13 +356,11 @@ void OverlayWrapper::showPressureOverlay(bool show) { m_truck_pressure_overlay->show(); m_truck_pressure_needle_overlay->show(); - BITMASK_SET_1(m_visible_overlays, VisibleOverlays::TRUCK_TIRE_PRESSURE_OVERLAY); } else { m_truck_pressure_overlay->hide(); m_truck_pressure_needle_overlay->hide(); - BITMASK_SET_0(m_visible_overlays, VisibleOverlays::TRUCK_TIRE_PRESSURE_OVERLAY); } } } @@ -953,13 +951,11 @@ void OverlayWrapper::UpdateMarineHUD(ActorPtr vehicle) void OverlayWrapper::ShowRacingOverlay() { m_racing_overlay->show(); - BITMASK_SET_1(m_visible_overlays, VisibleOverlays::RACING); } void OverlayWrapper::HideRacingOverlay() { m_racing_overlay->hide(); - BITMASK_SET_0(m_visible_overlays, VisibleOverlays::RACING); } void OverlayWrapper::UpdateRacingGui(RoR::GfxScene* gs) diff --git a/source/main/gui/OverlayWrapper.h b/source/main/gui/OverlayWrapper.h index 8eb690e88e..3d6c7856b3 100644 --- a/source/main/gui/OverlayWrapper.h +++ b/source/main/gui/OverlayWrapper.h @@ -148,15 +148,6 @@ class OverlayWrapper protected: - /// RoR needs to temporarily hide all overlays when player enters editor. - /// However, OGRE only provides per-overlay show() and hide() functionality. - /// Thus, an external state must be kept to restore overlays after exiting the editor. - struct VisibleOverlays - { - static const int RACING = BITMASK(4); - static const int TRUCK_TIRE_PRESSURE_OVERLAY = BITMASK(5); - }; - int init(); void resizePanel(Ogre::OverlayElement *oe); void reposPanel(Ogre::OverlayElement *oe); @@ -176,8 +167,6 @@ class OverlayWrapper // Overlays // ------------------------------------------------------------- - unsigned int m_visible_overlays = 0; - Ogre::Overlay *m_truck_pressure_overlay = nullptr; Ogre::Overlay *m_truck_pressure_needle_overlay = nullptr; From b088bd0e712fbffff91fccf285f1f6f53899441e Mon Sep 17 00:00:00 2001 From: ohlidalp Date: Wed, 29 Jan 2025 22:08:01 +0100 Subject: [PATCH 3/5] :bug: Fixed closed chatbox still obstructing mouse input to aero dash. --- source/main/gui/panels/GUI_GameChatBox.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/source/main/gui/panels/GUI_GameChatBox.cpp b/source/main/gui/panels/GUI_GameChatBox.cpp index 2d0f961dc2..37136e5882 100644 --- a/source/main/gui/panels/GUI_GameChatBox.cpp +++ b/source/main/gui/panels/GUI_GameChatBox.cpp @@ -64,17 +64,14 @@ void GameChatBox::Draw() msg_pos.y -= chat_size.y; msg_size.y -= 6.f; // prevents partially bottom chat messages } - ImGui::SetNextWindowPos(msg_pos); - ImGui::SetNextWindowSize(msg_size); - - if (m_is_visible) - { - ImGui::Begin("ChatMessages", nullptr, msg_flags); - } else { - ImGui::Begin("ChatMessages", nullptr, msg_flags | ImGuiWindowFlags_NoInputs); + msg_flags |= ImGuiWindowFlags_NoInputs; } + ImGui::SetNextWindowPos(msg_pos); + ImGui::SetNextWindowSize(msg_size); + + ImGui::Begin("ChatMessages", nullptr, msg_flags); if (initialized) { @@ -128,6 +125,10 @@ void GameChatBox::Draw() // Draw chat box ImGuiWindowFlags chat_flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; + if (!m_is_visible) + { + chat_flags |= ImGuiWindowFlags_NoInputs; + } ImGui::SetNextWindowSize(chat_size); ImGui::SetNextWindowPos(ImVec2(theme.screen_edge_padding.x, ImGui::GetIO().DisplaySize.y - (chat_size.y + theme.screen_edge_padding.y))); From dc3e5f9673d57ba12b7ef356ba92bcb6c780d1dd Mon Sep 17 00:00:00 2001 From: ohlidalp Date: Wed, 29 Jan 2025 22:17:17 +0100 Subject: [PATCH 4/5] :video_game: Aero dash: fixed throttless slipping from mouse, added hover. --- source/main/gui/OverlayWrapper.cpp | 126 +++++++++++++++++++++++++---- source/main/gui/OverlayWrapper.h | 14 ++++ 2 files changed, 123 insertions(+), 17 deletions(-) diff --git a/source/main/gui/OverlayWrapper.cpp b/source/main/gui/OverlayWrapper.cpp index 05993458cc..863e5e51e5 100644 --- a/source/main/gui/OverlayWrapper.cpp +++ b/source/main/gui/OverlayWrapper.cpp @@ -338,6 +338,7 @@ int OverlayWrapper::init() bestlaptime = (TextAreaOverlayElement*)loadOverlayElement("tracks/Racing/BestLapTime"); g_is_scaled = true; + m_aerial_dashboard.SetupHoverableElements(); return 0; } @@ -488,7 +489,7 @@ void OverlayWrapper::updateStats(bool detailed) } } -bool OverlayWrapper::handleMouseMoved() +bool OverlayWrapper::handleMousePressed() { if (!m_aerial_dashboard.needles_overlay->isVisible()) return false; @@ -503,33 +504,22 @@ bool OverlayWrapper::handleMouseMoved() if (!player_actor) return res; - float mouseX = ms.X.abs / (float)ms.width; - float mouseY = ms.Y.abs / (float)ms.height; - - // TODO: fix: when the window is scaled, the findElementAt doesn not seem to pick up the correct element :-\ - if (player_actor->ar_driveable == AIRPLANE && ms.buttonDown(OIS::MB_Left)) { const int num_engines = std::min(4, player_actor->ar_num_aeroengines); - OverlayElement* element = m_aerial_dashboard.needles_overlay->findElementAt(mouseX, mouseY); + OverlayElement* element = m_aerial_dashboard.GetHoveredElement(); if (element) { res = true; - float thr_value = 1.0f - ((mouseY - thrtop - throffset) / thrheight); for (int i = 0; i < num_engines; ++i) { if (element == m_aerial_dashboard.engines[i].thr_element) { - player_actor->ar_aeroengines[i]->setThrottle(thr_value); + m_aerial_dashboard.mouse_drag_in_progress = true; } } - } - element = m_aerial_dashboard.dash_overlay->findElementAt(mouseX, mouseY); - if (element) - { - res = true; for (int i = 0; i < num_engines; ++i) { if (element == m_aerial_dashboard.engines[i].engstart_element) @@ -631,14 +621,68 @@ bool OverlayWrapper::handleMouseMoved() return res; } -bool OverlayWrapper::handleMousePressed() +bool OverlayWrapper::handleMouseMoved() { - return handleMouseMoved(); + ActorPtr player_actor = App::GetGameContext()->GetPlayerActor(); + if (!player_actor || !m_aerial_dashboard.needles_overlay->isVisible()) + return false; + + const Ogre::Vector2 mousepos = App::GetInputEngine()->getMouseNormalizedScreenPos(); + + // Update mouse drag if ongoing + if (m_aerial_dashboard.mouse_drag_in_progress) + { + const int num_engines = std::min(4, player_actor->ar_num_aeroengines); + float thr_value = 1.0f - ((mousepos.y - thrtop - throffset) / thrheight); + for (int i = 0; i < num_engines; ++i) + { + if (m_aerial_dashboard.GetHoveredElement() == m_aerial_dashboard.engines[i].thr_element) + { + player_actor->ar_aeroengines[i]->setThrottle(thr_value); + } + } + return true; + } + + // Just detect mouse hover + bool found = false; + Ogre::OverlayElement* needles_element = m_aerial_dashboard.needles_overlay->findElementAt(mousepos.x, mousepos.y); + if (needles_element && m_aerial_dashboard.IsElementHoverable(needles_element)) + { + m_aerial_dashboard.SetHoveredElement(needles_element); + found = true; + } + + if (!found) + { + Ogre::OverlayElement* dash_element = m_aerial_dashboard.dash_overlay->findElementAt(mousepos.x, mousepos.y); + if (dash_element && m_aerial_dashboard.IsElementHoverable(dash_element)) + { + m_aerial_dashboard.SetHoveredElement(dash_element); + found = true; + } + } + + if (!found) + { + m_aerial_dashboard.SetHoveredElement(nullptr); + } + + return false; } bool OverlayWrapper::handleMouseReleased() { - return handleMouseMoved(); + // IMPORTANT: get mouse button state from InputEngine, not from OIS directly + // - that state may be dirty, see commentary in `InputEngine::getMouseState()` + const OIS::MouseState ms = App::GetInputEngine()->getMouseState(); + + if (!ms.buttonDown(OIS::MB_Left)) + { + m_aerial_dashboard.mouse_drag_in_progress = false; + } + + return false; } void OverlayWrapper::UpdatePressureOverlay(RoR::GfxActor* ga) @@ -1082,3 +1126,51 @@ void AeroTrimOverlay::DisplayFormat(const char* fmt, ...) display->setCaption(buffer); } + +bool AeroDashOverlay::IsElementHoverable(Ogre::OverlayElement* element) const +{ + auto itor = std::find_if(hoverable_elements.begin(), hoverable_elements.end(), + [element](Ogre::OverlayElement* e) { return e == element; }); + return itor != hoverable_elements.end(); +} + +void AeroDashOverlay::SetHoveredElement(Ogre::OverlayElement* element) +{ + // Use texture blending to 'highlight' the hovered element + // doc: https://ogrecave.github.io/ogre/api/13/_material-_scripts.html#autotoc_md169 + // ------------------------------------------------------- + + if (m_hovered_element) + { + // reset to defaults + m_hovered_element->getMaterial()->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setColourOperationEx( + Ogre::LBX_MODULATE, Ogre::LBS_TEXTURE, Ogre::LBS_CURRENT); + } + m_hovered_element = element; + if (m_hovered_element) + { + // set blend mode to 'add' with a custom colour + m_hovered_element->getMaterial()->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setColourOperationEx( + Ogre::LBX_ADD, Ogre::LBS_TEXTURE, Ogre::LBS_MANUAL, Ogre::ColourValue::White, ColourValue(0.32, 0.3, 0.25)); + } +} + +void AeroDashOverlay::SetupHoverableElements() +{ + // Aerial dashboard: setup materials for mouse-hover highlighting - give unique instance to each + // ------------------------------------------------------------------------------------------- + + // Throttles + Ogre::MaterialPtr thr_mat = engines[0].thr_element->getMaterial(); + engines[0].thr_element->setMaterial(thr_mat->clone("tracks/thrust1_hoverable")); + engines[1].thr_element->setMaterial(thr_mat->clone("tracks/thrust2_hoverable")); + engines[2].thr_element->setMaterial(thr_mat->clone("tracks/thrust3_hoverable")); + engines[3].thr_element->setMaterial(thr_mat->clone("tracks/thrust4_hoverable")); + hoverable_elements.push_back(engines[0].thr_element); + hoverable_elements.push_back(engines[1].thr_element); + hoverable_elements.push_back(engines[2].thr_element); + hoverable_elements.push_back(engines[3].thr_element); + + +} + diff --git a/source/main/gui/OverlayWrapper.h b/source/main/gui/OverlayWrapper.h index 3d6c7856b3..98df3c1f5d 100644 --- a/source/main/gui/OverlayWrapper.h +++ b/source/main/gui/OverlayWrapper.h @@ -106,6 +106,20 @@ struct AeroDashOverlay float thrust_track_top; float thrust_track_height; + + /// @name Mouse-hover highlighting and dragging + /// @{ + void SetupHoverableElements(); + bool IsElementHoverable(Ogre::OverlayElement* element) const; + void SetHoveredElement(Ogre::OverlayElement* element); + Ogre::OverlayElement* GetHoveredElement() const { return m_hovered_element; } + + bool mouse_drag_in_progress = false; + std::vector hoverable_elements; + /// @} + +private: + Ogre::OverlayElement* m_hovered_element = nullptr; }; class OverlayWrapper From cc93e1dff517b722d77ac79da934531cb6fba853 Mon Sep 17 00:00:00 2001 From: ohlidalp Date: Thu, 30 Jan 2025 02:54:10 +0100 Subject: [PATCH 5/5] :video_game: Aero dashboard: all interactive elements now highlight on mouse hover. --- source/main/gui/OverlayWrapper.cpp | 245 ++++++++++++++++++++--------- source/main/gui/OverlayWrapper.h | 65 +++++--- 2 files changed, 206 insertions(+), 104 deletions(-) diff --git a/source/main/gui/OverlayWrapper.cpp b/source/main/gui/OverlayWrapper.cpp index 863e5e51e5..20b03881ec 100644 --- a/source/main/gui/OverlayWrapper.cpp +++ b/source/main/gui/OverlayWrapper.cpp @@ -190,14 +190,14 @@ int OverlayWrapper::init() m_aerial_dashboard.thrust_track_top = thrtop; m_aerial_dashboard.thrust_track_height = thrheight; - m_aerial_dashboard.engines[0].engfire_element = loadOverlayElement("tracks/engfire1"); - m_aerial_dashboard.engines[1].engfire_element = loadOverlayElement("tracks/engfire2"); - m_aerial_dashboard.engines[2].engfire_element = loadOverlayElement("tracks/engfire3"); - m_aerial_dashboard.engines[3].engfire_element = loadOverlayElement("tracks/engfire4"); - m_aerial_dashboard.engines[0].engstart_element = loadOverlayElement("tracks/engstart1"); - m_aerial_dashboard.engines[1].engstart_element = loadOverlayElement("tracks/engstart2"); - m_aerial_dashboard.engines[2].engstart_element = loadOverlayElement("tracks/engstart3"); - m_aerial_dashboard.engines[3].engstart_element = loadOverlayElement("tracks/engstart4"); + m_aerial_dashboard.engines[0].Setup("tracks/engfire1", "tracks/thrust1"); + m_aerial_dashboard.engines[1].Setup("tracks/engfire2", "tracks/thrust2"); + m_aerial_dashboard.engines[2].Setup("tracks/engfire3", "tracks/thrust3"); + m_aerial_dashboard.engines[3].Setup("tracks/engfire4", "tracks/thrust4"); + m_aerial_dashboard.engstarts[0].Setup("tracks/engstart1", "tracks/engstart-on", "tracks/engstart-off"); + m_aerial_dashboard.engstarts[1].Setup("tracks/engstart2", "tracks/engstart-on", "tracks/engstart-off"); + m_aerial_dashboard.engstarts[2].Setup("tracks/engstart3", "tracks/engstart-on", "tracks/engstart-off"); + m_aerial_dashboard.engstarts[3].Setup("tracks/engstart4", "tracks/engstart-on", "tracks/engstart-off"); resizePanel(loadOverlayElement("tracks/airrpm1")); resizePanel(loadOverlayElement("tracks/airrpm2")); resizePanel(loadOverlayElement("tracks/airrpm3")); @@ -338,7 +338,7 @@ int OverlayWrapper::init() bestlaptime = (TextAreaOverlayElement*)loadOverlayElement("tracks/Racing/BestLapTime"); g_is_scaled = true; - m_aerial_dashboard.SetupHoverableElements(); + m_aerial_dashboard.SetupMouseHovers(); return 0; } @@ -491,24 +491,21 @@ void OverlayWrapper::updateStats(bool detailed) bool OverlayWrapper::handleMousePressed() { - if (!m_aerial_dashboard.needles_overlay->isVisible()) + ActorPtr player_actor = App::GetGameContext()->GetPlayerActor(); + if (!player_actor + || !m_aerial_dashboard.needles_overlay->isVisible() + || !m_aerial_dashboard.hovered_widget) return false; - bool res = false; // IMPORTANT: get mouse button state from InputEngine, not from OIS directly // - that state may be dirty, see commentary in `InputEngine::getMouseState()` const OIS::MouseState ms = App::GetInputEngine()->getMouseState(); - - ActorPtr player_actor = App::GetGameContext()->GetPlayerActor(); - - if (!player_actor) - return res; - + bool res = false; if (player_actor->ar_driveable == AIRPLANE && ms.buttonDown(OIS::MB_Left)) { const int num_engines = std::min(4, player_actor->ar_num_aeroengines); - OverlayElement* element = m_aerial_dashboard.GetHoveredElement(); + OverlayElement* element = m_aerial_dashboard.hovered_widget->GetHoveredElement(); if (element) { res = true; @@ -522,11 +519,12 @@ bool OverlayWrapper::handleMousePressed() for (int i = 0; i < num_engines; ++i) { - if (element == m_aerial_dashboard.engines[i].engstart_element) + if (element == m_aerial_dashboard.engstarts[i].element) { player_actor->ar_aeroengines[i]->flipStart(); } } + if (player_actor->ar_autopilot && mTimeUntilNextToggle <= 0) { //heading group @@ -636,7 +634,7 @@ bool OverlayWrapper::handleMouseMoved() float thr_value = 1.0f - ((mousepos.y - thrtop - throffset) / thrheight); for (int i = 0; i < num_engines; ++i) { - if (m_aerial_dashboard.GetHoveredElement() == m_aerial_dashboard.engines[i].thr_element) + if (m_aerial_dashboard.hovered_widget == &m_aerial_dashboard.engines[i]) { player_actor->ar_aeroengines[i]->setThrottle(thr_value); } @@ -645,28 +643,7 @@ bool OverlayWrapper::handleMouseMoved() } // Just detect mouse hover - bool found = false; - Ogre::OverlayElement* needles_element = m_aerial_dashboard.needles_overlay->findElementAt(mousepos.x, mousepos.y); - if (needles_element && m_aerial_dashboard.IsElementHoverable(needles_element)) - { - m_aerial_dashboard.SetHoveredElement(needles_element); - found = true; - } - - if (!found) - { - Ogre::OverlayElement* dash_element = m_aerial_dashboard.dash_overlay->findElementAt(mousepos.x, mousepos.y); - if (dash_element && m_aerial_dashboard.IsElementHoverable(dash_element)) - { - m_aerial_dashboard.SetHoveredElement(dash_element); - found = true; - } - } - - if (!found) - { - m_aerial_dashboard.SetHoveredElement(nullptr); - } + m_aerial_dashboard.UpdateMouseHovers(); return false; } @@ -1086,36 +1063,130 @@ void AeroDashOverlay::SetIgnition(int engine, bool visible, bool ignited) { if (visible) { - engines[engine].engstart_element->show(); - engines[engine].engstart_element->setMaterialName( - ignited ? "tracks/engstart-on" : "tracks/engstart-off"); + engstarts[engine].element->show(); + engstarts[engine].SetActive(ignited); } else { - engines[engine].engstart_element->hide(); + engstarts[engine].element->hide(); } } -void AeroSwitchOverlay::SetActive(bool value) +// ----------------- +// AeroEngineOverlay + +void AeroEngineWidget::Setup(std::string const& engfire_elemname, std::string const& thr_elemname) +{ + // Because highlighting works on per-material basis, we must clone the material for each element + engfire_element = Ogre::OverlayManager::getSingleton().getOverlayElement(engfire_elemname); + thr_element = Ogre::OverlayManager::getSingleton().getOverlayElement(thr_elemname); + thr_element->setMaterial(thr_element->getMaterial()->clone(thr_element->getMaterial()->getName() + "@" + thr_elemname)); +} + +bool AeroEngineWidget::UpdateMouseHover() +{ + const Ogre::Vector2 mouse_pos = App::GetInputEngine()->getMouseNormalizedScreenPos(); + bool retval = false; + if (retval = thr_element->contains(mouse_pos.x, mouse_pos.y)) + { + AeroDashOverlay::SetMaterialHighlighted(thr_element->getMaterial(), true); + } + else + { + AeroDashOverlay::SetMaterialHighlighted(thr_element->getMaterial(), false); + } + return retval; +} + +Ogre::OverlayElement* AeroEngineWidget::GetHoveredElement() const +{ + return thr_element; +} + +// --------------- +// AeroSwitchOverlay + +void AeroSwitchWidget::SetActive(bool value) { element->setMaterial(value ? on_material : off_material); } -void AeroSwitchOverlay::Setup(std::string const & elem_name, std::string const & mat_on, std::string const & mat_off) +void AeroSwitchWidget::Setup(std::string const & elem_name, std::string const & mat_on, std::string const & mat_off) { + // Because highlighting works on per-material basis, we must clone the material for each element element = Ogre::OverlayManager::getSingleton().getOverlayElement(elem_name); - on_material = Ogre::MaterialManager::getSingleton().getByName(mat_on); - off_material = Ogre::MaterialManager::getSingleton().getByName(mat_off); + on_material = Ogre::MaterialManager::getSingleton().getByName(mat_on)->clone(elem_name + "@" + mat_on); + off_material = Ogre::MaterialManager::getSingleton().getByName(mat_off)->clone(elem_name + "@" + mat_off); } -void AeroTrimOverlay::Setup(std::string const & up, std::string const & dn, std::string const & disp) +bool AeroSwitchWidget::UpdateMouseHover() { + // Element's current material switches dynamically based on game state, we must always sync both variants. + const Ogre::Vector2 mouse_pos = App::GetInputEngine()->getMouseNormalizedScreenPos(); + bool retval = false; + if (retval = element->contains(mouse_pos.x, mouse_pos.y)) + { + AeroDashOverlay::SetMaterialHighlighted(on_material, true); + AeroDashOverlay::SetMaterialHighlighted(off_material, true); + } + else + { + AeroDashOverlay::SetMaterialHighlighted(on_material, false); + AeroDashOverlay::SetMaterialHighlighted(off_material, false); + } + return retval; +} + +Ogre::OverlayElement* AeroSwitchWidget::GetHoveredElement() const +{ + return element; +} + +// --------------- +// AeroTrimOverlay + +void AeroTrimWidget::Setup(std::string const & up, std::string const & dn, std::string const & disp) +{ + // Because highlighting works on per-material basis, we must clone the material for each element display = Ogre::OverlayManager::getSingleton().getOverlayElement(disp); up_button = Ogre::OverlayManager::getSingleton().getOverlayElement(up); + up_button->setMaterial(up_button->getMaterial()->clone(up + "$" + disp)); dn_button = Ogre::OverlayManager::getSingleton().getOverlayElement(dn); + dn_button->setMaterial(dn_button->getMaterial()->clone(dn + "$" + disp)); +} + +bool AeroTrimWidget::UpdateMouseHover() +{ + const Ogre::Vector2 mouse_pos = App::GetInputEngine()->getMouseNormalizedScreenPos(); + + bool retval = false; + if (retval = up_button->contains(mouse_pos.x, mouse_pos.y)) + { + hovered_button = up_button; + AeroDashOverlay::SetMaterialHighlighted(up_button->getMaterial(), true); + AeroDashOverlay::SetMaterialHighlighted(dn_button->getMaterial(), false); + } + else if (retval = dn_button->contains(mouse_pos.x, mouse_pos.y)) + { + hovered_button = dn_button; + AeroDashOverlay::SetMaterialHighlighted(up_button->getMaterial(), false); + AeroDashOverlay::SetMaterialHighlighted(dn_button->getMaterial(), true); + } + else + { + hovered_button = nullptr; + AeroDashOverlay::SetMaterialHighlighted(up_button->getMaterial(), false); + AeroDashOverlay::SetMaterialHighlighted(dn_button->getMaterial(), false); + } + return retval; } -void AeroTrimOverlay::DisplayFormat(const char* fmt, ...) +Ogre::OverlayElement* AeroTrimWidget::GetHoveredElement() const +{ + return hovered_button; +} + +void AeroTrimWidget::DisplayFormat(const char* fmt, ...) { char buffer[500] = {}; @@ -1127,50 +1198,68 @@ void AeroTrimOverlay::DisplayFormat(const char* fmt, ...) display->setCaption(buffer); } -bool AeroDashOverlay::IsElementHoverable(Ogre::OverlayElement* element) const -{ - auto itor = std::find_if(hoverable_elements.begin(), hoverable_elements.end(), - [element](Ogre::OverlayElement* e) { return e == element; }); - return itor != hoverable_elements.end(); -} +// --------------------- +// AeroDashOverlay -void AeroDashOverlay::SetHoveredElement(Ogre::OverlayElement* element) +void AeroDashOverlay::SetMaterialHighlighted(Ogre::MaterialPtr mat, bool val) { // Use texture blending to 'highlight' the hovered element // doc: https://ogrecave.github.io/ogre/api/13/_material-_scripts.html#autotoc_md169 // ------------------------------------------------------- - if (m_hovered_element) + if (!val) { // reset to defaults - m_hovered_element->getMaterial()->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setColourOperationEx( + mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setColourOperationEx( Ogre::LBX_MODULATE, Ogre::LBS_TEXTURE, Ogre::LBS_CURRENT); } - m_hovered_element = element; - if (m_hovered_element) + else { // set blend mode to 'add' with a custom colour - m_hovered_element->getMaterial()->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setColourOperationEx( + mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setColourOperationEx( Ogre::LBX_ADD, Ogre::LBS_TEXTURE, Ogre::LBS_MANUAL, Ogre::ColourValue::White, ColourValue(0.32, 0.3, 0.25)); } } -void AeroDashOverlay::SetupHoverableElements() +void AeroDashOverlay::SetupMouseHovers() { - // Aerial dashboard: setup materials for mouse-hover highlighting - give unique instance to each - // ------------------------------------------------------------------------------------------- - // Throttles - Ogre::MaterialPtr thr_mat = engines[0].thr_element->getMaterial(); - engines[0].thr_element->setMaterial(thr_mat->clone("tracks/thrust1_hoverable")); - engines[1].thr_element->setMaterial(thr_mat->clone("tracks/thrust2_hoverable")); - engines[2].thr_element->setMaterial(thr_mat->clone("tracks/thrust3_hoverable")); - engines[3].thr_element->setMaterial(thr_mat->clone("tracks/thrust4_hoverable")); - hoverable_elements.push_back(engines[0].thr_element); - hoverable_elements.push_back(engines[1].thr_element); - hoverable_elements.push_back(engines[2].thr_element); - hoverable_elements.push_back(engines[3].thr_element); - + aero_widgets.push_back(&engines[0]); + aero_widgets.push_back(&engines[1]); + aero_widgets.push_back(&engines[2]); + aero_widgets.push_back(&engines[3]); + + // Engine start switches + aero_widgets.push_back(&engstarts[0]); + aero_widgets.push_back(&engstarts[1]); + aero_widgets.push_back(&engstarts[2]); + aero_widgets.push_back(&engstarts[3]); + + // Switches + aero_widgets.push_back(&hdg ); + aero_widgets.push_back(&wlv ); + aero_widgets.push_back(&nav ); + aero_widgets.push_back(&alt ); + aero_widgets.push_back(&vs ); + aero_widgets.push_back(&ias ); + aero_widgets.push_back(&gpws ); + aero_widgets.push_back(&brks ); + + // Trims + aero_widgets.push_back(&hdg_trim); + aero_widgets.push_back(&alt_trim); + aero_widgets.push_back(&vs_trim); + aero_widgets.push_back(&ias_trim); +} +void AeroDashOverlay::UpdateMouseHovers() +{ + for (auto elem : aero_widgets) + { + if (elem->UpdateMouseHover()) + { + hovered_widget = elem; + } + } } diff --git a/source/main/gui/OverlayWrapper.h b/source/main/gui/OverlayWrapper.h index 98df3c1f5d..ce0713c54e 100644 --- a/source/main/gui/OverlayWrapper.h +++ b/source/main/gui/OverlayWrapper.h @@ -34,33 +34,49 @@ namespace RoR { -struct AeroEngineOverlay +/// All this complexity just to have a nifty mouse-hover highlighting for the dashboard elements (well, proper throttle sliders also). +struct AeroInteractiveWidget { + virtual ~AeroInteractiveWidget() = default; + virtual bool UpdateMouseHover() = 0; + virtual Ogre::OverlayElement* GetHoveredElement() const = 0; +}; + +struct AeroEngineWidget : public AeroInteractiveWidget +{ + void Setup(std::string const& engfire_elemname, std::string const& thr_elemname); + bool UpdateMouseHover() override; + Ogre::OverlayElement* GetHoveredElement() const override; + Ogre::OverlayElement *thr_element; Ogre::OverlayElement *engfire_element; - Ogre::OverlayElement *engstart_element; Ogre::TextureUnitState *rpm_texture; Ogre::TextureUnitState *pitch_texture; Ogre::TextureUnitState *torque_texture; }; -struct AeroSwitchOverlay +struct AeroSwitchWidget: public AeroInteractiveWidget { void Setup(std::string const & elem_name, std::string const & mat_on, std::string const & mat_off); void SetActive(bool value); + bool UpdateMouseHover() override; + Ogre::OverlayElement* GetHoveredElement() const override; Ogre::OverlayElement *element; Ogre::MaterialPtr on_material; Ogre::MaterialPtr off_material; }; -struct AeroTrimOverlay +struct AeroTrimWidget : public AeroInteractiveWidget { void Setup(std::string const & up, std::string const & dn, std::string const & disp); void DisplayFormat(const char* fmt, ...); + bool UpdateMouseHover() override; + Ogre::OverlayElement* GetHoveredElement() const override; Ogre::OverlayElement *up_button; Ogre::OverlayElement *dn_button; + Ogre::OverlayElement* hovered_button = nullptr; Ogre::OverlayElement *display; }; @@ -73,7 +89,8 @@ struct AeroDashOverlay void SetEngineTorque(int engine, float pcent); void SetIgnition(int engine, bool visible, bool ignited); - AeroEngineOverlay engines[4]; + AeroEngineWidget engines[4]; + AeroSwitchWidget engstarts[4]; Ogre::Overlay *dash_overlay; Ogre::Overlay *needles_overlay; @@ -90,36 +107,32 @@ struct AeroDashOverlay Ogre::TextureUnitState *aoatexture; Ogre::TextAreaOverlayElement* alt_value_textarea; - AeroSwitchOverlay hdg; - AeroSwitchOverlay wlv; - AeroSwitchOverlay nav; - AeroSwitchOverlay alt; - AeroSwitchOverlay vs; - AeroSwitchOverlay ias; - AeroSwitchOverlay gpws; - AeroSwitchOverlay brks; + AeroSwitchWidget hdg; + AeroSwitchWidget wlv; + AeroSwitchWidget nav; + AeroSwitchWidget alt; + AeroSwitchWidget vs; + AeroSwitchWidget ias; + AeroSwitchWidget gpws; + AeroSwitchWidget brks; - AeroTrimOverlay hdg_trim; - AeroTrimOverlay alt_trim; - AeroTrimOverlay vs_trim; - AeroTrimOverlay ias_trim; + AeroTrimWidget hdg_trim; + AeroTrimWidget alt_trim; + AeroTrimWidget vs_trim; + AeroTrimWidget ias_trim; float thrust_track_top; float thrust_track_height; /// @name Mouse-hover highlighting and dragging /// @{ - void SetupHoverableElements(); - bool IsElementHoverable(Ogre::OverlayElement* element) const; - void SetHoveredElement(Ogre::OverlayElement* element); - Ogre::OverlayElement* GetHoveredElement() const { return m_hovered_element; } - bool mouse_drag_in_progress = false; - std::vector hoverable_elements; + std::vector aero_widgets; + AeroInteractiveWidget* hovered_widget = nullptr; + void SetupMouseHovers(); + void UpdateMouseHovers(); + static void SetMaterialHighlighted(Ogre::MaterialPtr, bool value); /// @} - -private: - Ogre::OverlayElement* m_hovered_element = nullptr; }; class OverlayWrapper