# T3: Overview of Gadget Features

We have already mentioned the HIRO_DRAW library in the T2: Creating Custom Modules. Lets now look at more possibilities to render your data beyond simple geometries.

Important

Whenever you change the visualization without user interaction, method `Gadget::DrawOnNextUpdate` should be called to notify the system about the change. The method is required when you are using the Optimized rendering setting, otherwise, the system might not notice the change and the screen will not be redrawn.

## Camera Focus

Have you seen and tryed the button with magnifying glass icon in the Gadget GUI titlebar? That is the camer focus feature. It allows users to immediately set camera pose, so that it views the data in scene correctly. Override the method `Gadget::FocusCamera` in order to define the custom pose of camera.

## GUI Generator

Another functionality that HIRO offers for custom gadgets, is a simple creation of an interactive GUI. One of the possible uses is manipulation with visual settings of rendered objects.

First of all, the function `Gadget::GenerateGui` must be overridden for your derived gadget class. The method is called automatically by the system and `gui` parameter is a reference to `GuiGenerator` object that corresponds to this currently this gadget. Options in the sidebar panel can be modified using this reference. Explore possibilities of each GUI element in namespace hiro::gui documentation and use them to your advantage.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    void MyGadget::GenerateGui(hiro::GuiGenerator &gui)
    {
      hiro::Gadget::GenerateGui(gui);
      gui.AddCheckbox("Material visible")  // creates a checkbox with specified caption
      ->SetLink(&is_mat_visible_)          // makes the variable to reflect the checkbox state
      ->Set(true)                          // changes the state of checkbox to true
      ->Subscribe([](const hiro::gui::Checkbox *checkbox) {
        // this lambda function will be invoked every time a checkbox state is changed
      });
    };

It is possible to set up an element so that it will be visible only under some conditions. This can be done using `SetConditionFunc` or `SetConditionBool` methods.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    ...
      // Class MyGadget can has a boolean member variable called "is_mat_visible_".
      // There are two ways how to set element to be visible only when the value is true.

      gui.AddNumericInt("Material id 1")
      ->SetConditionBool(&is_mat_visible_);

      gui.AddNumericInt("Material id 2")
      ->SetConditionFunc([this](){
        return is_mat_visible_;
      });
    ...

Method `GenerateGui` is called automatically, and cannot be called multiple times. If you wish to re-generate your GUI, use method `Resource::ResetGadgets` - it refreshes all gadgets created by the resource that called this method.

Hint

During the creation of a gadget object, the following functions are called in this order:

- Constructor

- `Initialize`

- `GenerateGui`

When overriding method `Initialize`, be sure to also call superclass method `Gadget::Initialize` at the beginning. Otherwise, some features of your `Gadget` will not work properly.

## Text Printing

HIRO provides a very simple interface enabling to render any 2D text in the application window. The `Gadget` class has a `RenderTexts` method. You can simply override it and feed your own text to be rendered every frame.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    ...
    void MyGadget::RenderTexts(hiro::draw::TextRenderer &t_renderer)
    {
      hiro::Gadget::RenderTexts(t_renderer);
      t_renderer.SetAlignment(hiro::draw::TextAlignment::center);
      t_renderer.SetColor(cogs::color::RED);

      const auto proj = GetProjectionParams();
      t_renderer.Print( {proj.width / 2, proj.height / 2}, "Your text here!");
      DrawOnNextUpdate();
    }
    ...

The method `RenderTexts` is automatically called by the engine on every draw, passing in a `TextRenderer` reference. When overriding a method, make sure to call the parent’s method as well. You can set various attributes, such as text alignment and color. Finally, call the `Print` method providing a 2D position vector and your text.

Method `TextRenderer::Print` takes text position as the first parameter. The position is relative to the top left corner of the viewarea in which the gadget exists.

Hint

Method `GetProjectionParams` returns `ProjectionParams` structure that describes the projection used in current viewarea. It holds several useful information about viewport, camera view, and projection and can be used for example to cast camera raya via `ProjectionParams::CastRay`.

## Events

You may find yourself in a situation, when you wish to affect the renderers and their styles by the events produced by user. Gadget class provides several options for processing input such as:

- keyboard key press/release

- mouse button press/release

- mouse wheel rotate

- mouse move

- view resize

Override theese methods to define your custom behavior. This example shows processing keyboard key press event by overriding the method `Gadget::KeyPressed`.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    ...
    hiro::EventStatus MyGadget::KeyPressed (
      hiro::Key key,
      const hiro::ModKeys &mods,
      hiro::EventStatus status
    )
    {
      if (hiro::Gadget::KeyPressed(key, mods, status) == hiro::EventStatus::processed)
      {
        return hiro::EventStatus::processed;
      }
      if (status == hiro::EventStatus::processed)
      {
        return hiro::EventStatus::unprocessed;
      }
      if (key == hiro::Key::space)
      {
        std::cout << "Space key was pressed!" << std::endl;
        return hiro::EventStatus::processed;
      }
      return hiro::EventStatus::unprocessed;
    }
    ...

The `KeyPressed` method comes with various parameters. The parameter `key` simply describes which key was pressed by the user. You can use the `const hiro::ModKeys &mods` parameter to check if any modifier keys, such as CTRL or Shift, were pressed. Finally, the `status` is a state flag, specifying whether this event was already processed. The good manners are, you should not process the event when it has status `hiro::EventStatus::processed` since the event was already processed by another object, however, there are exceptions. As before, always remember to call the parent’s method when overriding.

Hint

The event functions should return `hiro::EventStatus::processed`, if the event was processed in the function successfully. This ensures the correct status within the engine.

## Auto-save System

When debugging an algorithm, changes in the code may be introduced frequently and the application is started over and over. Also during application runtime, users can adjust rendering, show/hide some elements or change parameter values. To spare debugging time, it is recommended to store the state of gadget, that can be restored after the next application startup. Lucky you! HIRO offers a save/load system that is very simple to use.

If you wish to use an auto-save feature, call `Gadget::LoadState` in your `Initialization` method. This does two things. First, it immediately loads the previously-stored state file and invokes `ReadFromStream` method. Second, it tells the system that you wish to use the auto-save feature and from now on, `WriteToStream` will be triggered whenever a change to the generated GUI is introduced.

To define custom state values that should be stored/loaded, override `ReadFromStream` and `WriteToStream` methods. Be sure to check the superclass load method as shown in the example below. The boolean result of the method tells the system whether the data has been loaded correctly.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    ...
    void MyGadget::Initialize()
    {
      Gadget::Initialize();
      LoadState();
    }

    uint32_t MyGadget::STATE_VERSION = 0;

    bool MyGadget::ReadFromStream(std::istream &str)
    {
      // Read super class state. If it fails, do not continue.
      if (!Gadget::ReadFromStream(str))
      return false;

      // Read style. If it fails, do not continue.
      if (!style_->ReadFromStream(str))
      return false;

      // Check if the file data is not deprecated. If it is, do not continue.
      if (!ReadStateVersion(str, STATE_VERSION))
      return false;

      // Read custom properties.
      std_ext::Read(is_mat_visible_, str);

      // Everything went successfuly.
      return true;
    }

    void MyGadget::WriteToStream(std::ostream &str)
    {
      // This method should reflect reading, except... it is writing.
      Gadget::WriteToStream(str);
      style_->WriteToStream(str);
      WriteStateVersion(str, STATE_VERSION);
      std_ext::Write(is_mat_visible_, str);
    }
    ...

Constant `STATE_VERSION` is a custom number that defines the version of your read/write code. It is recommended to use `ReadStateVersion` and `WriteStateVersion` to ensure that the values you read will not be corrupted when you read from file that is stale. You should increase the `STATE_VERSION` number every time the changes are introduced to the read/write methods.

Attention

When changes were introduced to the `ReadFromStream`/`WriteToStream` method a common issue with state saving can be caused by the following:

- changes were not reflected correctly in the other of the two methods, causing the inconsistency between the read and write code

- `STATE_VERSION` was not increased, causing the stale old files to be considered new

Therefore, always take care when changing `ReadFromStream`/`WriteToStream` methods.