QWERTY implementation of a virtual keyboard.

pull/615/head
Mads Buvik Sandvei 4 years ago
parent 3158a2510e
commit 3e581571f4

@ -249,7 +249,7 @@ if(BUILD_OPENMW_VR)
add_openmw_dir (mwvr add_openmw_dir (mwvr
openxraction openxractionset openxrdebug openxrinput openxrmanager openxrmanagerimpl openxrswapchain openxrswapchainimpl openxraction openxractionset openxrdebug openxrinput openxrmanager openxrmanagerimpl openxrswapchain openxrswapchainimpl
realisticcombat realisticcombat
vranimation vrcamera vrenvironment vrframebuffer vrgui vrinputmanager vrinput vrmetamenu vrsession vrshadow vrtypes vrview vrviewer vranimation vrcamera vrenvironment vrframebuffer vrgui vrinputmanager vrinput vrmetamenu vrsession vrshadow vrtypes vrview vrviewer vrvirtualkeyboard
) )
openmw_add_executable(openmw_vr openmw_add_executable(openmw_vr

@ -107,6 +107,7 @@ namespace MWBase
/// @note This method will block until the video finishes playing /// @note This method will block until the video finishes playing
/// (and will continually update the window while doing so) /// (and will continually update the window while doing so)
virtual void playVideo(const std::string& name, bool allowSkipping) = 0; virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
virtual bool isPlayingVideo(void) const = 0;
virtual void setNewGame(bool newgame) = 0; virtual void setNewGame(bool newgame) = 0;

@ -123,6 +123,7 @@
#include "../mwvr/vrmetamenu.hpp" #include "../mwvr/vrmetamenu.hpp"
#include "../mwvr/vrenvironment.hpp" #include "../mwvr/vrenvironment.hpp"
#include "../mwvr/vrgui.hpp" #include "../mwvr/vrgui.hpp"
#include "../mwvr/vrvirtualkeyboard.hpp"
#endif #endif
namespace MWGui namespace MWGui
@ -168,6 +169,8 @@ namespace MWGui
, mScreenFader(nullptr) , mScreenFader(nullptr)
, mDebugWindow(nullptr) , mDebugWindow(nullptr)
, mJailScreen(nullptr) , mJailScreen(nullptr)
, mVrMetaMenu(nullptr)
, mVirtualKeyboardManager(nullptr)
, mTranslationDataStorage (translationDataStorage) , mTranslationDataStorage (translationDataStorage)
, mCharGen(nullptr) , mCharGen(nullptr)
, mInputBlocker(nullptr) , mInputBlocker(nullptr)
@ -308,6 +311,14 @@ namespace MWGui
mDragAndDrop = new DragAndDrop(); mDragAndDrop = new DragAndDrop();
#ifdef USE_OPENXR
mVrMetaMenu = new MWVR::VrMetaMenu(w, h);
mWindows.push_back(mVrMetaMenu);
mGuiModeStates[GM_VrMetaMenu] = GuiModeState(mVrMetaMenu);
mVirtualKeyboardManager = new MWVR::VirtualKeyboardManager;
#endif
Recharge* recharge = new Recharge(); Recharge* recharge = new Recharge();
mGuiModeStates[GM_Recharge] = GuiModeState(recharge); mGuiModeStates[GM_Recharge] = GuiModeState(recharge);
mWindows.push_back(recharge); mWindows.push_back(recharge);
@ -448,12 +459,6 @@ namespace MWGui
mWindows.push_back(mJailScreen); mWindows.push_back(mJailScreen);
mGuiModeStates[GM_Jail] = GuiModeState(mJailScreen); mGuiModeStates[GM_Jail] = GuiModeState(mJailScreen);
#ifdef USE_OPENXR
mVrMetaMenu = new MWVR::VrMetaMenu(w, h);
mWindows.push_back(mVrMetaMenu);
mGuiModeStates[GM_VrMetaMenu] = GuiModeState(mVrMetaMenu);
#endif
std::string werewolfFaderTex = "textures\\werewolfoverlay.dds"; std::string werewolfFaderTex = "textures\\werewolfoverlay.dds";
if (mResourceSystem->getVFS()->exists(werewolfFaderTex)) if (mResourceSystem->getVFS()->exists(werewolfFaderTex))
{ {
@ -1835,6 +1840,11 @@ namespace MWGui
mVideoEnabled = false; mVideoEnabled = false;
} }
bool WindowManager::isPlayingVideo(void) const
{
return mVideoEnabled;
}
void WindowManager::sizeVideo(int screenWidth, int screenHeight) void WindowManager::sizeVideo(int screenWidth, int screenHeight)
{ {
// Use black bars to correct aspect ratio // Use black bars to correct aspect ratio

@ -81,6 +81,7 @@ namespace osgMyGUI
namespace Gui namespace Gui
{ {
class FontLoader; class FontLoader;
class VirtualKeyboardManager;
} }
namespace MWRender namespace MWRender
@ -153,6 +154,7 @@ namespace MWGui
/// @note This method will block until the video finishes playing /// @note This method will block until the video finishes playing
/// (and will continually update the window while doing so) /// (and will continually update the window while doing so)
void playVideo(const std::string& name, bool allowSkipping) override; void playVideo(const std::string& name, bool allowSkipping) override;
bool isPlayingVideo(void) const override;
/// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this.
void setKeyFocusWidget (MyGUI::Widget* widget) override; void setKeyFocusWidget (MyGUI::Widget* widget) override;
@ -447,6 +449,8 @@ namespace MWGui
JailScreen* mJailScreen; JailScreen* mJailScreen;
MWVR::VrMetaMenu* mVrMetaMenu; MWVR::VrMetaMenu* mVrMetaMenu;
Gui::VirtualKeyboardManager* mVirtualKeyboardManager;
std::vector<WindowBase*> mWindows; std::vector<WindowBase*> mWindows;
Translation::Storage& mTranslationDataStorage; Translation::Storage& mTranslationDataStorage;

@ -480,6 +480,22 @@ namespace MWVR
LayerConfig gMessageBoxConfig = createDefaultConfig(6, false, SizingMode::Auto);; LayerConfig gMessageBoxConfig = createDefaultConfig(6, false, SizingMode::Auto);;
LayerConfig gNotificationConfig = createDefaultConfig(7, false, SizingMode::Fixed); LayerConfig gNotificationConfig = createDefaultConfig(7, false, SizingMode::Fixed);
//LayerConfig gVirtualKeyboardConfig = createDefaultConfig(50);
LayerConfig gVirtualKeyboardConfig = LayerConfig{
10,
false,
osg::Vec4{0.f,0.f,0.f,.75f},
osg::Vec3(0.025f,.025f,.066f), // offset (meters)
osg::Vec2(0.f,0.5f), // center (model space)
osg::Vec2(.25f, .25f), // extent (meters)
2048, // Spatial resolution (pixels per meter)
osg::Vec2i(2048,2048), // Texture resolution
osg::Vec2(1,1),
SizingMode::Auto,
TrackingMode::HudLeftHand,
""
};
static const float sSideBySideRadius = 1.f; static const float sSideBySideRadius = 1.f;
static const float sSideBySideAzimuthInterval = -osg::PI_4; static const float sSideBySideAzimuthInterval = -osg::PI_4;
static const LayerConfig createSideBySideConfig(int priority) static const LayerConfig createSideBySideConfig(int priority)
@ -559,6 +575,7 @@ namespace MWVR
{"InputBlocker", gVideoPlayerConfig}, {"InputBlocker", gVideoPlayerConfig},
{"Menu", gVideoPlayerConfig}, {"Menu", gVideoPlayerConfig},
{"LoadingScreen", gLoadingScreenConfig}, {"LoadingScreen", gLoadingScreenConfig},
{"VirtualKeyboard", gVirtualKeyboardConfig},
}; };
static std::set<std::string> layerBlacklist = static std::set<std::string> layerBlacklist =
@ -806,6 +823,30 @@ namespace MWVR
} }
} }
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)
mFocusWidget->_riseMouseLostFocus(widget);
if (widget)
widget->_riseMouseSetFocus(mFocusWidget);
mFocusWidget = widget;
}
bool VRGUIManager::injectMouseClick(bool onPress)
{
// TODO: This relies on MyGUI internal functions and may break on any future version.
if (mFocusWidget)
{
if(onPress)
mFocusWidget->_riseMouseButtonClick();
return true;
}
return false;
}
void VRGUIManager::computeGuiCursor(osg::Vec3 hitPoint) void VRGUIManager::computeGuiCursor(osg::Vec3 hitPoint)
{ {
float x = 0; float x = 0;
@ -830,6 +871,21 @@ namespace MWVR
MyGUI::InputManager::getInstance().injectMouseMove((int)x, (int)y, 0); MyGUI::InputManager::getInstance().injectMouseMove((int)x, (int)y, 0);
MWBase::Environment::get().getWindowManager()->setCursorActive(true); 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);
} }
} }

@ -159,12 +159,16 @@ namespace MWVR
/// Gui cursor coordinates to use to simulate a mouse press/move if the player is currently pointing at a vr gui layer /// Gui cursor coordinates to use to simulate a mouse press/move if the player is currently pointing at a vr gui layer
osg::Vec2i guiCursor() { return mGuiCursor; }; osg::Vec2i guiCursor() { return mGuiCursor; };
/// Inject mouse click if applicable
bool injectMouseClick(bool onPress);
private: private:
void computeGuiCursor(osg::Vec3 hitPoint); void computeGuiCursor(osg::Vec3 hitPoint);
void updateSideBySideLayers(); void updateSideBySideLayers();
void insertWidget(MWGui::Layout* widget); void insertWidget(MWGui::Layout* widget);
void removeWidget(MWGui::Layout* widget); void removeWidget(MWGui::Layout* widget);
void setFocusLayer(VRGUILayer* layer); void setFocusLayer(VRGUILayer* layer);
void setFocusWidget(MyGUI::Widget* widget);
osg::ref_ptr<osgViewer::Viewer> mOsgViewer{ nullptr }; osg::ref_ptr<osgViewer::Viewer> mOsgViewer{ nullptr };
@ -178,6 +182,7 @@ namespace MWVR
Pose mHeadPose{}; Pose mHeadPose{};
osg::Vec2i mGuiCursor{}; osg::Vec2i mGuiCursor{};
VRGUILayer* mFocusLayer{ nullptr }; VRGUILayer* mFocusLayer{ nullptr };
MyGUI::Widget* mFocusWidget{ nullptr };
osg::observer_ptr<osg::Camera> mCamera{ nullptr }; osg::observer_ptr<osg::Camera> mCamera{ nullptr };
}; };
} }

@ -141,6 +141,9 @@ namespace MWVR
void VRInputManager::injectMousePress(int sdlButton, bool onPress) void VRInputManager::injectMousePress(int sdlButton, bool onPress)
{ {
if (Environment::get().getGUIManager()->injectMouseClick(onPress))
return;
SDL_MouseButtonEvent arg; SDL_MouseButtonEvent arg;
if (onPress) if (onPress)
mMouseManager->mousePressed(arg, sdlButton); mMouseManager->mousePressed(arg, sdlButton);
@ -212,19 +215,15 @@ namespace MWVR
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"}, {MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"}, {MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"}, {MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
//{MWInput::A_ToggleHUD, "/user/hand/left/input/thumbstick/click"},
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"}, {MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
{MWInput::A_ToggleSpell, "/user/hand/left/input/x/click"}, {MWInput::A_ToggleSpell, "/user/hand/left/input/x/click"},
//{MWInput::A_QuickSave, "/user/hand/left/input/y/click"},
{MWInput::A_Rest, "/user/hand/left/input/y/click"}, {MWInput::A_Rest, "/user/hand/left/input/y/click"},
{MWInput::A_ToggleWeapon, "/user/hand/right/input/a/click"}, {MWInput::A_ToggleWeapon, "/user/hand/right/input/a/click"},
{MWInput::A_Inventory, "/user/hand/right/input/b/click"}, {MWInput::A_Inventory, "/user/hand/right/input/b/click"},
//{MWInput::A_Journal, "/user/hand/right/input/b/click"},
{A_ActivateTouch, "/user/hand/right/input/squeeze/value"}, {A_ActivateTouch, "/user/hand/right/input/squeeze/value"},
{MWInput::A_Activate, "/user/hand/right/input/squeeze/value"}, {MWInput::A_Activate, "/user/hand/right/input/squeeze/value"},
{MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"}, {MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"},
{MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"}, {MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"},
//{MWInput::A_ToggleDebug, "/user/hand/right/input/thumbstick/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/value"}, {MWInput::A_Use, "/user/hand/right/input/trigger/value"},
}; };
@ -255,17 +254,13 @@ namespace MWVR
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"}, {MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"}, {MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"}, {MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
//{MWInput::A_ToggleHUD, "/user/hand/left/input/thumbstick/click"},
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"}, {MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
{MWInput::A_ToggleSpell, "/user/hand/left/input/x/click"}, {MWInput::A_ToggleSpell, "/user/hand/left/input/x/click"},
//{MWInput::A_QuickSave, "/user/hand/left/input/y/click"},
{MWInput::A_Rest, "/user/hand/left/input/y/click"}, {MWInput::A_Rest, "/user/hand/left/input/y/click"},
{MWInput::A_ToggleWeapon, "/user/hand/right/input/a/click"}, {MWInput::A_ToggleWeapon, "/user/hand/right/input/a/click"},
{MWInput::A_Inventory, "/user/hand/right/input/b/click"}, {MWInput::A_Inventory, "/user/hand/right/input/b/click"},
//{MWInput::A_Journal, "/user/hand/right/input/b/click"},
{MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"}, {MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"},
{MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"}, {MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"},
//{MWInput::A_ToggleDebug, "/user/hand/right/input/thumbstick/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/value"}, {MWInput::A_Use, "/user/hand/right/input/trigger/value"},
{A_ActivateTouch, "/user/hand/right/input/squeeze/value"}, {A_ActivateTouch, "/user/hand/right/input/squeeze/value"},
{MWInput::A_Activate, "/user/hand/right/input/squeeze/value"}, {MWInput::A_Activate, "/user/hand/right/input/squeeze/value"},
@ -294,21 +289,15 @@ namespace MWVR
// In-game character controls // In-game character controls
SuggestedBindings gameplayBindings{ SuggestedBindings gameplayBindings{
//{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
//{MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"},
//{MWInput::A_ToggleDebug, "/user/hand/right/input/thumbstick/click"},
//{MWInput::A_ToggleHUD, "/user/hand/left/input/thumbstick/click"},
{A_Recenter, "/user/hand/left/input/menu/click"}, {A_Recenter, "/user/hand/left/input/menu/click"},
{A_VrMetaMenu, "/user/hand/left/input/menu/click"}, {A_VrMetaMenu, "/user/hand/left/input/menu/click"},
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"}, {MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"}, {MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"}, {MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
//{MWInput::A_QuickSave, "/user/hand/left/input/thumbstick/click"},
{MWInput::A_Rest, "/user/hand/left/input/thumbstick/click"}, {MWInput::A_Rest, "/user/hand/left/input/thumbstick/click"},
{MWInput::A_ToggleSpell, "/user/hand/left/input/trackpad/click"}, {MWInput::A_ToggleSpell, "/user/hand/left/input/trackpad/click"},
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/click"}, {MWInput::A_Sneak, "/user/hand/left/input/squeeze/click"},
{MWInput::A_Inventory, "/user/hand/right/input/thumbstick/click"}, {MWInput::A_Inventory, "/user/hand/right/input/thumbstick/click"},
//{MWInput::A_Journal, "/user/hand/right/input/thumbstick/click"},
{MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"}, {MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"},
{MWInput::A_ToggleWeapon, "/user/hand/right/input/trackpad/click"}, {MWInput::A_ToggleWeapon, "/user/hand/right/input/trackpad/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/value"}, {MWInput::A_Use, "/user/hand/right/input/trigger/value"},
@ -336,23 +325,17 @@ namespace MWVR
std::string controllerProfilePath = "/interaction_profiles/valve/index_controller"; std::string controllerProfilePath = "/interaction_profiles/valve/index_controller";
// In-game character controls // In-game character controls
SuggestedBindings gameplayBindings{ SuggestedBindings gameplayBindings{
//{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
{MWInput::A_ToggleSpell, "/user/hand/left/input/a/click"}, {MWInput::A_ToggleSpell, "/user/hand/left/input/a/click"},
{MWInput::A_Rest, "/user/hand/left/input/b/click"}, {MWInput::A_Rest, "/user/hand/left/input/b/click"},
//{MWInput::A_QuickSave, "/user/hand/left/input/b/click"},
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"}, {MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"}, {MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
//{MWInput::A_ToggleHUD, "/user/hand/left/input/thumbstick/click"},
{A_Recenter, "/user/hand/left/input/trackpad/force"}, {A_Recenter, "/user/hand/left/input/trackpad/force"},
{A_VrMetaMenu, "/user/hand/left/input/trackpad/force"}, {A_VrMetaMenu, "/user/hand/left/input/trackpad/force"},
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"}, {MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/force"}, {MWInput::A_Sneak, "/user/hand/left/input/squeeze/force"},
{MWInput::A_ToggleWeapon, "/user/hand/right/input/a/click"}, {MWInput::A_ToggleWeapon, "/user/hand/right/input/a/click"},
{MWInput::A_Inventory, "/user/hand/right/input/b/click"}, {MWInput::A_Inventory, "/user/hand/right/input/b/click"},
//{MWInput::A_Journal, "/user/hand/right/input/b/click"},
//{MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"},
{MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"}, {MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"},
//{MWInput::A_ToggleDebug, "/user/hand/right/input/thumbstick/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/value"}, {MWInput::A_Use, "/user/hand/right/input/trigger/value"},
{A_ActivateTouch, "/user/hand/right/input/squeeze/force"}, {A_ActivateTouch, "/user/hand/right/input/squeeze/force"},
{MWInput::A_Activate, "/user/hand/right/input/squeeze/force"}, {MWInput::A_Activate, "/user/hand/right/input/squeeze/force"},
@ -377,18 +360,8 @@ namespace MWVR
{ {
std::string controllerProfilePath = "/interaction_profiles/htc/vive_controller"; std::string controllerProfilePath = "/interaction_profiles/htc/vive_controller";
// TODO: I Didn't realize the vive wands were so bad. We don't have NEARLY enough actions available.
// In-game character controls // In-game character controls
SuggestedBindings gameplayBindings{ SuggestedBindings gameplayBindings{
//{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
//{MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"},
//{MWInput::A_Inventory, "/user/hand/right/input/b/click"},
//{MWInput::A_Journal, "/user/hand/right/input/b/click"},
//{MWInput::A_QuickSave, "/user/hand/left/input/b/click"},
//{MWInput::A_Rest, "/user/hand/left/input/b/click"},
//{MWInput::A_ToggleDebug, "/user/hand/right/input/thumbstick/click"},
//{MWInput::A_ToggleHUD, "/user/hand/left/input/thumbstick/click"},
{A_Recenter, "/user/hand/left/input/menu/click"}, {A_Recenter, "/user/hand/left/input/menu/click"},
{A_VrMetaMenu, "/user/hand/left/input/menu/click"}, {A_VrMetaMenu, "/user/hand/left/input/menu/click"},
{A_VrMetaMenu, "/user/hand/right/input/squeeze/click"}, {A_VrMetaMenu, "/user/hand/right/input/squeeze/click"},
@ -556,19 +529,23 @@ namespace MWVR
{ {
static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input");
auto* vrGuiManager = Environment::get().getGUIManager(); auto* vrGuiManager = Environment::get().getGUIManager();
auto* wm = MWBase::Environment::get().getWindowManager();
// OpenMW does not currently provide any way to directly request skipping a video. // OpenMW does not currently provide any way to directly request skipping a video.
// This is copied from the controller manager and is used to skip videos, // This is copied from the controller manager and is used to skip videos,
// and works because mygui only consumes the escape press if a video is currently playing. // and works because mygui only consumes the escape press if a video is currently playing.
auto kc = MWInput::sdlKeyToMyGUI(SDLK_ESCAPE); if (wm->isPlayingVideo())
if (action->onActivate())
{
mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyPress(kc, 0));
}
else if (action->onDeactivate())
{ {
mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(kc)); auto kc = MWInput::sdlKeyToMyGUI(SDLK_ESCAPE);
if (action->onActivate())
{
mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyPress(kc, 0));
}
else if (action->onDeactivate())
{
mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(kc));
}
} }
if (disableControls) if (disableControls)
@ -623,14 +600,14 @@ namespace MWVR
vrGuiManager->updateTracking(); vrGuiManager->updateTracking();
break; break;
case A_MenuSelect: case A_MenuSelect:
if (!MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Space, 0, 0)) if (!wm->injectKeyPress(MyGUI::KeyCode::Return, 0, false))
executeAction(MWInput::A_Activate); executeAction(MWInput::A_Activate);
break; break;
case A_MenuBack: case A_MenuBack:
if (MyGUI::InputManager::getInstance().isModalAny()) if (MyGUI::InputManager::getInstance().isModalAny())
MWBase::Environment::get().getWindowManager()->exitCurrentModal(); wm->exitCurrentModal();
else else
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); wm->exitCurrentGuiMode();
break; break;
case MWInput::A_Use: case MWInput::A_Use:
pointActivation(true); pointActivation(true);

@ -0,0 +1,273 @@
#include "vrvirtualkeyboard.hpp"
#include <MyGUI_InputManager.h>
#include <MyGUI_LayerManager.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/statemanager.hpp"
namespace MWVR
{
VirtualKeyboardManager::VirtualKeyboardManager()
: mVk(new VrVirtualKeyboard)
{
}
void VirtualKeyboardManager::registerEditBox(MyGUI::EditBox* editBox)
{
IDelegate* onSetFocusDelegate = newDelegate(mVk.get(), &VrVirtualKeyboard::delegateOnSetFocus);
IDelegate* onLostFocusDelegate = newDelegate(mVk.get(), &VrVirtualKeyboard::delegateOnLostFocus);
editBox->eventKeySetFocus += onSetFocusDelegate;
editBox->eventKeyLostFocus += onLostFocusDelegate;
mDelegates[editBox] = Delegates(onSetFocusDelegate, onLostFocusDelegate);
}
void VirtualKeyboardManager::unregisterEditBox(MyGUI::EditBox* editBox)
{
auto it = mDelegates.find(editBox);
if (it != mDelegates.end())
{
editBox->eventKeySetFocus -= it->second.first;
editBox->eventKeyLostFocus -= it->second.second;
mDelegates.erase(it);
}
}
static const char* mClassTypeName;
VrVirtualKeyboard::VrVirtualKeyboard()
: WindowBase("openmw_vr_virtual_keyboard.layout")
, mButtonBox(nullptr)
, mTarget(nullptr)
, mButtons()
, mShift(false)
, mCaps(false)
{
getWidget(mButtonBox, "ButtonBox");
mMainWidget->setNeedKeyFocus(false);
mButtonBox->setNeedKeyFocus(false);
updateMenu();
}
VrVirtualKeyboard::~VrVirtualKeyboard()
{
}
void VrVirtualKeyboard::onResChange(int w, int h)
{
updateMenu();
}
void VrVirtualKeyboard::onFrame(float dt)
{
}
void VrVirtualKeyboard::open(MyGUI::EditBox* target)
{
updateMenu();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(target);
mTarget = target;
setVisible(true);
}
void VrVirtualKeyboard::close()
{
setVisible(false);
mTarget = nullptr;
}
void VrVirtualKeyboard::delegateOnSetFocus(MyGUI::Widget* _sender, MyGUI::Widget* _old)
{
open(static_cast<MyGUI::EditBox*>(_sender));
}
void VrVirtualKeyboard::delegateOnLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new)
{
close();
}
void VrVirtualKeyboard::onButtonClicked(MyGUI::Widget* sender)
{
assert(mTarget);
MyGUI::InputManager::getInstance().setKeyFocusWidget(mTarget);
std::string name = *sender->getUserData<std::string>();
if (name == "Esc")
onEsc();
if (name == "Tab")
onTab();
if (name == "Caps")
onCaps();
if (name == "Shift")
onShift();
else
mShift = false;
if (name == "Back")
onBackspace();
if (name == "Return")
onReturn();
if (name == "Space")
textInput(" ");
if (name == "->")
textInput("->");
if (name.length() == 1)
textInput(name);
updateMenu();
}
void VrVirtualKeyboard::textInput(const std::string& symbol)
{
MyGUI::UString ustring(symbol);
MyGUI::UString::utf32string utf32string = ustring.asUTF32();
for (MyGUI::UString::utf32string::const_iterator it = utf32string.begin(); it != utf32string.end(); ++it)
MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::None, *it);
}
void VrVirtualKeyboard::onEsc()
{
close();
}
void VrVirtualKeyboard::onTab()
{
MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Tab);
MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Tab);
}
void VrVirtualKeyboard::onCaps()
{
mCaps = !mCaps;
}
void VrVirtualKeyboard::onShift()
{
mShift = !mShift;
}
void VrVirtualKeyboard::onBackspace()
{
MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Backspace);
MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Backspace);
}
void VrVirtualKeyboard::onReturn()
{
MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Return);
MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Return);
}
bool VrVirtualKeyboard::exit()
{
close();
return true;
}
void VrVirtualKeyboard::updateMenu()
{
// TODO: Localization?
static std::vector<std::string> row1{ "`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "Back" };
static std::vector<std::string> row2{ "Tab", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "Return" };
static std::vector<std::string> row3{ "Caps", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "\\", "->" };
static std::vector<std::string> row4{ "Shift", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Space" };
std::map<std::string, std::string> shiftMap;
shiftMap["1"] = "!";
shiftMap["2"] = "@";
shiftMap["3"] = "#";
shiftMap["4"] = "$";
shiftMap["5"] = "%";
shiftMap["6"] = "^";
shiftMap["7"] = "&";
shiftMap["8"] = "*";
shiftMap["9"] = "(";
shiftMap["0"] = ")";
shiftMap["-"] = "_";
shiftMap["="] = "+";
shiftMap["\\"] = "|";
shiftMap[","] = "<";
shiftMap["."] = ">";
shiftMap["/"] = "?";
shiftMap[";"] = ":";
shiftMap["'"] = "\"";
shiftMap["["] = "{";
shiftMap["]"] = "}";
shiftMap["`"] = "~";
std::vector< std::vector< std::string > > rows{ row1, row2, row3, row4 };
int sideSize = 50;
int margin = 10;
int xmax = 0;
int ymax = 0;
if (mButtons.empty())
{
int y = margin;
for (auto& row : rows)
{
int x = margin;
for (std::string& buttonId : row)
{
int width = sideSize + 10 * (buttonId.length() - 1);
MyGUI::Button* button = mButtonBox->createWidget<MyGUI::Button>(
"MW_Button", MyGUI::IntCoord(x, y, width, sideSize), MyGUI::Align::Default, buttonId);
button->eventMouseButtonClick += MyGUI::newDelegate(this, &VrVirtualKeyboard::onButtonClicked);
button->setUserData(std::string(buttonId));
button->setVisible(true);
button->setFontHeight(32);
button->setCaption(buttonId);
button->setNeedKeyFocus(false);
mButtons[buttonId] = button;
x += width + margin;
}
y += sideSize + margin;
}
}
for (auto& row : rows)
{
for (std::string& buttonId : row)
{
auto* button = mButtons[buttonId];
xmax = std::max(xmax, button->getAbsoluteRect().right);
ymax = std::max(ymax, button->getAbsoluteRect().bottom);
if (buttonId.length() == 1)
{
auto caption = buttonId;
if (mShift ^ mCaps)
caption[0] = std::toupper(caption[0]);
else
caption[0] = std::tolower(caption[0]);
button->setCaption(caption);
button->setUserData(caption);
}
if (mShift)
{
auto it = shiftMap.find(buttonId);
if (it != shiftMap.end())
{
button->setCaption(it->second);
button->setUserData(it->second);
}
}
}
}
std::cout << xmax << ", " << ymax << std::endl;
setCoord(0, 0, xmax + margin, ymax + margin);
mButtonBox->setCoord(0, 0, xmax + margin, ymax + margin);
//mButtonBox->setCoord (margin, margin, width, height);
mButtonBox->setVisible(true);
}
}

@ -0,0 +1,79 @@
#ifndef OPENMW_GAME_MWVR_VRVIRTUALKEYBOARD_H
#define OPENMW_GAME_MWVR_VRVIRTUALKEYBOARD_H
#include "../mwgui/windowbase.hpp"
#include <MyGUI_Button.h>
#include "components/widgets/virtualkeyboardmanager.hpp"
#include <map>
namespace Gui
{
class VirtualKeyboardManager;
}
namespace MWVR
{
class VrVirtualKeyboard : public MWGui::WindowBase
{
public:
VrVirtualKeyboard();
~VrVirtualKeyboard();
void onResChange(int w, int h) override;
void onFrame(float dt) override;
bool exit() override;
void open(MyGUI::EditBox* target);
void close();
void delegateOnSetFocus(MyGUI::Widget* _sender, MyGUI::Widget* _old);
void delegateOnLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _old);
private:
void onButtonClicked(MyGUI::Widget* sender);
void textInput(const std::string& symbol);
void onEsc();
void onTab();
void onCaps();
void onShift();
void onBackspace();
void onReturn();
void updateMenu();
MyGUI::Widget* mButtonBox;
MyGUI::EditBox* mTarget;
std::map<std::string, MyGUI::Button*> mButtons;
bool mShift;
bool mCaps;
};
class VirtualKeyboardManager : public Gui::VirtualKeyboardManager
{
public:
VirtualKeyboardManager();
void registerEditBox(MyGUI::EditBox* editBox) override;
void unregisterEditBox(MyGUI::EditBox* editBox) override;
VrVirtualKeyboard& virtualKeyboard() { return *mVk; };
private:
std::unique_ptr<VrVirtualKeyboard> mVk;
// MyGUI deletes delegates when you remove them from an event.
// Therefore i need one pair of delegates per box instead of being able to reuse one pair.
// And i have to set them aside myself to know what to remove from each event.
// There is an IDelegateUnlink type that might simplify this, but it is poorly documented.
using IDelegate = MyGUI::EventHandle_WidgetWidget::IDelegate;
// .first = onSetFocus, .second = onLostFocus
using Delegates = std::pair<IDelegate*, IDelegate*>;
std::map<MyGUI::EditBox*, Delegates> mDelegates;
};
}
#endif

@ -3966,7 +3966,7 @@ namespace MWWorld
if (windowManager->isGuiMode() && windowManager->isConsoleMode()) if (windowManager->isGuiMode() && windowManager->isConsoleMode())
{ {
return getTargetObject(result, pointer, getMaxActivationDistance() * 50, false); return getTargetObject(result, pointer, getMaxActivationDistance() * 50, true);
} }
else else
{ {

@ -131,7 +131,7 @@ add_component_dir (myguiplatform
) )
add_component_dir (widgets add_component_dir (widgets
box fontwrapper imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets box fontwrapper imagebutton tags list numericeditbox sharedstatebutton virtualkeyboardmanager windowcaption widgets
) )
add_component_dir (fontloader add_component_dir (fontloader

@ -1,4 +1,5 @@
#include "box.hpp" #include "box.hpp"
#include "virtualkeyboardmanager.hpp"
#include <MyGUI_EditText.h> #include <MyGUI_EditText.h>
@ -486,4 +487,36 @@ namespace Gui
setUserString("VStretch", "true"); setUserString("VStretch", "true");
} }
EditBox::EditBox(bool shouldSupportVirtualKeyboard)
: mVirtualKeyboardRegistered(false)
{
if (shouldSupportVirtualKeyboard)
registerVirtualKeyboard();
}
EditBox::~EditBox()
{
unregisterVirtualKeyboard();
}
void EditBox::registerVirtualKeyboard()
{
if (!mVirtualKeyboardRegistered)
{
auto* vkm = Gui::VirtualKeyboardManager::getInstancePtr();
if (vkm)
{
vkm->registerEditBox(this);
mVirtualKeyboardRegistered = true;
}
}
}
void EditBox::unregisterVirtualKeyboard()
{
if (mVirtualKeyboardRegistered)
{
// No need to check here
Gui::VirtualKeyboardManager::getInstance().unregisterEditBox(this);
mVirtualKeyboardRegistered = false;
}
}
} }

@ -23,7 +23,17 @@ namespace Gui
class EditBox : public FontWrapper<MyGUI::EditBox> class EditBox : public FontWrapper<MyGUI::EditBox>
{ {
MYGUI_RTTI_DERIVED( EditBox ) MYGUI_RTTI_DERIVED( EditBox );
/// @param supportsVirtualKeyboard If true, VR mode will spawn a virtual keyboard whenever this widget is focused.
EditBox(bool shouldSupportVirtualKeyboard = true);
~EditBox();
private:
void registerVirtualKeyboard();
void unregisterVirtualKeyboard();
bool mVirtualKeyboardRegistered;
}; };
class AutoSizedWidget class AutoSizedWidget

@ -3,7 +3,7 @@
#include <MyGUI_EditBox.h> #include <MyGUI_EditBox.h>
#include "fontwrapper.hpp" #include "box.hpp"
namespace Gui namespace Gui
{ {
@ -11,7 +11,7 @@ namespace Gui
/** /**
* @brief A variant of the EditBox that only allows integer inputs * @brief A variant of the EditBox that only allows integer inputs
*/ */
class NumericEditBox final : public FontWrapper<MyGUI::EditBox> class NumericEditBox final : public Gui::EditBox
{ {
MYGUI_RTTI_DERIVED(NumericEditBox) MYGUI_RTTI_DERIVED(NumericEditBox)

@ -0,0 +1,4 @@
#include "virtualkeyboardmanager.hpp"
Gui::VirtualKeyboardManager* MyGUI::Singleton<Gui::VirtualKeyboardManager>::msInstance = nullptr;
const char* MyGUI::Singleton<Gui::VirtualKeyboardManager>::mClassTypeName = "Gui::VirtualKeyboardManager";

@ -0,0 +1,18 @@
#ifndef OPENMW_WIDGETS_VIRTUALKEYBOARDMANAGER_H
#define OPENMW_WIDGETS_VIRTUALKEYBOARDMANAGER_H
#include <MyGUI_EditBox.h>
#include "MyGUI_Singleton.h"
namespace Gui
{
class VirtualKeyboardManager :
public MyGUI::Singleton<VirtualKeyboardManager>
{
public:
virtual void registerEditBox(MyGUI::EditBox* editBox) = 0;
virtual void unregisterEditBox(MyGUI::EditBox* editBox) = 0;
};
}
#endif

@ -98,6 +98,7 @@ set(MYGUI_FILES
openmw_trade_window_vr.layout openmw_trade_window_vr.layout
openmw_trainingwindow.layout openmw_trainingwindow.layout
openmw_travel_window.layout openmw_travel_window.layout
openmw_vr_virtual_keyboard.layout
openmw_wait_dialog.layout openmw_wait_dialog.layout
openmw_wait_dialog_progressbar.layout openmw_wait_dialog_progressbar.layout
openmw_windows.skin.xml openmw_windows.skin.xml

@ -27,5 +27,6 @@
<Layer name="MessageBox" overlapped="false" pick="true"/> <Layer name="MessageBox" overlapped="false" pick="true"/>
<Layer name="InputBlocker" overlapped="false" pick="true"/> <Layer name="InputBlocker" overlapped="false" pick="true"/>
<Layer name="VideoPlayer" overlapped="false" pick="true"/> <Layer name="VideoPlayer" overlapped="false" pick="true"/>
<Layer name="VirtualKeyboard" overlapped="false" pick="true"/>
<Layer name="Pointer" overlapped="false" pick="false"/> <Layer name="Pointer" overlapped="false" pick="false"/>
</MyGUI> </MyGUI>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layout">
<Widget type="Widget" layer="VirtualKeyboard" position="0 0 900 500" name="_Main" align="Center">
<Widget type="Widget" position="25 25 850 400" name="ButtonBox">
</Widget>
</Widget>
</MyGUI>
Loading…
Cancel
Save