Tab Order
From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Written for CEGUI 0.5
Works with versions 0.5.x (obsolete)
Written for CEGUI 0.6
Works with versions 0.6.x (obsolete)
Contents
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 "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; }
#ifndef _WidgetFocus_Demo_h_ #define _WidgetFocus_Demo_h_ #include "CEGuiSample.h" #include "CEGUI.h" #include "TabNavigation.h" class DemoSample : public CEGuiSample { public: bool initialiseSample() { using namespace CEGUI; try { // Retrieve the window manager WindowManager& winMgr = WindowManager::getSingleton(); // Load the TaharezLook scheme and set up the default mouse cursor and font SchemeManager::getSingleton().loadScheme("TaharezLook.scheme"); System::getSingleton().setDefaultMouseCursor("TaharezLook", "MouseArrow"); if(!FontManager::getSingleton().isFontPresent("Commonwealth-10")) FontManager::getSingleton().createFont("Commonwealth-10.font"); // Set the GUI Sheet Window* sheet = winMgr.createWindow("DefaultWindow", "root_wnd"); System::getSingleton().setGUISheet(sheet); // Load a layout Window* guiLayout = winMgr.loadWindowLayout("TabNavigation.layout"); sheet->addChildWindow(guiLayout); /* TabNavigation-specific code */ navMainWindow.setParent("TabOrder/MainWindow"); navMainWindow.addWidget("TabOrder/MainWindow/Field_1"); navMainWindow.addWidget("TabOrder/MainWindow/Field_2"); navMainWindow.addWidget("TabOrder/MainWindow/Field_3"); navSecondaryWindow.setParent("TabOrder/SecondaryWindow"); navSecondaryWindow.addWidget("TabOrder/SecondaryWindow/Field_1"); navSecondaryWindow.addWidget("TabOrder/SecondaryWindow/Field_2"); navSecondaryWindow.addWidget("TabOrder/SecondaryWindow/Field_3"); } catch(Exception &e) { #if defined( __WIN32__ ) || defined( _WIN32 ) MessageBox(NULL, e.getMessage().c_str(), "Error initializing the demo", MB_OK | MB_ICONERROR | MB_TASKMODAL); #else std::cerr << "Error initializing the demo:" << e.getMessage().c_str() << "\n"; #endif } return true; } void cleanupSample(void) { } private: TabNavigation navMainWindow; TabNavigation navSecondaryWindow; }; #endif // _WidgetFocus_Demo_h_
<?xml version="1.0" encoding="UTF-8"?> <GUILayout > <Window Type="DefaultWindow" Name="TabOrder" > <Property Name="InheritsAlpha" Value="False" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedAreaRect" Value="{{0,0},{0,0},{1,0},{1,0}}" /> <Window Type="TaharezLook/FrameWindow" Name="TabOrder/MainWindow" > <Property Name="Text" Value="Main Window" /> <Property Name="TitlebarFont" Value="Commonwealth-10" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="TitlebarEnabled" Value="True" /> <Property Name="UnifiedAreaRect" Value="{{0.0296875,0},{0.0583334,0},{0.43125,0},{0.468749,0}}" /> <Window Type="TaharezLook/Editbox" Name="TabOrder/MainWindow/Field_1" > <Property Name="Text" Value="Field 1" /> <Property Name="MaxTextLength" Value="1073741823" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedAreaRect" Value="{{0.0719849,0},{0.19543,0},{0.905642,0},{0.354059,0}}" /> </Window> <Window Type="TaharezLook/Button" Name="TabOrder/MainWindow/Field_2" > <Property Name="Text" Value="Field 2" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedAreaRect" Value="{{0.071985,0},{0.413199,0},{0.905642,0},{0.663199,0}}" /> </Window> <Window Type="TaharezLook/Spinner" Name="TabOrder/MainWindow/Field_3" > <Property Name="StepSize" Value="1" /> <Property Name="CurrentValue" Value="0" /> <Property Name="MaximumValue" Value="32767" /> <Property Name="MinimumValue" Value="-32768" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedAreaRect" Value="{{0.071985,0},{0.711169,0},{0.905642,0},{0.961169,0}}" /> </Window> </Window> <Window Type="TaharezLook/FrameWindow" Name="TabOrder/SecondaryWindow" > <Property Name="Font" Value="Commonwealth-10" /> <Property Name="Text" Value="Secondary Window" /> <Property Name="TitlebarFont" Value="Commonwealth-10" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="TitlebarEnabled" Value="True" /> <Property Name="UnifiedAreaRect" Value="{{0.454688,0},{0.0583334,0},{0.856251,0},{0.468749,0}}" /> <Window Type="TaharezLook/Editbox" Name="TabOrder/SecondaryWindow/Field_1" > <Property Name="Font" Value="Commonwealth-10" /> <Property Name="Text" Value="Field 1" /> <Property Name="MaxTextLength" Value="1073741823" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedAreaRect" Value="{{0.071985,0},{0.19543,0},{0.905642,0},{0.354059,0}}" /> </Window> <Window Type="TaharezLook/Button" Name="TabOrder/SecondaryWindow/Field_2" > <Property Name="Font" Value="Commonwealth-10" /> <Property Name="Text" Value="Field 2" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedAreaRect" Value="{{0.071985,0},{0.413199,0},{0.905642,0},{0.663199,0}}" /> </Window> <Window Type="TaharezLook/Spinner" Name="TabOrder/SecondaryWindow/Field_3" > <Property Name="Font" Value="Commonwealth-10" /> <Property Name="StepSize" Value="1" /> <Property Name="CurrentValue" Value="0" /> <Property Name="MaximumValue" Value="32767" /> <Property Name="MinimumValue" Value="-32768" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedAreaRect" Value="{{0.071985,0},{0.711169,0},{0.905642,0},{0.961169,0}}" /> </Window> </Window> </Window> </GUILayout>