Tab Order
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.