Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/GUIManager.cc @ 8143

Last change on this file since 8143 was 8079, checked in by landauf, 14 years ago

merged usability branch back to trunk

incomplete summary of the changes in this branch:

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