Shortcuts

Studio keyboard shortcuts are backed by a central command registry (editor.shortcuts) rather than hardcoded key checks scattered across panels. Every shortcut is rebindable, conflicts are detected automatically, and menu items show their live binding.


Default shortcuts

Command Default Context
Undo Ctrl+Z Global
Redo Ctrl+Shift+Z, Ctrl+Y Global
Copy Ctrl+C Global
Cut Ctrl+X Global
Paste Ctrl+V Global
Play / Stop Ctrl+P Global
Save Ctrl+S Global
Save All Ctrl+Shift+S Global
Close Tab Ctrl+W Global
Next / Previous Tab Ctrl+Tab / Ctrl+Shift+Tab Global
Move / Rotate / Scale tool W / E / R Scene View (hover)
Focus Selection F Scene View (hover)
Rename F2 Hierarchy / Asset Browser (focused)
Delete Delete, Backspace Hierarchy / Asset Browser (focused)

"Global" commands fire regardless of which panel is focused. Panel-scoped commands (Scene View's tool keys, Hierarchy/Asset Browser's rename and navigation) only fire while that specific panel is the one you're actually interacting with — hovering the viewport, or having clicked a row/tile — not merely because the panel happens to be open somewhere on screen. This is what stops, for example, the Scene View's R key from switching the gizmo to Scale while you're typing an R into a rename field elsewhere.

Typing into any text field also suppresses every shortcut that isn't held down with Ctrl or Cmd — plain letters and Shift+letter always reach the field you're editing.

Rebinding

Open Turian ▸ Settings, select the Shortcuts category in the sidebar. Each row shows the command, which panel it's scoped to, and its current binding:

  • Click the binding button and press a new key combination. Esc cancels; Backspace clears the binding entirely (the command becomes unbound, not merely "unset back to default").
  • A warning icon appears next to a binding that collides with another command whose scope overlaps it (two Global commands, or a Global one and a panel-scoped one) — hover it to see which command it conflicts with. Conflicting bindings are still allowed, same as most editors; it's a warning, not a hard block.
  • The revert icon next to an overridden row resets that one command back to its default. Reset All does the same for every command.
  • Rebinds take effect immediately as you make them. Save writes them to the settings file; Reload discards any unsaved rebinds.

Menu items that have a registered command show their current shortcut right-aligned and dimmed, sourced live from the registry — a rebind in the Shortcuts settings panel is reflected in the menu on the next frame, with no separate text to keep in sync.

For plugin / module authors

Commands are registered at runtime, not declared in one central file — each feature module owns its own commands, the same way studio/main-window/Panels.zig lets each panel register itself:

const Shortcuts = @import("studio/services/Shortcuts.zig");

const my_commands = [_]Shortcuts.CommandDesc{
    .{
        .id = "myPlugin.doThing",
        .title = "Do The Thing",
        .context = .global, // or .scene_viewport / .hierarchy / .asset_browser / .ui_editor / .text_input
        .requires_focus = false, // true if this only makes sense while your panel is actually engaged
        .defaults = &.{Shortcuts.Binding.single(.{ .key = .t, .ctrl = true })},
    },
};
const my_handlers = [_]?Shortcuts.Handler{myHandler};

// once, e.g. on first draw:
Shortcuts.register(&my_commands, &my_handlers);

For a command scoped to your own panel (context other than .global), don't pass a handler — instead check it directly inside your panel's own event loop, in place of a hardcoded key check:

for (gui.events()) |*e| {
    if (Shortcuts.eventMatches(e, "myPlugin.panelAction", panel_is_engaged)) {
        e.handle(@src(), my_widget_data);
        doPanelAction();
    }
}

panel_is_engaged is whatever "the user is looking at my panel" means for you — real dvui keyboard focus for a widget-based panel, mouse-hover for a non-focusable surface like a 3D viewport.

Command ids are namespaced strings (area.action) and double as the settings key suffix (shortcut.<command_id>) — pick one you're willing to keep stable, since renaming it orphans any user's saved rebind.


← All docs Edit this page