Skip to main content

Chained commands

Two commands you'll find yourself wanting:

  • AnchorAll: anchor everything currently selected.
  • RecolorAll: recolor everything currently selected.

You don't have to choose. props:RunCommand lets one command call another.

The basics​

return {
Arguments = {},

Run = function(props)
-- Run AnchorAll using AnchorAll's *current* argument values.
props:RunCommand("AnchorAll")

-- Run Recolor with explicit arg overrides.
props:RunCommand("Recolor", { Color = "Bright red" })
end,
}

RunCommand is synchronous. The next line doesn't execute until the inner command's Run (and its OnExecuted) have finished. Whatever the inner Run returned is RunCommand's return value.

Undo behavior​

Inner RunCommand calls bypass their own ChangeHistoryService recording. The outer command's recording (assuming AutoRecordChanges = true) covers everything.

What this means in practice:

-- Outer command, opens recording
Run = function(props)
props:RunCommand("AnchorAll") -- inner: NO new recording, joins outer
props:RunCommand("RecolorAll") -- inner: same, joins outer
-- All three operations are one undo step.
end,

If you set AutoRecordChanges = false on the outer and want each inner step to be its own undo step, you'll need to call them differently, manually wrapping each in your own ChangeHistoryService:TryBeginRecording block. But that's an unusual setup; most chains want the single-undo behavior.

Argument inheritance​

RunCommand("Name") (with no second argument) uses whatever argument values are currently set on Name: the values the user entered into its UI, or the persisted attribute values from the last session.

RunCommand("Name", { ... }) uses your overrides for any keys you provide; missing keys fall back to the current values. This lets you partially override:

-- Run Recolor with the user's current Anchor setting,
-- but force the Color regardless of what's typed in the field.
props:RunCommand("Recolor", { Color = "Black" })

The override is call-scoped. The persisted argument values on the Recolor command are not changed.

"Macro" command pattern​

A common shape: a command that chains together several others to do a complete workflow:

return {
Description = "Anchor everything, recolor to gray, lock further edits.",
Tags = { "macro" },

Arguments = {},

Run = function(props)
props:RunCommand("AnchorAll")
props:RunCommand("Recolor", { Color = "Medium stone grey" })
props:RunCommand("LockSelection")
end,
}

The macro doesn't need its own arguments, it just sequences the underlying commands. Treat macros as the "high-level recipe" that your "low-level building blocks" plug into.

Switching the active sidebar selection​

RunCommand runs a command but doesn't change which command is visible in the sidebar. To switch the user's active view:

props:ViewCommand("Recolor")

This is useful in two patterns:

  • "Continue" buttons: a button argument that runs some prep work and then takes the user to the next command in a flow.
  • Undo-then-show: when a command finishes, navigate to a related command so the user can iterate without searching.

ViewCommand does not run the command, it just selects it. Combine with RunCommand if you want both:

props:RunCommand("AnchorAll")
props:ViewCommand("Recolor")