From 259330ab1494a783e4cfced6191113e023fa6163 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Fri, 27 Nov 2020 21:47:20 +0100 Subject: [PATCH] Drop MyGUI 3.4 requirement. Manage layer and widget focus manually. --- apps/openmw/mwbase/environment.cpp | 2 +- apps/openmw/mwbase/environment.hpp | 2 +- apps/openmw/mwgui/keyboardnavigation.cpp | 9 ++ apps/openmw/mwgui/layout.cpp | 19 --- apps/openmw/mwgui/layout.hpp | 3 - apps/openmw/mwgui/tooltips.cpp | 9 +- apps/openmw/mwgui/windowmanagerimp.cpp | 4 +- apps/openmw/mwinput/mousemanager.cpp | 5 + apps/openmw/mwvr/vrgui.cpp | 188 ++++++++++++++--------- apps/openmw/mwvr/vrgui.hpp | 27 +++- 10 files changed, 164 insertions(+), 104 deletions(-) diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 90e228aec..9cb5aa66c 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -109,7 +109,7 @@ void MWBase::Environment::setVrMode(bool vrMode) mVrMode = vrMode; } -bool MWBase::Environment::getVrMode(void) +bool MWBase::Environment::getVrMode(void) const { return mVrMode; } diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 46690795e..23e776495 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -79,7 +79,7 @@ namespace MWBase void limitFrameRate(double dt) const; void setVrMode(bool vrMode); - bool getVrMode(void); + bool getVrMode(void) const; World *getWorld() const; diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index 6dd66029b..35c2b0058 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -11,6 +11,9 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwvr/vrenvironment.hpp" +#include "../mwvr/vrgui.hpp" + namespace MWGui { @@ -91,6 +94,9 @@ void KeyboardNavigation::_unlinkWidget(MyGUI::Widget *widget) w.second = nullptr; if (widget == mCurrentFocus) mCurrentFocus = nullptr; + + if (MWBase::Environment::get().getVrMode()) + MWVR::Environment::get().getGUIManager()->notifyWidgetUnlinked(widget); } void styleFocusedButton(MyGUI::Widget* w) @@ -169,6 +175,9 @@ void KeyboardNavigation::setDefaultFocus(MyGUI::Widget *window, MyGUI::Widget *d void KeyboardNavigation::setModalWindow(MyGUI::Widget *window) { mModalWindow = window; + + if (MWBase::Environment::get().getVrMode()) + MWVR::Environment::get().getGUIManager()->notifyModalWindow(window); } void KeyboardNavigation::setEnabled(bool enabled) diff --git a/apps/openmw/mwgui/layout.cpp b/apps/openmw/mwgui/layout.cpp index ab201ae64..49030817b 100644 --- a/apps/openmw/mwgui/layout.cpp +++ b/apps/openmw/mwgui/layout.cpp @@ -83,25 +83,6 @@ namespace MWGui window->setCaptionWithReplacing(title); } - void Layout::setLayerPick(bool pick) - { -#if MYGUI_VERSION >= MYGUI_DEFINE_VERSION(3,4,0) - MyGUI::ILayer* layer = mMainWidget->getLayer(); - // MyGUI exposes pick on the implementations of ILayer only, but not ILayer itself. - auto* oLayer = layer->castType(false); - auto* sLayer = layer->castType(false); - if (oLayer) - oLayer->setPick(pick); - if (sLayer) - sLayer->setPick(pick); -#else -#ifdef USE_OPENXR -#error "MyGUI version 3.4.0 or greater required to build for VR" -#endif - throw std::logic_error("Not implemented"); -#endif - } - MyGUI::Widget* Layout::getWidget(const std::string &_name) { for (MyGUI::Widget* widget : mListWindowRoot) diff --git a/apps/openmw/mwgui/layout.hpp b/apps/openmw/mwgui/layout.hpp index d10aadc55..910015c1e 100644 --- a/apps/openmw/mwgui/layout.hpp +++ b/apps/openmw/mwgui/layout.hpp @@ -64,9 +64,6 @@ namespace MWGui // NOTE: this assume that mMainWidget is of type Window. void setTitle(const std::string& title); - /// \note Affects the layer, not just the widget. - void setLayerPick(bool pick); - MyGUI::Widget* mMainWidget; protected: diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 3d60243b7..6cd558aad 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -25,6 +25,9 @@ #include "itemmodel.hpp" +#include "../mwvr/vrenvironment.hpp" +#include "../mwvr/vrgui.hpp" + namespace MWGui { std::string ToolTips::sSchoolNames[] = {"#{sSchoolAlteration}", "#{sSchoolConjuration}", "#{sSchoolDestruction}", "#{sSchoolIllusion}", "#{sSchoolMysticism}", "#{sSchoolRestoration}"}; @@ -156,7 +159,11 @@ namespace MWGui if (mRemainingDelay > 0) return; - MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget(); + MyGUI::Widget* focus = nullptr; + if (MWBase::Environment::get().getVrMode()) + focus = MWVR::Environment::get().getGUIManager()->focusWidget(); + else + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget(); if (focus == 0) return; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 7a75b092e..a1a1f55fd 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1781,7 +1781,7 @@ namespace MWGui #ifdef USE_OPENXR auto* vrGuiManager = MWVR::Environment::get().getGUIManager(); - vrGuiManager->insertLayer(mVideoBackground->getLayer()->getName()); + vrGuiManager->insertLayer(mVideoBackground->getLayer()); #endif bool cursorWasVisible = mCursorVisible; @@ -1834,7 +1834,7 @@ namespace MWGui updateVisible(); #ifdef USE_OPENXR - vrGuiManager->removeLayer(mVideoBackground->getLayer()->getName()); + vrGuiManager->removeLayer(mVideoBackground->getLayer()); #endif mVideoBackground->setVisible(false); mVideoEnabled = false; diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index 2f80db522..9575b5f1a 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -67,6 +67,9 @@ namespace MWInput void MouseManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg) { + if (MWBase::Environment::get().getVrMode()) + return; + mBindingsManager->mouseMoved(arg); MWBase::InputManager* input = MWBase::Environment::get().getInputManager(); @@ -236,6 +239,8 @@ namespace MWInput void MouseManager::injectMouseMove(float xMove, float yMove, float mouseWheelMove) { + if (MWBase::Environment::get().getVrMode()) + return; mGuiCursorX += xMove; mGuiCursorY += yMove; mMouseWheel += mouseWheelMove; diff --git a/apps/openmw/mwvr/vrgui.cpp b/apps/openmw/mwvr/vrgui.cpp index d420bf0dc..595a35f92 100644 --- a/apps/openmw/mwvr/vrgui.cpp +++ b/apps/openmw/mwvr/vrgui.cpp @@ -40,6 +40,7 @@ #include #include #include +#include namespace osg { @@ -146,11 +147,12 @@ namespace MWVR VRGUILayer::VRGUILayer( osg::ref_ptr geometryRoot, osg::ref_ptr cameraRoot, - std::string layerName, + MyGUI::ILayer* layer, LayerConfig config, VRGUIManager* parent) : mConfig(config) - , mLayerName(layerName) + , mLayerName(layer->getName()) + , mMyGUILayer(layer) , mGeometryRoot(geometryRoot) , mCameraRoot(cameraRoot) { @@ -468,7 +470,8 @@ namespace MWVR osg::Vec2(1,1), sizingMode, TrackingMode::Menu, - extraLayers + extraLayers, + false }; } LayerConfig gDefaultConfig = createDefaultConfig(1); @@ -493,7 +496,8 @@ namespace MWVR osg::Vec2(1,1), SizingMode::Auto, TrackingMode::HudLeftHand, - "" + "", + true }; static const float sSideBySideRadius = 1.f; @@ -512,7 +516,8 @@ namespace MWVR osg::Vec2(0.70f, 0.70f), SizingMode::Fixed, gDefaultConfig.trackingMode, - "" + "", + false }; }; @@ -536,7 +541,8 @@ namespace MWVR gDefaultConfig.myGUIViewSize, SizingMode::Auto, TrackingMode::HudLeftHand, - "" + "", + false }; LayerConfig gPopupConfig = LayerConfig @@ -552,7 +558,8 @@ namespace MWVR gDefaultConfig.myGUIViewSize, SizingMode::Auto, TrackingMode::HudRightHand, - "" + "", + false }; @@ -601,9 +608,10 @@ namespace MWVR mSideBySideLayers[i]->setAngle(low + static_cast(i) * sSideBySideAzimuthInterval); } - void VRGUIManager::insertLayer(const std::string& name) + void VRGUIManager::insertLayer(MyGUI::ILayer* layer) { LayerConfig config = gDefaultConfig; + const auto& name = layer->getName(); auto configIt = gLayerConfigs.find(name); if (configIt != gLayerConfigs.end()) { @@ -614,25 +622,25 @@ namespace MWVR Log(Debug::Warning) << "Layer " << name << " has no configuration, using default"; } - auto layer = std::shared_ptr(new VRGUILayer( + auto vrlayer = std::shared_ptr(new VRGUILayer( mGUIGeometriesRoot, mGUICamerasRoot, - name, + layer, config, this )); - mLayers[name] = layer; + mLayers[name] = vrlayer; - layer->mGeometry->setUserData(new VRGUILayerUserData(mLayers[name])); + vrlayer->mGeometry->setUserData(new VRGUILayerUserData(mLayers[name])); if (config.sideBySide) { - mSideBySideLayers.push_back(layer); + mSideBySideLayers.push_back(vrlayer); updateSideBySideLayers(); } if (config.trackingMode == TrackingMode::Menu) - layer->updateTracking(mHeadPose); + vrlayer->updateTracking(mHeadPose); } void VRGUIManager::insertWidget(MWGui::Layout* widget) @@ -643,32 +651,24 @@ namespace MWVR auto it = mLayers.find(name); if (it == mLayers.end()) { - insertLayer(name); + insertLayer(layer); it = mLayers.find(name); - if (it == mLayers.end()) - { - Log(Debug::Error) << "Failed to insert layer " << name; - return; - } } it->second->insertWidget(widget); - - if (it->second.get() != mFocusLayer) - widget->setLayerPick(false); } - void VRGUIManager::removeLayer(const std::string& name) + void VRGUIManager::removeLayer(MyGUI::ILayer* layer) { - auto it = mLayers.find(name); + auto it = mLayers.find(layer->getName()); if (it == mLayers.end()) return; - auto layer = it->second; + auto vrlayer = it->second; for (auto it2 = mSideBySideLayers.begin(); it2 < mSideBySideLayers.end(); it2++) { - if (*it2 == layer) + if (*it2 == vrlayer) { mSideBySideLayers.erase(it2); updateSideBySideLayers(); @@ -684,9 +684,8 @@ namespace MWVR void VRGUIManager::removeWidget(MWGui::Layout* widget) { auto* layer = widget->mMainWidget->getLayer(); - auto name = layer->getName(); - auto it = mLayers.find(name); + auto it = mLayers.find(layer->getName()); if (it == mLayers.end()) { //Log(Debug::Warning) << "Tried to remove widget from nonexistent layer " << name; @@ -696,7 +695,7 @@ namespace MWVR it->second->removeWidget(widget); if (it->second->widgetCount() == 0) { - removeLayer(name); + removeLayer(layer); } } @@ -705,14 +704,12 @@ namespace MWVR auto* layer = widget->mMainWidget->getLayer(); auto name = layer->getName(); - Log(Debug::Verbose) << "setVisible (" << name << "): " << visible; if (layerBlacklist.find(name) != layerBlacklist.end()) { - Log(Debug::Verbose) << "Blacklisted"; - // Never pick an invisible layer - widget->setLayerPick(false); + Log(Debug::Verbose) << "setVisible (" << name << "): ignored"; return; } + Log(Debug::Verbose) << "setVisible (" << name << "): " << visible; if (visible) insertWidget(widget); @@ -807,46 +804,111 @@ namespace MWVR if (layer == mFocusLayer) return; - if (mFocusLayer) - { - mFocusLayer->mWidgets.front()->setLayerPick(false); - } + setFocusWidget(nullptr); mFocusLayer = layer; - if (mFocusLayer) - { - Log(Debug::Verbose) << "Set focus layer to " << mFocusLayer->mWidgets.front()->mMainWidget->getLayer()->getName(); - mFocusLayer->mWidgets.front()->setLayerPick(true); - } - else - { - Log(Debug::Verbose) << "Set focus layer to null"; - } } void VRGUIManager::setFocusWidget(MyGUI::Widget* widget) { - // TODO: This relies on MyGUI internal functions and may break on any future version. if (widget == mFocusWidget) return; - if (mFocusWidget) + + // TODO: This relies on MyGUI internal functions and may break on any future version. + if (validateFocusWidget()) mFocusWidget->_riseMouseLostFocus(widget); if (widget) widget->_riseMouseSetFocus(mFocusWidget); mFocusWidget = widget; } - bool VRGUIManager::injectMouseClick(bool onPress) + // MyGUI may delete the focusWidget. + // Call this and check the result before dereferencing mFocusWidget + bool VRGUIManager::validateFocusWidget() { - // TODO: This relies on MyGUI internal functions and may break on any future version. if (mFocusWidget) { - if(onPress) - mFocusWidget->_riseMouseButtonClick(); + auto* cursorWidget = widgetFromGuiCursor(mGuiCursor.x(), mGuiCursor.y()); + // If the focus widget is no longer seen at the coordinate it was last seen + // it may have been deleted by mygui. + // In theory, notifyWidgetUnlinked() should catch all cases but doesn't. + if (cursorWidget != mFocusWidget) + { + mFocusWidget = nullptr; + return false; + } return true; } return false; } + bool VRGUIManager::focusIsModalWindow() + { + if (mFocusLayer) + for (auto* window : mFocusLayer->mWidgets) + if (mModalWindow == window->mMainWidget) + return true; + return false; + } + + MyGUI::Widget* VRGUIManager::widgetFromGuiCursor(int x, int y) + { + if (mFocusLayer) + { + if ( + mFocusLayer->mConfig.ignoreModality + || !mModalWindow + || focusIsModalWindow() + ) + return static_cast(mFocusLayer->mMyGUILayer->getLayerItemByPoint(x, y)); + } + return nullptr; + } + + void VRGUIManager::updateGuiCursor(int x, int y) + { + setFocusWidget(widgetFromGuiCursor(x, y)); + mGuiCursor.x() = x; + mGuiCursor.y() = y; + } + + bool VRGUIManager::injectMouseClick(bool onPress) + { + // TODO: This relies on MyGUI internal functions and may break on any future version. + if (validateFocusWidget()) + { + if (onPress) + { + mFocusWidget->_riseMouseButtonPressed(mGuiCursor.x(), mGuiCursor.y(), MyGUI::MouseButton::Left); + + MyGUI::Button* b = mFocusWidget->castType(false); + if (b && b->getEnabled()) + { + MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); + } + } + else + { + mFocusWidget->_riseMouseButtonReleased(mGuiCursor.x(), mGuiCursor.y(), MyGUI::MouseButton::Left); + mFocusWidget->_riseMouseButtonClick(); + } + return true; + } + return false; + } + + void VRGUIManager::notifyWidgetUnlinked(MyGUI::Widget* widget) + { + if (widget == mFocusWidget) + { + mFocusWidget = nullptr; + } + } + + void VRGUIManager::notifyModalWindow(MyGUI::Widget* window) + { + mModalWindow = window; + } + void VRGUIManager::computeGuiCursor(osg::Vec3 hitPoint) { float x = 0; @@ -866,26 +928,8 @@ namespace MWVR y = bottom - height * y; } - mGuiCursor.x() = (int)x; - mGuiCursor.y() = (int)y; - - MyGUI::InputManager::getInstance().injectMouseMove((int)x, (int)y, 0); MWBase::Environment::get().getWindowManager()->setCursorActive(true); - - // The virtual keyboard must be interactive regardless of modals - // This could be generalized with another config entry, but i don't think any other - // widgets/layers need it so i'm hardcoding it for the VirtualKeyboard for now. - if ( - mFocusLayer - && mFocusLayer->mLayerName == "VirtualKeyboard" - && MyGUI::InputManager::getInstance().isModalAny()) - { - auto* widget = MyGUI::LayerManager::getInstance().getWidgetFromPoint((int)x, (int)y); - setFocusWidget(widget); - } - else - setFocusWidget(nullptr); - + updateGuiCursor((int)x, (int)y); } } diff --git a/apps/openmw/mwvr/vrgui.hpp b/apps/openmw/mwvr/vrgui.hpp index d32edb4ff..cb2c5c054 100644 --- a/apps/openmw/mwvr/vrgui.hpp +++ b/apps/openmw/mwvr/vrgui.hpp @@ -1,5 +1,5 @@ -#ifndef OPENXR_MENU_HPP -#define OPENXR_MENU_HPP +#ifndef VRGUI_HPP +#define VRGUI_HPP #include #include @@ -25,6 +25,7 @@ namespace MWGui { class Layout; class WindowBase; + class ILayer; } struct XrCompositionLayerQuad; @@ -63,6 +64,7 @@ namespace MWVR SizingMode sizingMode; //!< How to size the layer TrackingMode trackingMode; //!< Tracking mode std::string extraLayers; //!< Additional layers to draw (list separated by any non-alphabetic) + bool ignoreModality; //!< Layer input should be enabled even if another modal window is active bool operator<(const LayerConfig& rhs) const { return priority < rhs.priority; } }; @@ -78,7 +80,7 @@ namespace MWVR VRGUILayer( osg::ref_ptr geometryRoot, osg::ref_ptr cameraRoot, - std::string layerName, + MyGUI::ILayer* layer, LayerConfig config, VRGUIManager* parent); ~VRGUILayer(); @@ -102,6 +104,7 @@ namespace MWVR Pose mTrackedPose{}; LayerConfig mConfig; std::string mLayerName; + MyGUI::ILayer* mMyGUILayer; std::vector mWidgets; osg::ref_ptr mGeometryRoot; osg::ref_ptr mGeometry{ new osg::Geometry }; @@ -139,10 +142,10 @@ namespace MWVR void setVisible(MWGui::Layout*, bool visible); /// Insert the given layer quad if it isn't already - void insertLayer(const std::string& name); + void insertLayer(MyGUI::ILayer* layer); /// Remove the given layer quad - void removeLayer(const std::string& name); + void removeLayer(MyGUI::ILayer* layer); /// Update layer quads based on current camera void updateTracking(void); @@ -162,6 +165,15 @@ namespace MWVR /// Inject mouse click if applicable bool injectMouseClick(bool onPress); + /// Notify VRGUIManager that a widget has been unlinked + void notifyWidgetUnlinked(MyGUI::Widget* widget); + + /// Notify VRGUIManager that a window is modal + void notifyModalWindow(MyGUI::Widget* window); + + /// Currently focus widget according to VR + MyGUI::Widget* focusWidget() { return mFocusWidget; } + private: void computeGuiCursor(osg::Vec3 hitPoint); void updateSideBySideLayers(); @@ -169,6 +181,10 @@ namespace MWVR void removeWidget(MWGui::Layout* widget); void setFocusLayer(VRGUILayer* layer); void setFocusWidget(MyGUI::Widget* widget); + bool validateFocusWidget(); + bool focusIsModalWindow(); + MyGUI::Widget* widgetFromGuiCursor(int x, int y); + void updateGuiCursor(int x, int y); osg::ref_ptr mOsgViewer{ nullptr }; @@ -183,6 +199,7 @@ namespace MWVR osg::Vec2i mGuiCursor{}; VRGUILayer* mFocusLayer{ nullptr }; MyGUI::Widget* mFocusWidget{ nullptr }; + MyGUI::Widget* mModalWindow{ nullptr }; osg::observer_ptr mCamera{ nullptr }; }; }