diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index edd0dead11..0bfd97c5dc 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -272,9 +272,7 @@ void OMW::Engine::go() // Get the path for the keybinder xml file std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); - - if (!boost::filesystem::exists(keybinderUser)) - keybinderUser = ""; + bool keybinderUserExists = boost::filesystem::exists(keybinderUser); mFpsLevel = settings.getInt("fps", "HUD"); @@ -374,7 +372,7 @@ void OMW::Engine::go() mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, MWBase::Environment::get().getWorld()->getPlayer(), - *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser)); + *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser, keybinderUserExists)); std::cout << "\nPress Q/ESC or close window to exit.\n"; diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 5d73025a7d..bf1d8e45f6 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -31,6 +31,12 @@ namespace MWBase virtual void setDragDrop(bool dragDrop) = 0; virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; + + virtual std::string getActionDescription (int action) = 0; + virtual std::string getActionBindingName (int action) = 0; + virtual std::vector getActionSorting () = 0; + virtual int getNumActions() = 0; + virtual void enableDetectingBindingMode (int action) = 0; }; } diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 931353a900..fde965256c 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -177,6 +177,10 @@ namespace MWBase virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedWeapon() = 0; + virtual void disallowMouse() = 0; + virtual void allowMouse() = 0; + virtual void notifyInputActionBound() = 0; + virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; ///< Hides dialog and schedules dialog to be deleted. diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index bdbb316b05..92dc4e495f 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -237,6 +237,9 @@ void HUD::setBottomRightVisibility(bool effectBoxVisible, bool minimapBoxVisible void HUD::onWorldClicked(MyGUI::Widget* _sender) { + if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) + return; + if (mDragAndDrop->mIsOnDragAndDrop) { // drop item into the gameworld diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index 485c788fc3..baa350a10c 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -48,6 +48,8 @@ namespace MWGui MyGUI::TextBox* mCellNameBox; MyGUI::TextBox* mWeaponSpellBox; + MyGUI::Widget* mDummy; + MyGUI::WidgetPtr fpsbox; MyGUI::TextBox* fpscounter; MyGUI::TextBox* trianglecounter; diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index f6597a64ef..477ffe0e24 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -115,6 +115,7 @@ namespace MWGui getWidget(mMiscShadows, "MiscShadows"); getWidget(mShadowsDebug, "ShadowsDebug"); getWidget(mUnderwaterButton, "UnderwaterButton"); + getWidget(mControlsBox, "ControlsBox"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mUnderwaterButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); @@ -511,4 +512,54 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); MWBase::Environment::get().getInputManager()->processChangedSettings(changed); } + + void SettingsWindow::updateControlsBox() + { + while (mControlsBox->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mControlsBox->getChildAt(0)); + + std::vector actions = MWBase::Environment::get().getInputManager()->getActionSorting (); + + const int h = 18; + const int w = mControlsBox->getWidth() - 34; + int curH = 6; + for (std::vector::const_iterator it = actions.begin(); it != actions.end(); ++it) + { + std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (*it); + if (desc == "") + continue; + + std::string binding = MWBase::Environment::get().getInputManager()->getActionBindingName (*it); + + MyGUI::TextBox* leftText = mControlsBox->createWidget("SandText", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); + leftText->setCaptionWithReplacing(desc); + + MyGUI::Button* rightText = mControlsBox->createWidget("SandTextButton", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); + rightText->setCaptionWithReplacing(binding); + rightText->setTextAlign (MyGUI::Align::Right); + rightText->setUserData(*it); // save the action id for callbacks + rightText->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onRebindAction); + curH += h; + } + + mControlsBox->setCanvasSize (mControlsBox->getWidth(), std::max(curH, mControlsBox->getHeight())); + } + + void SettingsWindow::onRebindAction(MyGUI::Widget* _sender) + { + int actionId = *_sender->getUserData(); + + static_cast(_sender)->setCaptionWithReplacing("#{sNone}"); + + MWBase::Environment::get().getWindowManager ()->messageBox ("#{sControlsMenu3}", std::vector()); + MWBase::Environment::get().getWindowManager ()->disallowMouse(); + + MWBase::Environment::get().getInputManager ()->enableDetectingBindingMode (actionId); + + } + + void SettingsWindow::open() + { + updateControlsBox (); + } } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index ca11b6f9c2..61614da014 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -15,6 +15,10 @@ namespace MWGui public: SettingsWindow(MWBase::WindowManager& parWindowManager); + virtual void open(); + + void updateControlsBox(); + private: static int const sFovMin = 30; static int const sFovMax = 140; @@ -60,6 +64,9 @@ namespace MWGui MyGUI::ScrollBar* mFootstepsVolumeSlider; MyGUI::ScrollBar* mMusicVolumeSlider; + // controls + MyGUI::ScrollView* mControlsBox; + void onOkButtonClicked(MyGUI::Widget* _sender); void onFpsToggled(MyGUI::Widget* _sender); void onTextureFilteringToggled(MyGUI::Widget* _sender); @@ -72,6 +79,8 @@ namespace MWGui void onShadersToggled(MyGUI::Widget* _sender); void onShadowTextureSize(MyGUI::Widget* _sender); + void onRebindAction(MyGUI::Widget* _sender); + void apply(); }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 183c435fdf..676eb2046e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -134,6 +134,8 @@ WindowManager::WindowManager( mAlchemyWindow = new AlchemyWindow(*this); mSpellWindow = new SpellWindow(*this); + mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); + // The HUD is always on mHud->setVisible(true); @@ -230,11 +232,16 @@ void WindowManager::updateVisible() bool gameMode = !isGuiMode(); + mInputBlocker->setVisible (gameMode); + if (gameMode) mToolTips->enterGameMode(); else mToolTips->enterGuiMode(); + if (gameMode) + MyGUI::InputManager::getInstance ().setKeyFocusWidget (NULL); + setMinimapVisibility((mAllowed & GW_Map) && !mMap->pinned()); setWeaponVisibility((mAllowed & GW_Inventory) && !mInventoryWindow->pinned()); setSpellVisibility((mAllowed & GW_Magic) && !mSpellWindow->pinned()); @@ -646,6 +653,7 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector mScrollWindow->center(); mBookWindow->center(); mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); + mInputBlocker->setSize(MyGUI::IntSize(x,y)); } } @@ -823,3 +831,19 @@ WindowManager::SkillList WindowManager::getPlayerMajorSkills() { return mPlayerMajorSkills; } + +void WindowManager::disallowMouse() +{ + mInputBlocker->setVisible (true); +} + +void WindowManager::allowMouse() +{ + mInputBlocker->setVisible (!isGuiMode ()); +} + +void WindowManager::notifyInputActionBound () +{ + mSettingsWindow->updateControlsBox (); + allowMouse(); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index eaa6a16832..3913055942 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -157,6 +157,10 @@ namespace MWGui virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); + virtual void disallowMouse(); + virtual void allowMouse(); + virtual void notifyInputActionBound(); + virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. virtual void messageBox (const std::string& message, const std::vector& buttons); @@ -208,6 +212,8 @@ namespace MWGui CharacterCreation* mCharGen; + MyGUI::Widget* mInputBlocker; + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager ESM::Class mPlayerClass; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 0318995389..09ed50944c 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -14,8 +14,6 @@ #include #include -#include - #include #include "../engine.hpp" @@ -31,7 +29,7 @@ namespace MWInput MWBase::WindowManager &windows, bool debug, OMW::Engine& engine, - const std::string& userFile) + const std::string& userFile, bool userFileExists) : mOgre(ogre) , mPlayer(player) , mWindows(windows) @@ -101,11 +99,12 @@ namespace MWInput MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mMouse->getMouseState ().Z.abs); - mInputCtrl = new ICS::InputControlSystem(userFile, true, NULL, NULL, A_LAST); + std::string file = userFileExists ? userFile : ""; + mInputCtrl = new ICS::InputControlSystem(file, true, this, NULL, A_Last); loadKeyDefaults(); - for (int i = 0; i < A_LAST; ++i) + for (int i = 0; i < A_Last; ++i) { mInputCtrl->getChannel (i)->addListener (this); } @@ -322,8 +321,7 @@ namespace MWInput { mInputCtrl->keyPressed (arg); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text); + MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text); return true; } @@ -341,8 +339,7 @@ namespace MWInput { mInputCtrl->mousePressed (arg, id); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); return true; } @@ -440,15 +437,11 @@ namespace MWInput { bool gameMode = !mWindows.isGuiMode(); - std::cout << "gameMode: " << gameMode << std::endl; - // Toggle between game mode and inventory mode if(gameMode) mWindows.pushGuiMode(MWGui::GM_Inventory); else if(mWindows.getMode() == MWGui::GM_Inventory) mWindows.popGuiMode(); - else - std::cout << "toggleInv didnt do anything!!!" << std::endl; // .. but don't touch any other mode. } @@ -535,7 +528,7 @@ namespace MWInput std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; - for (int i = 0; i < A_LAST; ++i) + for (int i = 0; i < A_Last; ++i) { if (mInputCtrl->getChannel(i)->getControlsCount () == 0) { @@ -551,4 +544,141 @@ namespace MWInput } } + std::string InputManager::getActionDescription (int action) + { + std::map descriptions; + + descriptions[A_Activate] = "sActivate"; + descriptions[A_MoveBackward] = "sBack"; + descriptions[A_MoveForward] = "sForward"; + descriptions[A_MoveLeft] = "sLeft"; + descriptions[A_MoveRight] = "sRight"; + descriptions[A_ToggleWeapon] = "sReady_Weapon"; + descriptions[A_ToggleSpell] = "sReady_Magic"; + descriptions[A_Console] = "sConsoleTitle"; + descriptions[A_Crouch] = "sCrouch_Sneak"; + descriptions[A_AutoMove] = "sAuto_Run"; + descriptions[A_Jump] = "sJump"; + descriptions[A_Journal] = "sJournal"; + descriptions[A_Rest] = "sRestKey"; + descriptions[A_Inventory] = "sInventory"; + + if (action == A_GameMenu) + return "Menu"; // not configurable in morrowind so no GMST + + if (descriptions[action] == "") + return ""; // not configurable + + return "#{" + descriptions[action] + "}"; + } + + std::string InputManager::getActionBindingName (int action) + { + if (mInputCtrl->getChannel (action)->getControlsCount () == 0) + return "#{sNone}"; + + ICS::Control* c = mInputCtrl->getChannel (action)->getAttachedControls ().front().control; + + if (mInputCtrl->getKeyBinding (c, ICS::Control::INCREASE) != OIS::KC_UNASSIGNED) + return mInputCtrl->keyCodeToString (mInputCtrl->getKeyBinding (c, ICS::Control::INCREASE)); + else if (mInputCtrl->getMouseButtonBinding (c, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) + return "#{sMouse} " + boost::lexical_cast(mInputCtrl->getMouseButtonBinding (c, ICS::Control::INCREASE)); + else + return "#{sNone}"; + } + + std::vector InputManager::getActionSorting() + { + std::vector ret; + ret.push_back(A_MoveForward); + ret.push_back(A_MoveBackward); + ret.push_back(A_MoveLeft); + ret.push_back(A_MoveRight); + ret.push_back(A_Crouch); + ret.push_back(A_Activate); + ret.push_back(A_ToggleWeapon); + ret.push_back(A_AutoMove); + ret.push_back(A_Jump); + ret.push_back(A_Inventory); + ret.push_back(A_Journal); + ret.push_back(A_Rest); + ret.push_back(A_Console); + ret.push_back(A_GameMenu); + + return ret; + } + + void InputManager::enableDetectingBindingMode (int action) + { + ICS::Control* c = mInputCtrl->getChannel (action)->getAttachedControls ().front().control; + + mInputCtrl->enableDetectingBindingState (c, ICS::Control::INCREASE); + } + + void InputManager::mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction) + { + // we don't want mouse movement bindings + return; + } + + void InputManager::keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , OIS::KeyCode key, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::keyBindingDetected (ICS, control, key, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , unsigned int button, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::mouseButtonBindingDetected (ICS, control, button, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int axis, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickAxisBindingDetected (ICS, control, deviceId, axis, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, unsigned int button, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickButtonBindingDetected (ICS, control, deviceId, button, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickPOVBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int pov,ICS:: InputControlSystem::POVAxis axis, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickPOVBindingDetected (ICS, control, deviceId, pov, axis, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickSliderBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int slider, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickSliderBindingDetected (ICS, control, deviceId, slider, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::clearAllBindings (ICS::Control* control) + { + // right now we don't really need multiple bindings for the same action, so remove all others first + if (mInputCtrl->getKeyBinding (control, ICS::Control::INCREASE) != OIS::KC_UNASSIGNED) + mInputCtrl->removeKeyBinding (mInputCtrl->getKeyBinding (control, ICS::Control::INCREASE)); + if (mInputCtrl->getMouseButtonBinding (control, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) + mInputCtrl->removeMouseButtonBinding (mInputCtrl->getMouseButtonBinding (control, ICS::Control::INCREASE)); + + /// \todo add joysticks here once they are added + } + } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 9fc77aeeb4..d3d4d13853 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -46,6 +46,7 @@ namespace OIS #include #include +#include namespace MWInput { @@ -53,7 +54,7 @@ namespace MWInput /** * @brief Class that handles all input and key bindings for OpenMW. */ - class InputManager : public MWBase::InputManager, public OIS::KeyListener, public OIS::MouseListener, public ICS::ChannelListener + class InputManager : public MWBase::InputManager, public OIS::KeyListener, public OIS::MouseListener, public ICS::ChannelListener, public ICS::DetectingBindingListener { public: InputManager(OEngine::Render::OgreRenderer &_ogre, @@ -61,7 +62,7 @@ namespace MWInput MWBase::WindowManager &_windows, bool debug, OMW::Engine& engine, - const std::string& userFile); + const std::string& userFile, bool userFileExists); virtual ~InputManager(); @@ -75,6 +76,12 @@ namespace MWInput virtual void toggleControlSwitch (const std::string& sw, bool value); + virtual std::string getActionDescription (int action); + virtual std::string getActionBindingName (int action); + virtual int getNumActions() { return A_Last; } + virtual std::vector getActionSorting (); + virtual void enableDetectingBindingMode (int action); + public: virtual bool keyPressed( const OIS::KeyEvent &arg ); @@ -86,6 +93,29 @@ namespace MWInput virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); + virtual void mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction); + + virtual void keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , OIS::KeyCode key, ICS::Control::ControlChangingDirection direction); + + virtual void mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , unsigned int button, ICS::Control::ControlChangingDirection direction); + + virtual void joystickAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int axis, ICS::Control::ControlChangingDirection direction); + + virtual void joystickButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, unsigned int button, ICS::Control::ControlChangingDirection direction); + + virtual void joystickPOVBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int pov,ICS:: InputControlSystem::POVAxis axis, ICS::Control::ControlChangingDirection direction); + + virtual void joystickSliderBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int slider, ICS::Control::ControlChangingDirection direction); + + void clearAllBindings (ICS::Control* control); + private: OEngine::Render::OgreRenderer &mOgre; MWWorld::Player &mPlayer; @@ -175,7 +205,7 @@ namespace MWInput A_ToggleWeapon, A_ToggleSpell, - A_LAST // Marker for the last item + A_Last // Marker for the last item }; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index a14606ade9..ee4855f381 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -82,6 +82,13 @@ + + + + + + +