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 UIContext — no 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:
| Area | Methods |
|---|---|
| Windows | Begin / End, BeginChild / EndChild, SetNextWindowPos/Size/BgAlpha |
| Text | Text, TextColored, TextDisabled |
| Widgets | Button, Checkbox, Selectable, SliderInt, SliderFloat, InputText |
| Layout | SameLine, Separator, Dummy, SetCursorPosX/Y, SetNextItemWidth |
| Style | PushStyleVar / PopStyleVar, PushStyleColor / PopStyleColor |
| Menus & popups | BeginMenu/EndMenu, MenuItem, OpenPopup, BeginPopupModal, EndPopup |
| Custom drawing | GetWindowDrawList / GetBackgroundDrawList / GetForegroundDrawList → DrawList |
| Textures | LoadTexture, LoadTextureFromMemory → handle for DrawList::AddImage |
| Input state | GetMousePos, 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.