Difference between revisions of "Tab Order"
(→Introduction) |
(→Introduction) |
||
| Line 2: | Line 2: | ||
This code provides Tab and Shift+Tab navigation through a list of widgets. | This code provides Tab and Shift+Tab navigation through a list of widgets. | ||
| − | Create an instance of TabNavigation for each group of tab order, such as one per dialog. Specify the parent via a call to setParent(). This will help catch Tab and Shift+Tab keys, which will then properly cycle through the widgets specified via addWidget(). Note that addWidget() accepts window names as well as the handle of TabControl; this will automatically add the TabButton of that TabControl. | + | Create an instance of TabNavigation for each group of tab order, such as one per dialog. Specify the parent via a call to setParent(). This will help catch '''Tab''' and '''Shift+Tab''' keys, which will then properly cycle through the widgets specified via addWidget(). Note that addWidget() accepts window names as well as the handle of TabControl; this will automatically add the TabButton of that TabControl. |
| + | |||
| + | Please discuss this snippet within the [http://www.cegui.org.uk/phpBB2/viewtopic.php?p=8676#8676 Tab Order] thread. | ||
=== Files === | === Files === | ||
Revision as of 01:38, 15 July 2006
Introduction
This code provides Tab and Shift+Tab navigation through a list of widgets.
Create an instance of TabNavigation for each group of tab order, such as one per dialog. Specify the parent via a call to setParent(). This will help catch Tab and Shift+Tab keys, which will then properly cycle through the widgets specified via addWidget(). Note that addWidget() accepts window names as well as the handle of TabControl; this will automatically add the TabButton of that TabControl.
Please discuss this snippet within the Tab Order thread.
Files
#ifndef _TabNavigation_h_
#define _TabNavigation_h_
#include <vector>
#include "CEGUI.h"
class TabNavigation
{
public:
/* Specifies the parent or container.
Used to trap Tab and Shift+Tab keys and relay them to the list of tab navigation. */
void setParent(const CEGUI::String& window);
/* Adds a TabControl widget.
Its tab buttons will be added to the list of tab navigation. */
void addWidget(const CEGUI::TabControl* tabControl);
/* Adds a widget to the list of tab navigation.
The order in which they are added corresponds to the tab order. */
void addWidget(const CEGUI::String& window);
private:
/* Ensures that the last known focused widget regains input. */
bool _onParentActivated(const CEGUI::EventArgs& e);
/* Handles non-tab key activation.
This will ensure that the next tab key navigation will start from
the relevant widget. */
bool _onActivated(const CEGUI::EventArgs& e);
/* Traps the Tab and Shift+Tab key and activates the next or previous
widget accordingly. */
bool _onCharacterKey(const CEGUI::EventArgs& e);
/* Maintains the list of widgets that participate in the tab order */
std::vector<CEGUI::String> _tabNavigation;
/* Maintains the last known widget to have the focus. */
std::vector<CEGUI::String>::iterator _lastKnownFocus;
};
#endif // _TabNavigation_h_
#include "c:/Ogre/ClansOfDestiny/src/src/GUI/TabNavigation.h"
/* These provide a visual focus cue when the scheme does not provide one
* for every widget, such as TaharezLook's PushButton */
#define HACKED_FOCUS_GAIN(window) CEGUI::WindowManager::getSingleton().getWindow(window)->setAlpha(1.0f)
#define HACKED_FOCUS_LOSS(window) CEGUI::WindowManager::getSingleton().getWindow(window)->setAlpha(0.8f)
/* You can deactivate these hacked focus functions by defining the macro as a comment:
#define HACKED_FOCUS_GAIN(window) //
#define HACKED_FOCUS_LOSS(window) //
*/
void TabNavigation::setParent(const CEGUI::String& window)
{
// Parent will feed the tab and shift+tab navigation to the list of monitored widgets
CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton();
wmgr.getWindow(window)->subscribeEvent(CEGUI::Window::EventCharacterKey, CEGUI::Event::Subscriber(&TabNavigation::_onCharacterKey, this));
wmgr.getWindow(window)->subscribeEvent(CEGUI::Window::EventActivated, CEGUI::Event::Subscriber(&TabNavigation::_onParentActivated, this));
}
void TabNavigation::addWidget(const CEGUI::String& window)
{
// Add a widget to the list of widget navigation
CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton();
wmgr.getWindow(window)->subscribeEvent(CEGUI::Window::EventCharacterKey, CEGUI::Event::Subscriber(&TabNavigation::_onCharacterKey, this));
wmgr.getWindow(window)->subscribeEvent(CEGUI::Window::EventActivated, CEGUI::Event::Subscriber(&TabNavigation::_onActivated, this));
HACKED_FOCUS_LOSS(window);
if(!_tabNavigation.size())
{
wmgr.getWindow(window)->activate(); // Activate the first widget by default
HACKED_FOCUS_GAIN(window);
}
_tabNavigation.push_back(window);
_lastKnownFocus = _tabNavigation.begin(); // Reset the iterator after each modification to the vector
}
void TabNavigation::addWidget(const CEGUI::TabControl* tabControl)
{
// Add every tab buttons of a tab control
CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton();
CEGUI::Window* tabPaneButtons = wmgr.getWindow(tabControl->getName() + "__auto_TabPane__Buttons");
for(size_t idx = 0; idx < tabPaneButtons->getChildCount(); idx++)
{
addWidget(tabPaneButtons->getChildAtIdx(idx)->getName());
}
}
bool TabNavigation::_onParentActivated(const CEGUI::EventArgs& e)
{
// Parent is being activated, activate the widget with the last known focus
if(_tabNavigation.size() && _lastKnownFocus != _tabNavigation.end())
{
CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton();
CEGUI::Window* window = wmgr.getWindow(*_lastKnownFocus);
if(window)
window->activate();
}
return true;
}
bool TabNavigation::_onActivated(const CEGUI::EventArgs& e)
{
// A focus widget has been activated without tabbing (could be a mouse click)
CEGUI::String currentlyFocused = static_cast<const CEGUI::WindowEventArgs&>(e).window->getName();
if(_tabNavigation.size()
&& (_lastKnownFocus == _tabNavigation.end()
|| (*_lastKnownFocus).compare(currentlyFocused)) )
{
if(_lastKnownFocus != _tabNavigation.end())
{
// These curly braces are IMPORTANT!!!
HACKED_FOCUS_LOSS(*_lastKnownFocus);
}
for(_lastKnownFocus = _tabNavigation.begin(); _lastKnownFocus != _tabNavigation.end(); _lastKnownFocus++)
{
if(!(*_lastKnownFocus).compare(currentlyFocused))
{
HACKED_FOCUS_GAIN(currentlyFocused);
return true;
}
}
// Can't figure out who has the focus
_lastKnownFocus = _tabNavigation.begin();
HACKED_FOCUS_GAIN(*_lastKnownFocus);
}
return true;
}
bool TabNavigation::_onCharacterKey(const CEGUI::EventArgs& e)
{
// Handle Tab (next) and Shift+Tab (previous) widget navigation
assert(_tabNavigation.size() && "Don't simply call setParent(), also call addWidget()");
if(static_cast<const CEGUI::KeyEventArgs&>(e).codepoint == 9) // Tab or Shift+Tab
{
// Identify who currently has the focus
CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton();
std::vector<CEGUI::String>::iterator itFocus, itCurrent;
if(_lastKnownFocus != _tabNavigation.end() && wmgr.getWindow(*_lastKnownFocus)->isActive())
{
// The last known focus is still in focus
itCurrent = _lastKnownFocus;
}
else
{
// Figure out who (if anyone) has the focus
for(itCurrent = _tabNavigation.begin(); itCurrent != _tabNavigation.end(); itCurrent++)
{
if(wmgr.getWindow(*itCurrent)->isActive())
{
// Found who has the focus
break;
}
}
if(itCurrent == _tabNavigation.end())
{
// We did not find who had the focus
// Someone not in our list of monitored widgets has STOLEN the focus!
// Use the last known focus or, if that's invalid, the first widget
itCurrent = _lastKnownFocus != _tabNavigation.end() ? _lastKnownFocus : _tabNavigation.begin();
}
}
// Change the focus
itFocus = itCurrent; // The search starts from the currently focused
CEGUI::Window* newWidget = 0;
do
{
if(static_cast<const CEGUI::KeyEventArgs&>(e).sysKeys & CEGUI::Shift)
{
// Search previous
if(itFocus == _tabNavigation.begin())
itFocus = --_tabNavigation.end();
else
itFocus--;
}
else
{
// Search next
itFocus++;
if(itFocus == _tabNavigation.end())
itFocus = _tabNavigation.begin();
}
newWidget = wmgr.getWindow(*itFocus);
if(newWidget->isVisible() && !newWidget->isDisabled())
{
// We have found a valid focus target
_lastKnownFocus = itFocus;
break;
}
newWidget = 0;
} while(itFocus != itCurrent); // Iterate while we're on a different widget
if(newWidget)
{
// Remove the focus from this widget
HACKED_FOCUS_LOSS(*itCurrent);
// Give the focus to this widget
newWidget->activate();
HACKED_FOCUS_GAIN(newWidget->getName());
return true;
}
}
return false;
}
Demo
I did not provide a complete demo file. Instead here are the steps to create one. Add the following to WidgetGalore#WidgetGalore.h, within the initialiseSample() function, after the TabControl code and before the catch(Exception &e):
winWidgetsTabNavigation.setParent("winWidgets");
winWidgetsTabNavigation.addWidget("VerticalScrollbar");
winWidgetsTabNavigation.addWidget("Slider");
winWidgetsTabNavigation.addWidget("Combobox");
winWidgetsTabNavigation.addWidget("Editbox");
winWidgetsTabNavigation.addWidget("HorizontalScrollbar");
winWidgetsTabNavigation.addWidget("RadioButton_A");
winWidgetsTabNavigation.addWidget("RadioButton_B");
winWidgetsTabNavigation.addWidget("RadioButton_C");
winWidgetsTabNavigation.addWidget("RadioButton_1");
winWidgetsTabNavigation.addWidget("RadioButton_2");
winWidgetsTabNavigation.addWidget("RadioButton_3");
winWidgetsTabNavigation.addWidget("MultiColumnList");
winWidgetsTabNavigation.addWidget("Checkbox");
winWidgetsTabNavigation.addWidget("Spinner");
winWidgetsTabNavigation.addWidget("ScrollablePane");
winWidgetsTabNavigation.addWidget("Listbox");
winWidgetsTabNavigation.addWidget("MultiLineEditbox");
winWidgetsTabNavigation.addWidget("btnClose");
tabWindowTabNavigation.setParent("winTabControl");
tabWindowTabNavigation.addWidget(winTabControl); // Automatically add the tab buttons
tabWindowTabNavigation.addWidget("EditBoxPage1");
tabWindowTabNavigation.addWidget("EditBoxPage2");
tabWindowTabNavigation.addWidget("btnOk");
tabWindowTabNavigation.addWidget("btnCancel");
tabWindowTabNavigation.addWidget("btnApply");
Add these within the private section:
TabNavigation winWidgetsTabNavigation; TabNavigation tabWindowTabNavigation;
Also use both WidgetGalore#main.cpp and WidgetGalore#WidgetGalore.layout.