Skip to main content

Keybinds

A plugin can bind keyboard shortcuts to actions. Bindings are stored as strings (e.g. "Ctrl+F;F2") so users can rebind them, and they show up in the shared keybind UI. There are three pieces:

  1. A binding string member you load/save like any other setting.
  2. A keybind entry registered so the UI can display and edit it.
  3. The actual binding to a handler, done in OnBindHotkeys.

Storing the binding string

Keybind strings live in the shared [keybinds] section, not your own. Use LoadKeybinds/SaveKeybinds and namespace your keys with PrefixedKey so they never collide with another plugin's:

class MyPlugin : public PluginBase
{
std::string toggleKey_ = "Ctrl+M";

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

void LoadKeybinds(IPluginSettings& ks) override
{
toggleKey_ = ks.GetString(PrefixedKey("toggle").c_str(), "Ctrl+M");
}

void SaveKeybinds(IPluginSettings& ks) override
{
ks.SetString(PrefixedKey("toggle").c_str(), toggleKey_.c_str());
}
};

PrefixedKey("toggle") yields "MyPlugin.toggle".

Registering the entry for the UI

Override RegisterKeybinds and register an entry backed by your string member. The <framelift/ContextHelpers.h> overload takes the std::string& directly and writes the get/set glue for you:

#include <framelift/ContextHelpers.h>

void RegisterKeybinds(IPluginContext& ctx) override
{
framelift::RegisterKeybindEntry(ctx, "Toggle my panel", "MyPlugin.toggle", toggleKey_);
}
  • label — human-readable text shown in the keybind list.
  • actionName — a stable identifier (namespaced).
  • bindStr — the member the UI reads from and writes back to.

Binding to a handler

Override OnBindHotkeys. It runs once, after all plugins are installed, so you can safely reference services other plugins registered. Use the framelift::Bind helpers, which accept a lambda:

#include <framelift/HotkeyHelpers.h>

void OnBindHotkeys(Hotkeys& keys) override
{
// Named, rebindable binding driven by the stored string.
framelift::Bind(keys, "MyPlugin.toggle", toggleKey_, [this] {
showPanel_ = !showPanel_;
});
}

Bind has overloads for:

  • Named bindings from a bind-list string ("Ctrl+F;F2" — first entry is the rebindable name, the rest are aliases). This is what pairs with a registered keybind entry.
  • Unnamed bindings to a fixed Key (+ optional Mod) from <framelift/AppEvent.h>, e.g. framelift::Bind(keys, Keys::Space, [this] { ... }).

Each binding heap-allocates its closure and registers a cleanup callback, so the memory is released automatically when your plugin unloads.

Handling raw key events instead

For one-off keyboard handling that does not need to be user-rebindable, override OnKeyDownEvent instead of registering a hotkey. Return true to consume the event and stop further dispatch:

bool OnKeyDownEvent(const AppEvent& e) override
{
const auto& k = e.AsKey();
if (k.key == Keys::Escape && k.mods == Mod::None)
{
Close();
return true; // consumed
}
return false;
}

See AppEvent for the Key/Mod constants and the payload accessors.