1 // SuperTuxKart - a fun racing game with go-kart 2 // Copyright (C) 2010-2015 Marianne Gagnon 3 // 4 // This program is free software; you can redistribute it and/or 5 // modify it under the terms of the GNU General Public License 6 // as published by the Free Software Foundation; either version 3 7 // of the License, or (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with this program; if not, write to the Free Software 16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 18 19 #ifndef HEADER_SCREEN_HPP 20 #define HEADER_SCREEN_HPP 21 22 #include <map> 23 #include <string> 24 #include <typeinfo> 25 #include "utils/cpp2011.hpp" 26 27 #include <irrString.h> 28 #include <IXMLReader.h> 29 30 namespace irr 31 { 32 namespace gui { class IGUIElement; } 33 } 34 using namespace irr; 35 36 #include "config/stk_config.hpp" 37 #include "guiengine/abstract_top_level_container.hpp" 38 #include "guiengine/engine.hpp" 39 #include "guiengine/event_handler.hpp" 40 #include "guiengine/widget.hpp" 41 #include "input/input.hpp" 42 #include "utils/leak_check.hpp" 43 #include "utils/ptr_vector.hpp" 44 45 #include <functional> 46 47 /** 48 * \ingroup guiengine 49 */ 50 namespace GUIEngine 51 { 52 /** 53 * \brief Declares a class to be a singleton. 54 * Normally, all screens will be singletons. 55 * \ingroup guiengine 56 */ 57 template<typename SCREEN> 58 class ScreenSingleton 59 { 60 protected: 61 static SCREEN* singleton; 62 public: 63 ~ScreenSingleton()64 ~ScreenSingleton() 65 { 66 singleton = NULL; 67 } 68 getInstance()69 static SCREEN* getInstance() 70 { 71 if (singleton == NULL) 72 { 73 singleton = new SCREEN(); 74 std::function<SCREEN*()> new_screen_function = []() 75 { return ScreenSingleton::getInstance(); }; 76 singleton->setScreenPointerFunction(new_screen_function); 77 GUIEngine::addScreenToList(singleton); 78 } 79 80 return singleton; 81 } 82 }; 83 template <typename SCREEN> SCREEN* 84 ScreenSingleton<SCREEN>::singleton = nullptr; 85 86 /** 87 * \brief Represents a single GUI screen. 88 * Mainly responsible of its children widgets; Screen lays them 89 * out, asks them to add themselves, asks them to remove themselves, etc. 90 * 91 * Also initiates the read of GUI files, even though most of that work is 92 * done in "screen_loader.cpp" 93 * 94 * \ingroup guiengine 95 */ 96 class Screen : public AbstractTopLevelContainer 97 { 98 protected: 99 /** True if this screen is resizable 100 */ 101 bool m_resizable; 102 103 bool m_throttle_FPS; 104 private: 105 /** True if the race (if it is running) should be paused when this 106 * screen is shown. The RaceResultGUI uses this to leave the race 107 * running while it is being shown. */ 108 bool m_pause_race; 109 110 bool m_loaded; 111 112 /** Will be called to determine if the 3D scene must be rendered when 113 * at this screen. 114 */ 115 bool m_render_3d; 116 117 /** When set to true it updates the screen even if modal dialog is 118 * opened 119 */ 120 bool m_update_in_background; 121 122 /** to catch errors as early as possible, for debugging purposes only */ 123 unsigned int m_magic_number; 124 125 unsigned m_width, m_height; 126 127 friend class Skin; 128 129 std::string m_filename; 130 /** For runtime screen reloading without template */ 131 std::function<Screen*()> m_screen_func; 132 public: 133 134 LEAK_CHECK() 135 136 /** 137 * \ingroup guiengine 138 * \brief Loads a GUI screen from its XML file. 139 * 140 * Builds a hierarchy of Widget objects whose contents are a direct 141 * transcription of the XML file, with little analysis or layout 142 * performed on them. 143 */ 144 static void parseScreenFileDiv(irr::io::IXMLReader* xml, 145 PtrVector<Widget>& append_to, 146 irr::gui::IGUIElement* parent = NULL); 147 148 /** Save the function before GUIEngine::clearScreenCache, call it after 149 * to get the new screen instance pointer 150 */ getNewScreenPointer() const151 std::function<Screen*()> getNewScreenPointer() const { return m_screen_func; } 152 setScreenPointerFunction(const std::function<Screen * ()> & f)153 void setScreenPointerFunction(const std::function<Screen*()>& f) { m_screen_func = f; } 154 155 Screen(bool pause_race=true); 156 157 Screen(const char* filename, bool pause_race=true); 158 159 virtual ~Screen(); 160 operator ==(const char * filename) const161 bool operator ==(const char* filename) const { return m_filename == filename; } 162 163 void loadFromFile(); 164 165 /** \return whether this screen is currently loaded */ isLoaded() const166 bool isLoaded() const { return m_loaded; } 167 throttleFPS() const168 bool throttleFPS() const { return m_throttle_FPS; } 169 170 void addWidgets(); 171 172 void calculateLayout(); 173 174 void manualAddWidget(Widget* w); 175 176 void manualRemoveWidget(Widget* w); 177 178 /** When set to true it updates the screen even if modal dialog is 179 * opened 180 */ setUpdateInBackground(bool value)181 void setUpdateInBackground(bool value) {m_update_in_background = value;} getUpdateInBackground()182 bool getUpdateInBackground() {return m_update_in_background;} 183 184 /** \return the name of this menu (which is the name of the file) */ getName() const185 const std::string& getName() const { return m_filename; } 186 187 virtual void unload(); 188 189 /** Will be called to determine if the 3D scene must be rendered when 190 * at this screen 191 */ needs3D()192 bool needs3D() { return m_render_3d; } 193 194 /** \brief Invoke this method for screens that use a 3D scene as 195 * background. 196 * 197 * (if this method is not invoked with 'true' as parameter, the menu 198 * background will be rendered instead). 199 * 200 * \note To create the 3D background, use the facilities provided by 201 * the irrLicht scene manager, this class will not set up any 202 * 3D scene. 203 */ setNeeds3D(bool needs3D)204 void setNeeds3D(bool needs3D) { m_render_3d = needs3D; } 205 206 /** 207 * \brief Callback invoked when loading this menu. 208 * 209 * \pre Children widgets of this menu have been created by the time 210 * this callback is invoked. 211 * \note This method is not called everytime the screen is shown. 212 * Screen::init is. 213 * Use this method for persistent setup code (namely, that 214 * deals with setting up children widget objects and needs not 215 * be done everytime we visit the screen). 216 * \note A Screen object instance may be unloaded then loaded back. 217 * This method might thus be called more than once in the 218 * lifetime of a Screen object, however there will always 219 * be an 'unload' event in-between calls to this method. 220 */ 221 virtual void loadedFromFile() = 0; 222 223 /** 224 * \brief Callback invoked when this screen is being unloaded. 225 * Override this method in children classes if you need to be 226 * notified of this. 227 * \note A Screen object instance may be unloaded then loaded back 228 * at will. 229 * \note An unloaded Screen object does not have its children widgets 230 * anymore, it only retains its members (most importantly the 231 * path to its GUI file) so that it can be loaded back later. 232 */ unloaded()233 virtual void unloaded() {} 234 235 /** 236 * \brief Optional callback invoked very early, before widgets have 237 * been added (contrast with init(), which is invoked afer 238 * widgets were added) 239 */ beforeAddingWidget()240 virtual void beforeAddingWidget() {} 241 242 /** 243 * \brief Callback invoked when entering this menu (after the 244 * widgets have been added). 245 * 246 * \note The same instance of your object may be entered/left more 247 * than once, so make sure that one instance of your object 248 * can be used several times if the same screen is visited 249 * several times. 250 */ 251 virtual void init(); 252 253 /** Displays this screen bu pushing it onto the stack of screen 254 * in the state manager. */ 255 void push(); 256 257 /** 258 * \brief Callback invoked before leaving this menu. 259 * 260 * \note The same instance of your object may be entered/left more 261 * than once, so make sure that one instance of your object can 262 * be used several times if the same screen is visited several 263 * times. 264 */ 265 virtual void tearDown(); 266 267 /** 268 * \brief Called when escape is pressed. 269 * \return true if the screen should be closed, 270 * false if you handled the press another way 271 */ onEscapePressed()272 virtual bool onEscapePressed() { return true; } 273 274 /** 275 * \brief will be called everytime something happens. 276 * 277 * Events are generally a widget state change. In this case, a pointer 278 * to the said widget is passed along its name, so you get its new 279 * state and/or act. There are two special events, passed with a NULL 280 * widget, and which bear the names "init" and "tearDown", called 281 * respectively when a screen is being made visible and when it's being 282 * left, allowing for setup/clean-up. 283 */ 284 virtual void eventCallback(Widget* widget, const std::string& name, const int playerID) = 0; 285 286 /** 287 * \brief optional callback you can override to be notified at every frame. 288 */ onUpdate(float dt)289 virtual void onUpdate(float dt) { }; 290 291 /** 292 * \brief optional callback you can override to be notified at every frame. 293 */ onDraw(float dt)294 virtual void onDraw(float dt) { }; 295 296 /** 297 * \return which music to play at this screen 298 */ getMusic() const299 virtual MusicInformation* getMusic() const { return stk_config->m_title_music; } 300 301 /** 302 * \return which music to play at this screen, if accessed in "in-game-menu" mode 303 */ getInGameMenuMusic() const304 virtual MusicInformation* getInGameMenuMusic() const { return NULL; } 305 getWidth()306 virtual int getWidth() { return m_width; } 307 getHeight()308 virtual int getHeight() { return m_height; } 309 isResizable() const310 virtual bool isResizable() const { return m_resizable; } 311 /** 312 * \brief Override this if you need to be notified of player actions 313 * in subclasses. 314 */ filterActions(PlayerAction action,int deviceID,const unsigned int value,Input::InputType type,int playerId)315 virtual EventPropagation filterActions(PlayerAction action, 316 int deviceID, 317 const unsigned int value, 318 Input::InputType type, 319 int playerId) 320 { return EVENT_LET; } 321 322 /** Callback you can use if you want to know when the user pressed 323 * on a disabled ribbon item. 324 * (the main use I see for this is to give feedback) 325 */ onDisabledItemClicked(const std::string & item)326 virtual void onDisabledItemClicked(const std::string& item) {} 327 328 /** 329 * \brief Override this if you need to be notified of raw input in 330 * subclasses. 331 */ filterInput(Input::InputType type,int deviceID,int btnID,int axisDir,int value)332 virtual void filterInput(Input::InputType type, 333 int deviceID, 334 int btnID, 335 int axisDir, 336 int value) {} 337 338 /** Callback that gets called when a dialog is closed. 339 * Can be used to set focus for instance. 340 */ onDialogClose()341 virtual void onDialogClose() {} 342 343 /** Callback called when focus changes */ onFocusChanged(Widget * previous,Widget * focus,int playerID)344 virtual void onFocusChanged(Widget* previous, Widget* focus, int playerID) {} 345 }; 346 347 class CutsceneScreen : public Screen 348 { 349 public: CutsceneScreen(const char * name)350 CutsceneScreen(const char* name) : Screen(name, false) 351 { 352 setNeeds3D(true); 353 m_throttle_FPS = false; 354 } 355 356 virtual void onCutsceneEnd() = 0; 357 }; 358 } 359 360 #endif 361