mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-06 17:15:35 +00:00
QWERTY implementation of a virtual keyboard.
This commit is contained in:
parent
3158a2510e
commit
3e581571f4
19 changed files with 532 additions and 52 deletions
|
@ -249,7 +249,7 @@ if(BUILD_OPENMW_VR)
|
|||
add_openmw_dir (mwvr
|
||||
openxraction openxractionset openxrdebug openxrinput openxrmanager openxrmanagerimpl openxrswapchain openxrswapchainimpl
|
||||
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
|
||||
|
|
|
@ -107,6 +107,7 @@ namespace MWBase
|
|||
/// @note This method will block until the video finishes playing
|
||||
/// (and will continually update the window while doing so)
|
||||
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
|
||||
virtual bool isPlayingVideo(void) const = 0;
|
||||
|
||||
virtual void setNewGame(bool newgame) = 0;
|
||||
|
||||
|
|
|
@ -123,6 +123,7 @@
|
|||
#include "../mwvr/vrmetamenu.hpp"
|
||||
#include "../mwvr/vrenvironment.hpp"
|
||||
#include "../mwvr/vrgui.hpp"
|
||||
#include "../mwvr/vrvirtualkeyboard.hpp"
|
||||
#endif
|
||||
|
||||
namespace MWGui
|
||||
|
@ -168,6 +169,8 @@ namespace MWGui
|
|||
, mScreenFader(nullptr)
|
||||
, mDebugWindow(nullptr)
|
||||
, mJailScreen(nullptr)
|
||||
, mVrMetaMenu(nullptr)
|
||||
, mVirtualKeyboardManager(nullptr)
|
||||
, mTranslationDataStorage (translationDataStorage)
|
||||
, mCharGen(nullptr)
|
||||
, mInputBlocker(nullptr)
|
||||
|
@ -308,6 +311,14 @@ namespace MWGui
|
|||
|
||||
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();
|
||||
mGuiModeStates[GM_Recharge] = GuiModeState(recharge);
|
||||
mWindows.push_back(recharge);
|
||||
|
@ -448,12 +459,6 @@ namespace MWGui
|
|||
mWindows.push_back(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";
|
||||
if (mResourceSystem->getVFS()->exists(werewolfFaderTex))
|
||||
{
|
||||
|
@ -1835,6 +1840,11 @@ namespace MWGui
|
|||
mVideoEnabled = false;
|
||||
}
|
||||
|
||||
bool WindowManager::isPlayingVideo(void) const
|
||||
{
|
||||
return mVideoEnabled;
|
||||
}
|
||||
|
||||
void WindowManager::sizeVideo(int screenWidth, int screenHeight)
|
||||
{
|
||||
// Use black bars to correct aspect ratio
|
||||
|
|
|
@ -81,6 +81,7 @@ namespace osgMyGUI
|
|||
namespace Gui
|
||||
{
|
||||
class FontLoader;
|
||||
class VirtualKeyboardManager;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
|
@ -153,6 +154,7 @@ namespace MWGui
|
|||
/// @note This method will block until the video finishes playing
|
||||
/// (and will continually update the window while doing so)
|
||||
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.
|
||||
void setKeyFocusWidget (MyGUI::Widget* widget) override;
|
||||
|
@ -447,6 +449,8 @@ namespace MWGui
|
|||
JailScreen* mJailScreen;
|
||||
MWVR::VrMetaMenu* mVrMetaMenu;
|
||||
|
||||
Gui::VirtualKeyboardManager* mVirtualKeyboardManager;
|
||||
|
||||
std::vector<WindowBase*> mWindows;
|
||||
|
||||
Translation::Storage& mTranslationDataStorage;
|
||||
|
|
|
@ -480,6 +480,22 @@ namespace MWVR
|
|||
LayerConfig gMessageBoxConfig = createDefaultConfig(6, false, SizingMode::Auto);;
|
||||
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 sSideBySideAzimuthInterval = -osg::PI_4;
|
||||
static const LayerConfig createSideBySideConfig(int priority)
|
||||
|
@ -559,6 +575,7 @@ namespace MWVR
|
|||
{"InputBlocker", gVideoPlayerConfig},
|
||||
{"Menu", gVideoPlayerConfig},
|
||||
{"LoadingScreen", gLoadingScreenConfig},
|
||||
{"VirtualKeyboard", gVirtualKeyboardConfig},
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
float x = 0;
|
||||
|
@ -830,6 +871,21 @@ namespace MWVR
|
|||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
osg::Vec2i guiCursor() { return mGuiCursor; };
|
||||
|
||||
/// Inject mouse click if applicable
|
||||
bool injectMouseClick(bool onPress);
|
||||
|
||||
private:
|
||||
void computeGuiCursor(osg::Vec3 hitPoint);
|
||||
void updateSideBySideLayers();
|
||||
void insertWidget(MWGui::Layout* widget);
|
||||
void removeWidget(MWGui::Layout* widget);
|
||||
void setFocusLayer(VRGUILayer* layer);
|
||||
void setFocusWidget(MyGUI::Widget* widget);
|
||||
|
||||
osg::ref_ptr<osgViewer::Viewer> mOsgViewer{ nullptr };
|
||||
|
||||
|
@ -178,6 +182,7 @@ namespace MWVR
|
|||
Pose mHeadPose{};
|
||||
osg::Vec2i mGuiCursor{};
|
||||
VRGUILayer* mFocusLayer{ nullptr };
|
||||
MyGUI::Widget* mFocusWidget{ nullptr };
|
||||
osg::observer_ptr<osg::Camera> mCamera{ nullptr };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -141,6 +141,9 @@ namespace MWVR
|
|||
|
||||
void VRInputManager::injectMousePress(int sdlButton, bool onPress)
|
||||
{
|
||||
if (Environment::get().getGUIManager()->injectMouseClick(onPress))
|
||||
return;
|
||||
|
||||
SDL_MouseButtonEvent arg;
|
||||
if (onPress)
|
||||
mMouseManager->mousePressed(arg, sdlButton);
|
||||
|
@ -212,19 +215,15 @@ namespace MWVR
|
|||
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
|
||||
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
|
||||
{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_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_ToggleWeapon, "/user/hand/right/input/a/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"},
|
||||
{MWInput::A_Activate, "/user/hand/right/input/squeeze/value"},
|
||||
{MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"},
|
||||
{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"},
|
||||
};
|
||||
|
||||
|
@ -255,17 +254,13 @@ namespace MWVR
|
|||
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
|
||||
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
|
||||
{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_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_ToggleWeapon, "/user/hand/right/input/a/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_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"},
|
||||
{A_ActivateTouch, "/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
|
||||
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_VrMetaMenu, "/user/hand/left/input/menu/click"},
|
||||
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
|
||||
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
|
||||
{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_ToggleSpell, "/user/hand/left/input/trackpad/click"},
|
||||
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/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_ToggleWeapon, "/user/hand/right/input/trackpad/click"},
|
||||
{MWInput::A_Use, "/user/hand/right/input/trigger/value"},
|
||||
|
@ -336,23 +325,17 @@ namespace MWVR
|
|||
std::string controllerProfilePath = "/interaction_profiles/valve/index_controller";
|
||||
// In-game character controls
|
||||
SuggestedBindings gameplayBindings{
|
||||
//{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
|
||||
{MWInput::A_ToggleSpell, "/user/hand/left/input/a/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_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_VrMetaMenu, "/user/hand/left/input/trackpad/force"},
|
||||
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
|
||||
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/force"},
|
||||
{MWInput::A_ToggleWeapon, "/user/hand/right/input/a/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_ToggleDebug, "/user/hand/right/input/thumbstick/click"},
|
||||
{MWInput::A_Use, "/user/hand/right/input/trigger/value"},
|
||||
{A_ActivateTouch, "/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";
|
||||
|
||||
// TODO: I Didn't realize the vive wands were so bad. We don't have NEARLY enough actions available.
|
||||
|
||||
// In-game character controls
|
||||
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_VrMetaMenu, "/user/hand/left/input/menu/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");
|
||||
auto* vrGuiManager = Environment::get().getGUIManager();
|
||||
auto* wm = MWBase::Environment::get().getWindowManager();
|
||||
|
||||
|
||||
// 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,
|
||||
// and works because mygui only consumes the escape press if a video is currently playing.
|
||||
auto kc = MWInput::sdlKeyToMyGUI(SDLK_ESCAPE);
|
||||
if (action->onActivate())
|
||||
if (wm->isPlayingVideo())
|
||||
{
|
||||
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)
|
||||
|
@ -623,14 +600,14 @@ namespace MWVR
|
|||
vrGuiManager->updateTracking();
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
case A_MenuBack:
|
||||
if (MyGUI::InputManager::getInstance().isModalAny())
|
||||
MWBase::Environment::get().getWindowManager()->exitCurrentModal();
|
||||
wm->exitCurrentModal();
|
||||
else
|
||||
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
|
||||
wm->exitCurrentGuiMode();
|
||||
break;
|
||||
case MWInput::A_Use:
|
||||
pointActivation(true);
|
||||
|
|
273
apps/openmw/mwvr/vrvirtualkeyboard.cpp
Normal file
273
apps/openmw/mwvr/vrvirtualkeyboard.cpp
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
79
apps/openmw/mwvr/vrvirtualkeyboard.hpp
Normal file
79
apps/openmw/mwvr/vrvirtualkeyboard.hpp
Normal file
|
@ -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())
|
||||
{
|
||||
return getTargetObject(result, pointer, getMaxActivationDistance() * 50, false);
|
||||
return getTargetObject(result, pointer, getMaxActivationDistance() * 50, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -131,7 +131,7 @@ add_component_dir (myguiplatform
|
|||
)
|
||||
|
||||
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
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "box.hpp"
|
||||
#include "virtualkeyboardmanager.hpp"
|
||||
|
||||
#include <MyGUI_EditText.h>
|
||||
|
||||
|
@ -486,4 +487,36 @@ namespace Gui
|
|||
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>
|
||||
{
|
||||
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
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <MyGUI_EditBox.h>
|
||||
|
||||
#include "fontwrapper.hpp"
|
||||
#include "box.hpp"
|
||||
|
||||
namespace Gui
|
||||
{
|
||||
|
@ -11,7 +11,7 @@ namespace Gui
|
|||
/**
|
||||
* @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)
|
||||
|
||||
|
|
4
components/widgets/virtualkeyboardmanager.cpp
Normal file
4
components/widgets/virtualkeyboardmanager.cpp
Normal file
|
@ -0,0 +1,4 @@
|
|||
#include "virtualkeyboardmanager.hpp"
|
||||
|
||||
Gui::VirtualKeyboardManager* MyGUI::Singleton<Gui::VirtualKeyboardManager>::msInstance = nullptr;
|
||||
const char* MyGUI::Singleton<Gui::VirtualKeyboardManager>::mClassTypeName = "Gui::VirtualKeyboardManager";
|
18
components/widgets/virtualkeyboardmanager.hpp
Normal file
18
components/widgets/virtualkeyboardmanager.hpp
Normal file
|
@ -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_trainingwindow.layout
|
||||
openmw_travel_window.layout
|
||||
openmw_vr_virtual_keyboard.layout
|
||||
openmw_wait_dialog.layout
|
||||
openmw_wait_dialog_progressbar.layout
|
||||
openmw_windows.skin.xml
|
||||
|
|
|
@ -27,5 +27,6 @@
|
|||
<Layer name="MessageBox" overlapped="false" pick="true"/>
|
||||
<Layer name="InputBlocker" 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"/>
|
||||
</MyGUI>
|
8
files/mygui/openmw_vr_virtual_keyboard.layout
Normal file
8
files/mygui/openmw_vr_virtual_keyboard.layout
Normal file
|
@ -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…
Reference in a new issue