Skip to main content

Rendering UI

A plugin that draws its own UI implements IRenderable in addition to being a plugin, and exports with the render-enabled macro. The host calls Render() once per frame with an active ImGui frame, through the abstract UIContextno imgui.h symbols cross the boundary.

Making a plugin render

Implement IRenderable::Render alongside PluginBase, and set .renderOrder in the export descriptor (.render defaults to true, so a rendering plugin never mentions it):

#include <framelift/core.h>
#include <framelift/ui.h>

class MyPanel : public PluginBase, public IRenderable
{
bool open_ = true;

protected:
const char* PluginName() const override { return "MyPanel"; }

public:
void Render(int windowW, int windowH, UIContext& ui) override
{
if (!open_) return;

ui.SetNextWindowPos({20.f, 20.f}, UI::Cond::FirstUseEver);
ui.SetNextWindowSize({280.f, 160.f}, UI::Cond::FirstUseEver);
if (ui.Begin("My Panel", &open_, UI::WindowFlags::None))
{
ui.Text("Hello from a plugin panel!");
if (ui.Button("Close"))
open_ = false;
}
ui.End();
}

// Request a frame only when something is animating; return false when static
// to let the host idle the GPU.
bool NeedsRedraw() const override { return false; }
};

FRAMELIFT_PLUGIN_EXPORT(MyPanel, {
.name = "MyPanel",
.version = {1, 0, 0},
.renderOrder = 50,
})

The .renderOrder field controls draw order relative to other plugins — lower numbers render first (further back). The on-screen overlay, playlist, and history panels each pick an order; choose yours to sit where you want in the stack.

NeedsRedraw

FrameLift does not redraw every frame unconditionally — it renders when there are events or when something requests it. Return true from NeedsRedraw() while your UI is animating (a sliding panel, a fading notification) so you keep getting frames; return false when static to avoid unnecessary GPU work.

The UIContext API

UIContext is an abstract facade over Dear ImGui. The method names mirror ImGui, so existing ImGui knowledge transfers directly. Highlights:

AreaMethods
WindowsBegin / End, BeginChild / EndChild, SetNextWindowPos/Size/BgAlpha
TextText, TextColored, TextDisabled
WidgetsButton, Checkbox, Selectable, SliderInt, SliderFloat, InputText
LayoutSameLine, Separator, Dummy, SetCursorPosX/Y, SetNextItemWidth
StylePushStyleVar / PopStyleVar, PushStyleColor / PopStyleColor
Menus & popupsBeginMenu/EndMenu, MenuItem, OpenPopup, BeginPopupModal, EndPopup
Custom drawingGetWindowDrawList / GetBackgroundDrawList / GetForegroundDrawListDrawList
TexturesLoadTexture, LoadTextureFromMemory → handle for DrawList::AddImage
Input stateGetMousePos, IsMouseClicked, IsMouseDown, IsItemHovered

POD vector/color types (UI::Vec2, UI::Color32, UI::Color4f) and enums (UI::Cond, UI::WindowFlags, UI::StyleVar, …) live in <framelift/ui/UI.h>, pulled in by the <framelift/ui.h> umbrella.

InputText with std::string

The virtual InputText takes a caller-owned char* buffer. UIContext also provides a non-virtual std::string& overload that compiles into your plugin and bridges to it — convenient for settings forms:

std::string title_;
// ...
ui.InputText("Title", title_); // grows/reads the std::string for you

Custom drawing

For shapes, lines, images, and text outside the widget system, get a DrawList and issue commands. The reference is valid only for the current Render call:

DrawList& dl = ui.GetWindowDrawList();
dl.AddRectFilled({0, 0}, {100, 40}, UI::MakeColor32(0, 0, 0, 160), 6.f);
dl.AddText({8, 10}, UI::MakeColor32(255, 255, 255, 255), "overlay");

Load a texture once (the host owns the GPU resource) and draw it with AddImage using the returned handle.