From 1714271a7692e884fa0337cc56ef9d5d5bf74236 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Tue, 26 Sep 2017 22:21:30 +0200 Subject: [PATCH] Improve KeyboardNavigation to better handle modal windows It's no longer possible to cycle to widgets that aren't part of the current modal window. The window manager will remember the focused widget of a modal window on a limited basis (it'll be discarded when a different modal window opens). --- apps/openmw/mwgui/dialogue.cpp | 7 ++- apps/openmw/mwgui/dialogue.hpp | 2 + apps/openmw/mwgui/keyboardnavigation.cpp | 70 ++++++++++++++++++------ apps/openmw/mwgui/keyboardnavigation.hpp | 8 +++ apps/openmw/mwgui/windowbase.cpp | 5 +- apps/openmw/mwgui/windowbase.hpp | 2 + apps/openmw/mwgui/windowmanagerimp.cpp | 12 ++++ files/mygui/openmw_text.skin.xml | 1 + 8 files changed, 87 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index a7f18561e9..b7da86f4e8 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -77,7 +77,6 @@ namespace MWGui void PersuasionDialog::onOpen() { - WindowModal::onOpen(); center(); MWWorld::Ptr player = MWMechanics::getPlayer(); @@ -88,6 +87,12 @@ namespace MWGui mBribe1000Button->setEnabled (playerGold >= 1000); mGoldLabel->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); + WindowModal::onOpen(); + } + + MyGUI::Widget* PersuasionDialog::getDefaultKeyFocus() + { + return mAdmireButton; } // -------------------------------------------------------------------------------------------------- diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index c83906588f..6535ad4b24 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -32,6 +32,8 @@ namespace MWGui virtual void onOpen(); + virtual MyGUI::Widget* getDefaultKeyFocus(); + private: MyGUI::Button* mCancelButton; MyGUI::Button* mAdmireButton; diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index d4d0a8c43d..6e1113a4fa 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -38,6 +38,7 @@ bool shouldAcceptKeyFocus(MyGUI::Widget* w) KeyboardNavigation::KeyboardNavigation() : mCurrentFocus(nullptr) + , mModalWindow(nullptr) { MyGUI::WidgetManager::getInstance().registerUnlinker(this); } @@ -51,9 +52,13 @@ void KeyboardNavigation::saveFocus(int mode) { MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); if (shouldAcceptKeyFocus(focus)) + { mKeyFocus[mode] = focus; + } else + { mKeyFocus[mode] = mCurrentFocus; + } } void KeyboardNavigation::restoreFocus(int mode) @@ -87,6 +92,13 @@ void styleFocusedButton(MyGUI::Widget* w) } } +bool isRootParent(MyGUI::Widget* widget, MyGUI::Widget* root) +{ + while (widget && widget->getParent()) + widget = widget->getParent(); + return widget == root; +} + void KeyboardNavigation::onFrame() { MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); @@ -98,7 +110,7 @@ void KeyboardNavigation::onFrame() } // workaround incorrect key focus resets (fix in MyGUI TBD) - if (!shouldAcceptKeyFocus(focus) && shouldAcceptKeyFocus(mCurrentFocus)) + if (!shouldAcceptKeyFocus(focus) && shouldAcceptKeyFocus(mCurrentFocus) && (!mModalWindow || isRootParent(mCurrentFocus, mModalWindow))) { MyGUI::InputManager::getInstance().setKeyFocusWidget(mCurrentFocus); focus = mCurrentFocus; @@ -119,6 +131,25 @@ void KeyboardNavigation::onFrame() styleFocusedButton(mCurrentFocus); } +void KeyboardNavigation::setDefaultFocus(MyGUI::Widget *window, MyGUI::Widget *defaultFocus) +{ + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); + if (!focus || !shouldAcceptKeyFocus(focus)) + { + MyGUI::InputManager::getInstance().setKeyFocusWidget(defaultFocus); + } + else + { + if (!isRootParent(focus, window)) + MyGUI::InputManager::getInstance().setKeyFocusWidget(defaultFocus); + } +} + +void KeyboardNavigation::setModalWindow(MyGUI::Widget *window) +{ + mModalWindow = window; +} + enum Direction { D_Left, @@ -152,29 +183,15 @@ bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text) } } -bool selectFirstWidget() -{ - MyGUI::VectorWidgetPtr keyFocusList; - MyGUI::EnumeratorWidgetPtr enumerator = MyGUI::Gui::getInstance().getEnumerator(); - while (enumerator.next()) - getKeyFocusWidgets(enumerator.current(), keyFocusList); - - if (!keyFocusList.empty()) - { - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(keyFocusList[0]); - return true; - } - return false; -} - bool KeyboardNavigation::switchFocus(int direction, bool wrap) { MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); - if ((focus && focus->getTypeName().find("Button") == std::string::npos) && direction != D_Prev && direction != D_Next) + bool isCycle = (direction == D_Prev || direction == D_Next); + + if ((focus && focus->getTypeName().find("Button") == std::string::npos) && !isCycle) return false; - bool isCycle = (direction == D_Prev || direction == D_Next); if (focus && isCycle && focus->getUserString("AcceptTab") == "true") return false; @@ -230,6 +247,23 @@ bool KeyboardNavigation::switchFocus(int direction, bool wrap) return true; } +bool KeyboardNavigation::selectFirstWidget() +{ + MyGUI::VectorWidgetPtr keyFocusList; + MyGUI::EnumeratorWidgetPtr enumerator = MyGUI::Gui::getInstance().getEnumerator(); + if (mModalWindow) + enumerator = mModalWindow->getEnumerator(); + while (enumerator.next()) + getKeyFocusWidgets(enumerator.current(), keyFocusList); + + if (!keyFocusList.empty()) + { + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(keyFocusList[0]); + return true; + } + return false; +} + bool KeyboardNavigation::accept() { MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); diff --git a/apps/openmw/mwgui/keyboardnavigation.hpp b/apps/openmw/mwgui/keyboardnavigation.hpp index 15aa0d6a84..728f16a3df 100644 --- a/apps/openmw/mwgui/keyboardnavigation.hpp +++ b/apps/openmw/mwgui/keyboardnavigation.hpp @@ -23,15 +23,23 @@ namespace MWGui void onFrame(); + /// Set a key focus widget for this window, if one isn't already set. + void setDefaultFocus(MyGUI::Widget* window, MyGUI::Widget* defaultFocus); + + void setModalWindow(MyGUI::Widget* window); + private: bool switchFocus(int direction, bool wrap); + bool selectFirstWidget(); + /// Send button press event to focused button bool accept(); std::map mKeyFocus; MyGUI::Widget* mCurrentFocus; + MyGUI::Widget* mModalWindow; }; } diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index e563d9aa9e..a0e7eeddeb 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -67,14 +67,17 @@ WindowModal::WindowModal(const std::string& parLayout) void WindowModal::onOpen() { - // Order important. We need to save the key focus widget before its unset MWBase::Environment::get().getWindowManager()->addCurrentModal(this); //Set so we can escape it if needed + + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); MyGUI::InputManager::getInstance ().addWidgetModal (mMainWidget); + MyGUI::InputManager::getInstance().setKeyFocusWidget(focus); } void WindowModal::onClose() { MWBase::Environment::get().getWindowManager()->removeCurrentModal(this); + MyGUI::InputManager::getInstance ().removeWidgetModal (mMainWidget); } diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index f183c97feb..56901c95af 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -23,6 +23,8 @@ namespace MWGui public: WindowBase(const std::string& parLayout); + virtual MyGUI::Widget* getDefaultKeyFocus() { return NULL; } + // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_WindowBase; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 950b1edc3a..0b51b2b3f0 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1738,6 +1738,10 @@ namespace MWGui mKeyboardNavigation->saveFocus(getMode()); mCurrentModals.push(input); + mKeyboardNavigation->restoreFocus(-1); + + mKeyboardNavigation->setModalWindow(input->mMainWidget); + mKeyboardNavigation->setDefaultFocus(input->mMainWidget, input->getDefaultKeyFocus()); } void WindowManager::removeCurrentModal(WindowModal* input) @@ -1747,12 +1751,20 @@ namespace MWGui if(!mCurrentModals.empty()) { if(input == mCurrentModals.top()) + { mCurrentModals.pop(); + mKeyboardNavigation->saveFocus(-1); + } else std::cout << " warning: modal widget " << input << " " << typeid(input).name() << " not found " << std::endl; } if (mCurrentModals.empty()) + { + mKeyboardNavigation->setModalWindow(NULL); mKeyboardNavigation->restoreFocus(getMode()); + } + else + mKeyboardNavigation->setModalWindow(mCurrentModals.top()->mMainWidget); } void WindowManager::onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) diff --git a/files/mygui/openmw_text.skin.xml b/files/mygui/openmw_text.skin.xml index 163b9d1341..edc103443b 100644 --- a/files/mygui/openmw_text.skin.xml +++ b/files/mygui/openmw_text.skin.xml @@ -81,6 +81,7 @@ color_misc=0,205,205 # ???? +