Widget Tutorial

This tutorial will teach you how to use Widgets effectively.


Preparations

Set up the Development Environment, download zenilib, and learn how to build everything if you haven't already.

The Basics

  1. Open zenilib/jni/application/bootstrap.cpp.
  2. class Play_State is currently inheriting from Gamestate_Base. To make our lives easy (for now), change it to inherit from Widget_Gamestate:
    class Play_State : public Widget_Gamestate {
    
  3. Your compiler will complain that there is no default constructor for Widget_Gamestate, and this is true. We must tell the Widget_Gamestate base class what resolution we want to pretend to use:
      Play_State()
        : Widget_Gamestate(std::make_pair(Point2f(0.0f, 0.0f), Point2f(800.0f, 600.0f)), true)
      {
    
    Note that the Widget_Gamestate constructor takes in the same arguments as Video::set_2d(...).
  4. Delete the on_push and on_pop functions.

    So long as the Widget_Gamestate functions on_key, on_mouse_button, on_mouse_motion, perform_logic, and render are called correctly, Widget_Gamestate will do all the hard work. This will happen automatically so long as these functions are not overriden. If you choose to override them, you will have to call the functions manually.

    Widget_Gamestate functions on_push and on_pop do some work as well, to ensure that the cursor is both visible and not grabbed. You can, of course, override (and disable) this behavior.
  5. All Widgets are created differently. To create a simple text button, we derive from Text_Button and (optionally) create an instance at the same time:
      class Pop_Button : public Text_Button {
      } pop_button;
    
    The plan is to make a button that simply pops the Gamestate stack.
  6. We must pass some parameters to the constructor for Text_Button:
      public:
        Pop_Button()
          : Text_Button(Point2f(50.0f, 50.0f), Point2f(250.0f, 100.0f), "system_36_800x600", "Pop State")
        {
        }
    
    We tell the button to use a font that has been created for the specific resolution we're pretending to use.
  7. Then we just lend this Widget to our collection of Widgets in Play_State(), our constructor:
        m_widgets.lend_Widget(pop_button);
    
  8. Finally, to add functionality to our Pop_Button, we override its on_accept() function:
        void on_accept() {
          get_Game().pop_state();
        }
    

Descendents of Widget_Gamestate

  1. Note that, if we were to inherit from this state, we could change various properties of pop_button fairly easily. So long as pop_button is not private, we can add the following code to our derived class constructor to change the size and location of the button:
        pop_button.set_upper_left(Point2f(300.0f, 200.0f));
        pop_button.set_lower_right(Point2f(500.0f, 400.0f));
    
  2. Alternatively, we can easily remove the button entirely (though not from memory):
        m_widgets.unlend_Widget(pop_button);
    
  3. One final trick is that you can render whatever you like in a custom render() function, including arbitrary calls to Video::set_2d(...), before rendering the widgets:
      void render() {
        Gamestate_Base::render();
        Widget_Gamestate::render();
      }
    
    Here I'm calling the default rendering function before rendering the Widgets, but you could render whatever you like instead.

I encourage you to look at the implementation of Widget_Gamestate or Title_State to get more ideas about what you can do.