Creating GUI tabs with a C++ mod

UE4SS already includes the ImGui library to render its console GUI, built from the UE4SS-RE/imgui repo. Refer to ImGui documentation in that repo on how to use ImGui-specific classes and methods for rendering actual buttons and textboxes and other window objects.

This guide will show how you create custom tabs for the GUI with a C++ mod, and the guide will take the form of comments in the code example below:

#include <Mod/CppUserModBase.hpp>
#include <UE4SSProgram.hpp>

class MyAwesomeMod : public RC::CppUserModBase
{
private:
    int m_private_number{33};
    std::shared_ptr<GUI::GUITab> m_less_safe_tab{};

public:
    MyAwesomeMod() : CppUserModBase()
    {
        ModName = STR("MyAwesomeMod");
        ModVersion = STR("1.0");
        ModDescription = STR("This is my awesome mod");
        ModAuthors = STR("UE4SS Team");
        
        // It's critical that you enable ImGui before you create your tab.
        // If you don't do this, a crash will occur as soon as ImGui tries to render anything in your tab.
        UE4SS_ENABLE_IMGUI()
        
        // The 'register_tab' function will tell UE4SS to render a tab.
        // Tabs registered this way will be automatically cleaned up when this C++ mod is destructed.
        // The first param is the display name of your tab.
        // The second param is a callback that UE4SS will use to render the contents of the tab.
        // The param to the callback is a pointer to your mod.
        register_tab(STR("My Test Tab"), [](CppUserModBase* instance) {
            // In this callback, you can start rendering the contents of your tab with ImGui. 
            ImGui::Text("This is the contents of the tab");
            
            // You can access members of your mod class with the 'instance' param.
            auto mod = dynamic_cast<MyAwesomeMod*>(instance);
            if (!mod)
            {
                // Something went wrong that caused the 'instance' to not be correctly set.
                // Let's abort the rest of the function so that you don't access an invalid pointer.
                return;
            }
            
            // You can access both public and private members.
            mod->render_some_stuff(mod->m_private_number);
        });
        
        // The 'UE4SSProgram::add_gui_tab' function is another way to tell UE4SS to render a tab.
        // This way of registering a tab will make you responsible for cleaning up the tab when your mod destructs.
        // Failure to clean up the tab on mod destruction will result in a crash.
        // It's recommended that you use 'register_tab' instead of this function.
        m_less_safe_tab = std::make_shared<GUI::GUITab>(STR("My Less Safe Tab"), [](CppUserModBase* instance) {
            // This callback is identical to the one used with 'register_tab' except 'instance' is always nullptr.
            ImGui::Text("This is the contents of the less safe tab");
        });
        UE4SSProgram::get_program().add_gui_tab(m_less_safe_tab);
    }

    ~MyAwesomeMod() override
    {
        // Because you created a tab with 'UE4SSProgram::add_gui_tab', you must manually remove it.
        // Failure to remove the tab will result in a crash.
        UE4SSProgram::get_program().remove_gui_tab(m_less_safe_tab);
    }
    
    auto render_some_stuff(int Number) -> void
    {
        auto calculated_value = Number + 1;
        ImGui::Text(std::format("calculated_value: {}", calculated_value).c_str());
    }
};

#define MY_AWESOME_MOD_API __declspec(dllexport)
extern "C"
{
    MY_AWESOME_MOD_API RC::CppUserModBase* start_mod()
    {
        return new MyAwesomeMod();
    }

    MY_AWESOME_MOD_API void uninstall_mod(RC::CppUserModBase* mod)
    {
        delete mod;
    }
}