Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/usability/src/libraries/core/GUIManager.cc @ 8035

Last change on this file since 8035 was 8035, checked in by dafrick, 13 years ago

Replacing hard coded keys for the menu navigation with keys specified in the keybindings.ini file (note: You have to delete your keybindings.ini file for it to be regenerated and work correctly).
The upside is, that now we need less hackish, stuff, it's better integrated, toggling of OrxonoxOverlays (e.g. QuestGUI and PickupInventory, among others) is working again. Closing the InGameConsole with ESC no longer requires a workaround to work.
The downside is, that now GUI sheets that require input, e.g. GraphicsMenu or MiscConfigMenu, no longer support menu navigation and ESC doesn't work there. However, I don't know how to work around that, yet. But since all that ESC business is a hack anyway, I'd rather have the hacks there…

  • Property svn:eol-style set to native
File size: 21.0 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Reto Grieder
24 *      Benjamin Knecht
25 *   Co-authors:
26 *      ...
27 *
28 */
29
30#include "GUIManager.h"
31
32#include <boost/bind.hpp>
33#include <memory>
34
35#include <CEGUIDefaultLogger.h>
36#include <CEGUIExceptions.h>
37#include <CEGUIInputEvent.h>
38#include <CEGUIMouseCursor.h>
39#include <CEGUIResourceProvider.h>
40#include <CEGUISystem.h>
41#include <CEGUIWindow.h>
42#include <CEGUIWindowManager.h>
43#include <elements/CEGUIListbox.h>
44#include <elements/CEGUIListboxItem.h>
45#include <ogreceguirenderer/OgreCEGUIRenderer.h>
46
47#include "SpecialConfig.h" // Configures the macro below
48#ifdef CEGUILUA_USE_INTERNAL_LIBRARY
49#   include <ceguilua/CEGUILua.h>
50#else
51#   include <CEGUILua.h>
52#endif
53
54#include "util/Clock.h"
55#include "util/Convert.h"
56#include "util/Debug.h"
57#include "util/Exception.h"
58#include "util/OrxAssert.h"
59#include "ConfigValueIncludes.h"
60#include "Core.h"
61#include "CoreIncludes.h"
62#include "Game.h"
63#include "GraphicsManager.h"
64#include "LuaState.h"
65#include "PathConfig.h"
66#include "Resource.h"
67#include "command/ConsoleCommand.h"
68#include "input/InputManager.h"
69#include "input/InputState.h"
70#include "input/KeyBinderManager.h"
71
72namespace orxonox
73{
74    static void key_esc()
75        { GUIManager::getInstance().keyESC(); }
76    SetConsoleCommand("keyESC", &key_esc);
77
78    class CEGUILogger : public CEGUI::DefaultLogger
79    {
80    public:
81        void logEvent(const CEGUI::String& message, CEGUI::LoggingLevel level = CEGUI::Standard)
82        {
83            int orxonoxLevel = CEGUI::Standard;
84            switch (level)
85            {
86                case CEGUI::Errors:      orxonoxLevel = 1; break;
87                case CEGUI::Warnings:    orxonoxLevel = 2; break;
88                case CEGUI::Standard:    orxonoxLevel = 4; break;
89                case CEGUI::Informative: orxonoxLevel = 5; break;
90                case CEGUI::Insane:      orxonoxLevel = 6; break;
91                default: OrxAssert(false, "CEGUI log level out of range, inpect immediately!");
92            }
93            OutputHandler::getOutStream(orxonoxLevel)
94                << "CEGUI: " << message << std::endl;
95
96            CEGUI::DefaultLogger::logEvent(message, level);
97        }
98    };
99
100    static CEGUI::MouseButton convertButton(MouseButtonCode::ByEnum button);
101
102    GUIManager* GUIManager::singletonPtr_s = 0;
103    /*static*/ const std::string GUIManager::defaultScheme_ = "TaharezGreen";
104
105    static const std::string __CC_navigateGUI_name = "navigateGUI";
106
107    SetConsoleCommand("showGUI", &GUIManager::showGUI).defaultValue(1, false).defaultValue(2, false);
108    SetConsoleCommand("hideGUI", &GUIManager::hideGUI);
109    SetConsoleCommand("toggleGUI", &GUIManager::toggleGUI).defaultValue(1, false).defaultValue(2, false);
110    SetConsoleCommand(__CC_navigateGUI_name, &GUIManager::navigateGUI).deactivate();
111
112    //! Strings that specify modes for the GUI navigation.
113    /*static*/ const std::string GUIManager::NAVIGATE_UP = "up";
114    /*static*/ const std::string GUIManager::NAVIGATE_DOWN = "down";
115    /*static*/ const std::string GUIManager::NAVIGATE_LEFT = "left";
116    /*static*/ const std::string GUIManager::NAVIGATE_RIGHT = "right";
117    /*static*/ const std::string GUIManager::NAVIGATE_ENTER = "enter";
118
119    /**
120    @brief
121        Constructs the GUIManager by starting up CEGUI
122
123        Creates the interface to Ogre, sets up the CEGUI renderer and the Lua script module together with the Lua engine.
124        The log is set up and connected to the CEGUILogger.
125        After Lua setup tolua++-elements are linked to Lua-state to give Lua access to C++-code.
126        Finally initial Lua code is executed (maybe we can do this with the CEGUI startup script automatically).
127    @return true if success, otherwise false
128    */
129    GUIManager::GUIManager(const std::pair<int, int>& mousePosition)
130        : resourceProvider_(NULL)
131        , camera_(NULL)
132    {
133        RegisterRootObject(GUIManager);
134        this->setConfigValues();
135
136        using namespace CEGUI;
137
138        COUT(3) << "Initialising CEGUI." << std::endl;
139
140        // Note: No SceneManager specified yet
141        guiRenderer_.reset(new OgreCEGUIRenderer(GraphicsManager::getInstance().getRenderWindow(), Ogre::RENDER_QUEUE_OVERLAY, false, 3000));
142        resourceProvider_ = guiRenderer_->createResourceProvider();
143        resourceProvider_->setDefaultResourceGroup("General");
144
145        // Setup scripting
146        luaState_.reset(new LuaState());
147        rootFileInfo_ = Resource::getInfo("InitialiseGUI.lua");
148        // This is necessary to ensure that input events also use the right resource info when triggering lua functions
149        luaState_->setDefaultResourceInfo(this->rootFileInfo_);
150        scriptModule_.reset(new LuaScriptModule(luaState_->getInternalLuaState()));
151        scriptModule_->setDefaultPCallErrorHandler(LuaState::ERROR_HANDLER_NAME);
152
153        // Create our own logger to specify the filepath
154        std::auto_ptr<CEGUILogger> ceguiLogger(new CEGUILogger());
155        ceguiLogger->setLogFilename(PathConfig::getLogPathString() + "cegui.log");
156        // set the log level according to ours (translate by subtracting 1)
157        ceguiLogger->setLoggingLevel(
158            static_cast<LoggingLevel>(OutputHandler::getInstance().getSoftDebugLevel("logFile") - 1));
159        this->ceguiLogger_ = ceguiLogger.release();
160
161        // Create the CEGUI system singleton
162        guiSystem_.reset(new System(guiRenderer_.get(), resourceProvider_, 0, scriptModule_.get()));
163
164        // Align CEGUI mouse with OIS mouse
165        guiSystem_->injectMousePosition((float)mousePosition.first, (float)mousePosition.second);
166
167        // Initialise the Lua framework and load the schemes
168        this->luaState_->doFile("InitialiseGUI.lua");
169
170        // Create the root nodes
171        this->rootWindow_ = CEGUI::WindowManager::getSingleton().createWindow("MenuWidgets/StaticImage", "AbsoluteRootWindow");
172        this->rootWindow_->setProperty("FrameEnabled", "False");
173        this->hudRootWindow_ = CEGUI::WindowManager::getSingleton().createWindow("DefaultWindow", "HUDRootWindow");
174        this->menuRootWindow_ = CEGUI::WindowManager::getSingleton().createWindow("DefaultWindow", "MenuRootWindow");
175        // And connect them
176        CEGUI::System::getSingleton().setGUISheet(this->rootWindow_);
177        this->rootWindow_->addChildWindow(this->hudRootWindow_);
178        this->rootWindow_->addChildWindow(this->menuRootWindow_);
179
180        // No background to start with (sets the alpha value to 0)
181        this->setBackgroundImage("");
182
183        // Set up the sheet manager in the Lua framework
184        this->luaState_->doFile("SheetManager.lua");
185    }
186
187    /**
188    @brief
189        Basically shuts down CEGUI (member smart pointers) but first unloads our Tolua modules.
190    */
191    GUIManager::~GUIManager()
192    {
193    }
194
195    void GUIManager::setConfigValues(void)
196    {
197        SetConfigValue(guiScheme_, GUIManager::defaultScheme_) .description("Changes the current GUI scheme.") .callback(this, &GUIManager::changedGUIScheme);
198    }
199
200    void GUIManager::changedGUIScheme(void)
201    {
202
203    }
204
205    /**
206    @brief
207        used to tick the GUI
208    @param time
209        clock which provides time value for the GUI System
210
211        Ticking the GUI means updating it with a certain regularity.
212        The elapsed time since the last call is given in the time value provided by the clock.
213        This time value is then used to provide a fluent animation of the GUI.
214    */
215    void GUIManager::preUpdate(const Clock& time)
216    {
217        assert(guiSystem_);
218        this->protectedCall(boost::bind(&CEGUI::System::injectTimePulse, _1, time.getDeltaTime()));
219    }
220
221    /**
222    @brief
223        Tells the GUIManager which SceneManager to use
224    @param camera
225        The current camera on which the GUI should be displayed on.
226
227        In fact the GUIManager needs the SceneManager and not the Camera to display the GUI.
228        This means the GUI is not bound to a camera but rather to the SceneManager.
229        Hiding the GUI when needed can therefore not be resolved by just NOT setting the current camera.
230    */
231    void GUIManager::setCamera(Ogre::Camera* camera)
232    {
233        this->camera_ = camera;
234        if (camera == NULL)
235            this->guiRenderer_->setTargetSceneManager(0);
236        else
237            this->guiRenderer_->setTargetSceneManager(camera->getSceneManager());
238    }
239
240    /**
241    @brief
242        Executes Lua code
243    @param str
244        reference to string object holding the Lua code which is to be executed
245    */
246    void GUIManager::executeCode(const std::string& str)
247    {
248        this->luaState_->doString(str, rootFileInfo_);
249    }
250
251    /** Loads a GUI sheet by Lua script
252    @param name
253        The name of the GUI (like the script name, but without the extension)
254    */
255    void GUIManager::loadGUI(const std::string& name)
256    {
257        this->executeCode("loadSheet(\"" + name + "\")");
258    }
259
260    /**
261    @brief
262        Displays specified GUI on screen
263    @param name
264        The name of the GUI
265    @param bHidePrevious
266        If true all displayed GUIs on the stack, that are below this GUI are hidden.
267    @param bNoInput
268        If true the GUI is transparent to input.
269
270        The function executes the Lua function with the same name in case the GUIManager is ready.
271    */
272    /*static*/ void GUIManager::showGUI(const std::string& name, bool bHidePrevious, bool bNoInput)
273    {
274        GUIManager::getInstance().executeCode("showMenuSheet(\"" + name + "\", " + multi_cast<std::string>(bHidePrevious) + ", " + multi_cast<std::string>(bNoInput) + ")");
275    }
276
277    /**
278    @brief
279        Hack-ish. Needed for GUIOverlay.
280    */
281    void GUIManager::showGUIExtra(const std::string& name, const std::string& ptr, bool bHidePrevious, bool bNoInput)
282    {
283        this->executeCode("showMenuSheet(\"" + name + "\", " + multi_cast<std::string>(bHidePrevious) + ", " + multi_cast<std::string>(bNoInput) + ", " + ptr + ")");
284    }
285
286    /**
287    @brief
288        Hides specified GUI.
289    @param name
290        The name of the GUI.
291    */
292    /*static*/ void GUIManager::hideGUI(const std::string& name)
293    {
294        GUIManager::getInstance().executeCode("hideMenuSheet(\"" + name + "\")");
295    }
296
297    /**
298    @brief
299        Toggles specified GUI.
300        If the GUI with the input name is already shown and on the top, it is hidden, else it is shown.
301    */
302    /*static*/ void GUIManager::toggleGUI(const std::string& name, bool bHidePrevious, bool bNoInput)
303    {
304        GUIManager::getInstance().executeCode("getGUIFirstActive(\"" + name + "\", " + multi_cast<std::string>(bHidePrevious) + ", " + multi_cast<std::string>(bNoInput) + ")");
305    }
306
307    /**
308    @brief
309        Helper method to toggle a specified GUI.
310        Is called by lua.
311    */
312    void GUIManager::toggleGUIHelper(const std::string& name, bool bHidePrevious, bool bNoInput, bool show)
313    {
314        if(show)
315            GUIManager::showGUI(name, bHidePrevious, bNoInput);
316        else
317            GUIManager::hideGUI(name);
318    }
319
320    const std::string& GUIManager::createInputState(const std::string& name, TriBool::Value showCursor, TriBool::Value useKeyboard, bool bBlockJoyStick)
321    {
322        InputState* state = InputManager::getInstance().createInputState(name);
323        if (!state)
324            return BLANKSTRING;
325
326        /* Table that maps isFullScreen() and showCursor to mouseExclusive
327        isFullscreen / showCursor | True  | False | Dontcare
328        ----------------------------------------------------
329        true                      | True  | True  | Dontcare
330        ----------------------------------------------------
331        false                     | False | True  | Dontcare
332        */
333        if (showCursor == TriBool::Dontcare)
334            state->setMouseExclusive(TriBool::Dontcare);
335        else if (GraphicsManager::getInstance().isFullScreen() || showCursor == TriBool::False)
336            state->setMouseExclusive(TriBool::True);
337        else
338            state->setMouseExclusive(TriBool::False);
339
340        if (showCursor == TriBool::True)
341            state->setMouseHandler(this);
342        else if (showCursor == TriBool::False)
343            state->setMouseHandler(&InputHandler::EMPTY);
344
345        if (useKeyboard == TriBool::True)
346            state->setKeyHandler(this);
347        else if (useKeyboard == TriBool::False)
348            state->setKeyHandler(&InputHandler::EMPTY);
349
350        if (bBlockJoyStick)
351            state->setJoyStickHandler(&InputHandler::EMPTY);
352
353        return state->getName();
354    }
355
356    void GUIManager::keyESC()
357    {
358        this->executeCode("keyESC()");
359    }
360
361    void GUIManager::setBackgroundImage(const std::string& imageSet, const std::string imageName)
362    {
363        if (imageSet.empty() || imageName.empty())
364            this->setBackgroundImage("");
365        else
366            this->setBackgroundImage("set: " + imageSet + " image: " + imageName);
367    }
368
369    void GUIManager::setBackgroundImage(const std::string& image)
370    {
371        if (image.empty())
372            this->rootWindow_->setProperty("Alpha", "0.0");
373        else
374            this->rootWindow_->setProperty("Alpha", "1.0");
375        this->rootWindow_->setProperty("Image", image);
376    }
377
378    /**
379    @brief
380        Method to navigate the GUI, by specifying the mode of navigation.
381    @param mode
382        The mode of navigation, at this point can be either 'up', 'down', 'left', 'right' or 'enter'.
383    */
384    /*static*/ void GUIManager::navigateGUI(const std::string& mode)
385    {
386        if(mode == NAVIGATE_UP)
387            GUIManager::getInstance().executeCode("navigateGUI(\"" + NAVIGATE_UP + "\")");
388        else if(mode == NAVIGATE_DOWN)
389            GUIManager::getInstance().executeCode("navigateGUI(\"" + NAVIGATE_DOWN + "\")");
390        else if(mode == NAVIGATE_LEFT)
391            GUIManager::getInstance().executeCode("navigateGUI(\"" + NAVIGATE_LEFT + "\")");
392        else if(mode == NAVIGATE_RIGHT)
393            GUIManager::getInstance().executeCode("navigateGUI(\"" + NAVIGATE_RIGHT + "\")");
394        else if(mode == NAVIGATE_ENTER)
395            GUIManager::getInstance().executeCode("navigateGUI(\"" + NAVIGATE_ENTER + "\")");
396    }
397
398    /**
399    @brief
400        Is called by lua to change whether there are any GUIs active at the moment.
401    @param active
402        Whether GUIs are active.
403    */
404    void GUIManager::guisActiveChanged(bool active)
405    {
406        if(this->GUIsActive_ == active)
407            return;
408        this->GUIsActive_ = active;
409        if(this->GUIsActive_)
410            ModifyConsoleCommand(__CC_navigateGUI_name).activate();
411        else
412            ModifyConsoleCommand(__CC_navigateGUI_name).deactivate();
413    }
414
415    void GUIManager::buttonPressed(const KeyEvent& evt)
416    {
417        this->protectedCall(boost::bind(&CEGUI::System::injectKeyDown, _1, evt.getKeyCode()));
418        this->protectedCall(boost::bind(&CEGUI::System::injectChar, _1, evt.getText()));
419    }
420
421    void GUIManager::buttonReleased(const KeyEvent& evt)
422    {
423        this->protectedCall(boost::bind(&CEGUI::System::injectKeyUp, _1, evt.getKeyCode()));
424    }
425
426    /**
427    @brief
428        Function receiving a mouse button pressed event.
429    @param id
430        ID of the mouse button which got pressed
431
432        This function is inherited by MouseHandler and injects the event into CEGUI.
433        It is for CEGUI to process the event.
434    */
435    void GUIManager::buttonPressed(MouseButtonCode::ByEnum id)
436    {
437        this->protectedCall(boost::bind(&CEGUI::System::injectMouseButtonDown, _1, convertButton(id)));
438    }
439
440    /**
441    @brief
442        Function receiving a mouse button released event.
443    @param id
444        ID of the mouse button which got released
445
446        This function is inherited by MouseHandler and injects the event into CEGUI.
447        It is for CEGUI to process the event.
448    */
449    void GUIManager::buttonReleased(MouseButtonCode::ByEnum id)
450    {
451        this->protectedCall(boost::bind(&CEGUI::System::injectMouseButtonUp, _1, convertButton(id)));
452    }
453
454    void GUIManager::mouseMoved(IntVector2 abs, IntVector2 rel, IntVector2 clippingSize)
455    {
456        this->protectedCall(boost::bind(&CEGUI::System::injectMousePosition, _1, (float)abs.x, (float)abs.y));
457    }
458
459    void GUIManager::mouseScrolled(int abs, int rel)
460    {
461        this->protectedCall(boost::bind(&CEGUI::System::injectMouseWheelChange, _1, (float)rel));
462    }
463
464    /**
465        @brief Indicates that the mouse left the application's window.
466    */
467    void GUIManager::mouseLeft()
468    {
469        this->protectedCall(boost::bind(&CEGUI::System::injectMouseLeaves, _1));
470    }
471
472    /**
473    @brief
474        converts mouse event code to CEGUI event code
475    @param button
476        code of the mouse button as we use it in Orxonox
477    @return
478        code of the mouse button as it is used by CEGUI
479
480        Simple conversion from mouse event code in Orxonox to the one used in CEGUI.
481     */
482    static inline CEGUI::MouseButton convertButton(MouseButtonCode::ByEnum button)
483    {
484        switch (button)
485        {
486        case MouseButtonCode::Left:
487            return CEGUI::LeftButton;
488
489        case MouseButtonCode::Right:
490            return CEGUI::RightButton;
491
492        case MouseButtonCode::Middle:
493            return CEGUI::MiddleButton;
494
495        case MouseButtonCode::Button3:
496            return CEGUI::X1Button;
497
498        case MouseButtonCode::Button4:
499            return CEGUI::X2Button;
500
501        default:
502            return CEGUI::NoButton;
503        }
504    }
505
506    /** Executes a CEGUI function normally, but catches CEGUI::ScriptException.
507        When a ScriptException occurs, the error message will be displayed and
508        the program carries on.
509    @remarks
510        The exception behaviour may pose problems if the code is not written
511        exception-safe (and you can forget about that in Lua). The program might
512        be left in an undefined state. But otherwise one script error would
513        terminate the whole program...
514    @note
515        Your life gets easier if you use boost::bind to create the object/function.
516    @param function
517        Any callable object/function that takes this->guiSystem_ as its only parameter.
518    @return
519        True if input was handled, false otherwise. A caught exception yields true.
520    */
521    template <typename FunctionType>
522    bool GUIManager::protectedCall(FunctionType function)
523    {
524        try
525        {
526            return function(this->guiSystem_);
527        }
528        catch (CEGUI::ScriptException& ex)
529        {
530            // Display the error and proceed. See @remarks why this can be dangerous.
531            COUT(1) << ex.getMessage() << std::endl;
532            return true;
533        }
534    }
535
536    /**
537    @brief
538        Subscribe the input function to the input event for the input window.
539        This is a helper to be used in lua, because subscribeScriptedEvent() doesn't work in lua.
540    @param window
541        The window for which the event is subscribed.
542    @param event
543        The type of event to which we subscribe.
544    @param function
545        The function that is called when the event occurs.
546    */
547    void GUIManager::subscribeEventHelper(CEGUI::Window* window, const std::string& event, const std::string& function)
548    {
549        window->subscribeScriptedEvent(event, function);
550    }
551
552    /**
553    @brief
554        Set the input tooltip text for the input ListboxItem.
555    @param item
556        The ListboxItem for which the tooltip should be set.
557    @param tooltip
558        The tooltip text that should be set.
559    */
560    void GUIManager::setTooltipTextHelper(CEGUI::ListboxItem* item, const std::string& tooltip)
561    {
562        item->setTooltipText(tooltip);
563    }
564
565    /**
566    @brief
567        Set whether the tooltips for the input Listbox are enabled.
568    @param listbox
569        The Listbox for which to enable (or disable) tooltips.
570    @param enabled
571        Whether to enable or disabel the tooltips.
572    */
573    void GUIManager::setItemTooltipsEnabledHelper(CEGUI::Listbox* listbox, bool enabled)
574    {
575        listbox->setItemTooltipsEnabled(enabled);
576    }
577
578    /**
579        @brief Callback of window event listener, called if the window is resized. Sets the display size of CEGUI.
580    */
581    void GUIManager::windowResized(unsigned int newWidth, unsigned int newHeight)
582    {
583        this->guiRenderer_->setDisplaySize(CEGUI::Size((float)newWidth, (float)newHeight));
584    }
585
586    /**
587        @brief Notify CEGUI if the windows loses the focus (stops highlighting of menu items, etc).
588    */
589    void GUIManager::windowFocusChanged(bool bFocus)
590    {
591        if (!bFocus)
592            this->mouseLeft();
593    }
594
595}
Note: See TracBrowser for help on using the repository browser.