Search Docs
Loading...
Skip to content

Quick Actions

A quick action is a one-tap button that edits the selected block immediately, with no panel in between. Surface the built-in actions, then add your own that run engine edits on tap.

Quick Actions on Android

6 mins
estimated time
GitHub

Overview#

Quick actions live where the user is already working: the canvas menu that floats next to a selected block, and the inspector bar that runs along the bottom of the editor. Each one is a button whose onClick handler reaches into the engine and edits the current selection on tap — duplicate, delete, flip, reset — without opening a sheet.

CE.SDK ships a set of predefined quick actions and lets you add your own. A custom quick action is a CanvasMenu.Button or InspectorBar.Button whose handler calls the engine; the difference from an ordinary button is that the handler performs the edit directly.

Symbol Purpose
CanvasMenu.Button.rememberDuplicate() and siblings Predefined quick actions
CanvasMenu.Button.remember { ... } A custom one-tap action
editorContext.selection.designBlock The selected block the handler edits
InspectorBar.Button.remember { ... } Reuse an action from a second surface

The example creates and selects a graphic block during onCreate only so the quick actions are visible when the guide opens. That setup is demo scaffolding; the reusable lesson starts with the canvasMenu and inspectorBar builders.

Built-in Quick Actions#

CE.SDK ships factory functions for common selected-block edits. Each function returns a button with its default action, label, and visibility rules, so dropping it into the list is enough:

@Composable
private fun rememberQuickActionsCanvasMenu() = CanvasMenu.remember {
listBuilder = {
CanvasMenu.ListBuilder.remember {
add { CanvasMenu.Button.rememberDuplicate() }
add { rememberFlipSelectionButton() }
add { rememberResetOrientationButton() }
add { CanvasMenu.Button.rememberDelete() }
}
}
}

The example keeps the predefined duplicate and delete actions, then inserts custom quick actions between them. Other predefined canvas menu actions include selection and layer-order operations such as bring forward, send backward, and select group.

A Custom One-Tap Action#

When the predefined buttons do not cover your edit, construct a button with a stable reverse-domain id, a label, and an onClick handler. This custom action mirrors the selected block horizontally:

@Composable
private fun rememberFlipSelectionButton() = CanvasMenu.Button.remember {
id = { EditorComponentId("ly.img.guide.quickActions.canvasMenu.flip") }
visible = {
remember(this) {
val selectedBlock = editorContext.safeSelection?.designBlock
selectedBlock != null &&
editorContext.engine.block.supportsFill(selectedBlock) &&
editorContext.engine.block.isAllowedByScope(selectedBlock, key = "layer/flip")
}
}
textString = { "Flip" }
contentDescription = { "Flip selected block horizontally" }
onClick = {
flipSelectedBlockHorizontally()
}
}

The visible predicate gates the action to selected blocks where the operation applies. The onClick handler then edits the current editorContext.selection.designBlock.

Because the handler edits the engine directly, it records an undo step when it finishes. Built-in quick actions do this for you; custom engine edits should call engine.editor.addUndoStep() once their mutation is complete.

private fun CanvasMenu.ItemScope.flipSelectedBlockHorizontally() {
val selectedBlock = editorContext.selection.designBlock
val flipped = editorContext.engine.block.isFlipHorizontal(selectedBlock)
editorContext.engine.block.setFlipHorizontal(block = selectedBlock, flip = !flipped)
editorContext.engine.editor.addUndoStep()
}

Gate the Action to the Selection#

Use visible when an action should only appear for compatible selections, and enabled when it should stay visible but temporarily inactive. The sample checks the current block before showing the flip action, so the canvas menu does not offer an operation that the selection cannot perform.

A Compound Quick Action#

A single tap can run several engine calls. This action resets the block’s orientation by clearing its rotation and horizontal flip together:

@Composable
private fun rememberResetOrientationButton() = CanvasMenu.Button.remember {
id = { EditorComponentId("ly.img.guide.quickActions.canvasMenu.resetOrientation") }
visible = {
remember(this) {
val selectedBlock = editorContext.safeSelection?.designBlock
selectedBlock != null &&
editorContext.engine.block.isAllowedByScope(selectedBlock, key = "layer/rotate") &&
editorContext.engine.block.isAllowedByScope(selectedBlock, key = "layer/flip")
}
}
textString = { "Reset" }
contentDescription = { "Reset selected block orientation" }
onClick = {
val selectedBlock = editorContext.selection.designBlock
editorContext.engine.block.setRotation(block = selectedBlock, radians = 0F)
editorContext.engine.block.setFlipHorizontal(block = selectedBlock, flip = false)
editorContext.engine.editor.addUndoStep()
}
}

The single addUndoStep() after both edits collapses the operation into one history entry, so undo reverts the rotation and flip together.

Reuse the Action in the Inspector Bar#

A quick action is not tied to the canvas menu. The inspector bar exposes the same editor context, so the same engine-editing pattern works from a second surface:

@Composable
private fun rememberQuickActionsInspectorBar() = InspectorBar.remember {
listBuilder = {
InspectorBar.ListBuilder.remember {
add { InspectorBar.Button.rememberLayer() }
add {
InspectorBar.Button.remember {
id = { EditorComponentId("ly.img.guide.quickActions.inspectorBar.flip") }
visible = {
remember(this) {
val selectedBlock = editorContext.safeSelection?.designBlock
selectedBlock != null &&
editorContext.engine.block.supportsFill(selectedBlock) &&
editorContext.engine.block.isAllowedByScope(selectedBlock, key = "layer/flip")
}
}
textString = { "Flip" }
contentDescription = { "Flip selected block horizontally" }
onClick = {
val selectedBlock = editorContext.selection.designBlock
val flipped = editorContext.engine.block.isFlipHorizontal(selectedBlock)
editorContext.engine.block.setFlipHorizontal(block = selectedBlock, flip = !flipped)
editorContext.engine.editor.addUndoStep()
}
}
}
add { InspectorBar.Button.rememberDelete() }
}
}
}

Use the inspector bar for selected-block actions that should stay visible beside persistent controls such as layer and delete. Keep the behavior consistent across surfaces by sharing the same engine logic where possible.

Handling State and Feedback#

Quick actions should make their state clear before and after they run:

  • Hide or disable actions when the selected block cannot support the operation.
  • Keep the handler focused on one immediate edit or one atomic group of edits.
  • Use a panel, sheet, or app-level message for workflows that need user input, progress, or async failure handling.

Troubleshooting#

Canvas menu button missing - Select a compatible block and check that the button is added to CanvasMenu.ListBuilder.

Inspector bar button missing - Select a compatible block and check that the button is added to InspectorBar.ListBuilder.

Action does not change the block - Check both the selection predicate and the engine API scope. The sample guards the flip action with engine.block.supportsFill(block) and engine.block.isAllowedByScope(block=_, key="layer/flip").

Action cannot be undone - If a custom handler edits the engine directly, call engine.editor.addUndoStep() after the mutation. Built-in quick actions already handle undo history.

API Reference#

Method Description
EditorConfiguration.remember(builder=_) Creates the editor configuration where quick actions are wired
CanvasMenu.remember(builder=_) Creates a canvas menu component for the selected block
CanvasMenu.ListBuilder.remember(builder=_) Builds the ordered canvas menu item list
CanvasMenu.ListBuilder.add(block=_) Adds a button or divider to the canvas menu list
CanvasMenu.Button.remember(builder=_) Creates a custom canvas menu button
CanvasMenu.Button.rememberDuplicate(builder=_) Creates the predefined duplicate button
CanvasMenu.Button.rememberDelete(builder=_) Creates the predefined delete button
InspectorBar.remember(builder=_) Creates an inspector bar component for the selected block
InspectorBar.ListBuilder.remember(builder=_) Builds the ordered inspector bar item list
InspectorBar.ListBuilder.add(block=_) Adds a button to the inspector bar list
InspectorBar.Button.remember(builder=_) Creates a custom inspector bar button
InspectorBar.Button.rememberLayer(builder=_) Creates the predefined layer inspector button
InspectorBar.Button.rememberDelete(builder=_) Creates the predefined delete inspector button
engine.block.supportsFill(block=_) Checks whether the selected block supports fill-related actions
engine.block.isAllowedByScope(block=_, key=_) Checks whether a block allows a scoped operation
engine.block.isFlipHorizontal(block=_) Reads the block’s current horizontal flip state
engine.block.setFlipHorizontal(block=_, flip=_) Toggles the horizontal flip state
engine.block.setRotation(block=_, radians=_) Sets the selected block’s rotation
engine.editor.addUndoStep() Records a programmatic block change in the undo history

Next Steps#

  • Add a New Button - Add buttons to the Dock, Canvas Menu, Inspector Bar, and Navigation Bar.
  • Canvas Menu - Configure the floating toolbar that hosts quick actions.
  • Inspector Bar - Customize the inspector bar for editing properties like position, color, and size.