Add basic keyboard navigation for the GUI
(Shift)Tab cycles, arrow keys move to the next button in that direction, Enter/Space accepts. Note: Unless MyGUI is hacked to bits, clicking on an empty space will annoyingly reset the key focus. Not sure how to deal with that yet. The visual highlight for selected buttons requires MyGUI commit 632d007429d0bf0c7d7f6c5db4a08353a63dd839 or later to appear (to be released in 3.2.3).pull/303/head
parent
e7ad012977
commit
c11fe6788f
@ -0,0 +1,140 @@
|
||||
#include "keyboardnavigation.hpp"
|
||||
|
||||
#include <MyGUI_InputManager.h>
|
||||
#include <MyGUI_WidgetManager.h>
|
||||
#include <MyGUI_Button.h>
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
/// Recursively get all child widgets that accept keyboard input
|
||||
void getKeyFocusWidgets(MyGUI::Widget* parent, std::vector<MyGUI::Widget*>& results)
|
||||
{
|
||||
MyGUI::EnumeratorWidgetPtr enumerator = parent->getEnumerator();
|
||||
while (enumerator.next())
|
||||
{
|
||||
MyGUI::Widget* w = enumerator.current();
|
||||
if (!w->getVisible() || !w->getEnabled())
|
||||
continue;
|
||||
if (w->getNeedKeyFocus())
|
||||
results.push_back(w);
|
||||
else
|
||||
getKeyFocusWidgets(w, results);
|
||||
}
|
||||
}
|
||||
|
||||
KeyboardNavigation::KeyboardNavigation()
|
||||
{
|
||||
}
|
||||
|
||||
KeyboardNavigation::~KeyboardNavigation()
|
||||
{
|
||||
}
|
||||
|
||||
bool isButtonFocus()
|
||||
{
|
||||
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
|
||||
return focus->getTypeName().find("Button") != std::string::npos;
|
||||
}
|
||||
|
||||
enum Direction
|
||||
{
|
||||
D_Left,
|
||||
D_Up,
|
||||
D_Right,
|
||||
D_Down,
|
||||
D_Next,
|
||||
D_Prev
|
||||
};
|
||||
|
||||
bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text)
|
||||
{
|
||||
switch (key.getValue())
|
||||
{
|
||||
case MyGUI::KeyCode::ArrowLeft:
|
||||
return switchFocus(D_Left, false);
|
||||
case MyGUI::KeyCode::ArrowRight:
|
||||
return switchFocus(D_Right, false);
|
||||
case MyGUI::KeyCode::ArrowUp:
|
||||
return switchFocus(D_Up, false);
|
||||
case MyGUI::KeyCode::ArrowDown:
|
||||
return switchFocus(D_Down, false);
|
||||
case MyGUI::KeyCode::Tab:
|
||||
return switchFocus(MyGUI::InputManager::getInstance().isShiftPressed() ? D_Prev : D_Next, true);
|
||||
case MyGUI::KeyCode::Return:
|
||||
case MyGUI::KeyCode::NumpadEnter:
|
||||
case MyGUI::KeyCode::Space:
|
||||
return accept();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool KeyboardNavigation::switchFocus(int direction, bool wrap)
|
||||
{
|
||||
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
|
||||
if (!focus)
|
||||
return false;
|
||||
|
||||
if (!isButtonFocus() && direction != D_Prev && direction != D_Next)
|
||||
return false;
|
||||
|
||||
MyGUI::Widget* window = MyGUI::InputManager::getInstance().getKeyFocusWidget();
|
||||
while (window->getParent())
|
||||
window = window->getParent();
|
||||
|
||||
MyGUI::VectorWidgetPtr keyFocusList;
|
||||
getKeyFocusWidgets(window, keyFocusList);
|
||||
|
||||
if (keyFocusList.empty())
|
||||
return false;
|
||||
|
||||
MyGUI::VectorWidgetPtr::iterator found = std::find(keyFocusList.begin(), keyFocusList.end(), focus);
|
||||
if (found == keyFocusList.end())
|
||||
return false;
|
||||
|
||||
bool forward = (direction == D_Next || direction == D_Right || direction == D_Down);
|
||||
|
||||
int index = found - keyFocusList.begin();
|
||||
index = forward ? (index+1) : (index-1);
|
||||
if (wrap)
|
||||
index = (index + keyFocusList.size())%keyFocusList.size();
|
||||
else
|
||||
index = std::min(std::max(0, index), static_cast<int>(keyFocusList.size())-1);
|
||||
|
||||
MyGUI::Widget* next = keyFocusList[index];
|
||||
int vertdiff = next->getTop() - focus->getTop();
|
||||
int horizdiff = next->getLeft() - focus->getLeft();
|
||||
if (direction == D_Right && horizdiff <= 0)
|
||||
return false;
|
||||
else if (direction == D_Left && horizdiff >= 0)
|
||||
return false;
|
||||
else if (direction == D_Down && vertdiff <= 0)
|
||||
return false;
|
||||
else if (direction == D_Up && vertdiff >= 0)
|
||||
return false;
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(keyFocusList[index]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeyboardNavigation::accept()
|
||||
{
|
||||
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
|
||||
if (!focus)
|
||||
return false;
|
||||
//MyGUI::Button* button = focus->castType<MyGUI::Button>(false);
|
||||
//if (button && button->getEnabled())
|
||||
if (focus->getTypeName().find("Button") != std::string::npos && focus->getEnabled())
|
||||
{
|
||||
focus->eventMouseButtonClick(focus);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
#ifndef OPENMW_MWGUI_KEYBOARDNAVIGATION_H
|
||||
#define OPENMW_MWGUI_KEYBOARDNAVIGATION_H
|
||||
|
||||
#include <MyGUI_KeyCode.h>
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
class KeyboardNavigation
|
||||
{
|
||||
public:
|
||||
KeyboardNavigation();
|
||||
~KeyboardNavigation();
|
||||
|
||||
/// @return Was the key handled by this class?
|
||||
bool injectKeyPress(MyGUI::KeyCode key, unsigned int text);
|
||||
|
||||
private:
|
||||
bool switchFocus(int direction, bool wrap);
|
||||
|
||||
/// Send button press event to focused button
|
||||
bool accept();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue