Command Palette
Every command of the app, found by its name and run from the keyboard: what Cmd+K or Ctrl+K opens.
import com.pantrixui.components.overlay.commandpalette.PantrixCommandPalette
- A modal panel. As a dialog is: a scrim behind it, the focus inside; back, Esc and a click on the scrim ask to close. Whether it is shown is the screen's state (
visible,onDismissRequest); the palette does not close itself.onRunis where the palette is closed before the command does its work. - It stands at the top. At the top of the window, in the middle: as you type, the list under it shrinks and grows, and the field stays where it is. On a phone it is as wide as the margins leave, and ends above the keyboard.
- Commands. A
PantrixCommand'skeyis the same in every language;labelgives the name read,groupthe heading it stands under,iconthe icon before it,shortcutthe shortcut at its end,keywordsother words that find it, andsupportingTextthe second line under the label. - Search. Each word typed is looked for in the label, the group, the keywords or the second line; capitals and small letters are compared as the theme's language has them (in Turkish İ with i, I with ı). The groups keep their order; inside each group, those matched from the start, and from the start of a word, come first; the letters found are set in bold.
- Disabled. A command with
enabled = falseis listed, written faintly; the arrows pass it by and it is not run. - Shortcuts. A
PantrixShortcutholds a key and what is pressed with it;commandis Command on Apple's systems and Control elsewhere. It is written at the row's end as the system writes it: ⇧⌘K or Ctrl+Shift+K.Modifier.onShortcutbinds one shortcut andModifier.commandShortcutsevery command's in the list;Modifier.shortcutRooton the window's root makes the shortcuts heard while nothing has the focus too. - Platform. The panel is a dialog's, the rows the platform's list rows: 48dp on Android, 44pt on iOS, 36dp on desktop and the web. On desktop and the web the panel stands a little lower from the top.
Examples
Groups, shortcuts and keywords
A file editor's commands, with their groups. A button opens it; typing narrows the list, the arrows walk it, and Enter runs the highlighted command and closes the palette. "Delete" cannot be run now: it is listed, but the arrows pass it by. "Appearance settings" is found by "theme" or "dark" too: those are its keywords. Two commands have a second line under their label that says what they do (supportingText); the search reads it too: type "address".
var open by rememberSaveable { mutableStateOf(false) }
var ran by rememberSaveable { mutableStateOf<String?>(null) }
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md)) {
PantrixButton(text = "Open the command palette", onClick = { open = true }, tagKey = "open_editor")
PantrixText(
text = "Last run: ${ran ?: "none"}",
color = PantrixTheme.colors.textSecondary,
tagKey = "ran_editor"
)
}
PantrixCommandPalette(
visible = open,
onDismissRequest = { open = false },
commands = EDITOR_COMMANDS,
onRun = { command ->
open = false
ran = command.label
},
tagKey = "editor"
)
private val EDITOR_COMMANDS = listOf(
PantrixCommand(
key = "new",
label = "New file",
group = "File",
icon = PantrixIcons.Plus,
shortcut = PantrixShortcut(Key.N, command = true)
),
PantrixCommand(
key = "open",
label = "Open file",
group = "File",
icon = PantrixIcons.Folder,
shortcut = PantrixShortcut(Key.O, command = true)
),
PantrixCommand(
key = "rename",
label = "Rename",
group = "File",
shortcut = PantrixShortcut(Key.R, command = true, shift = true)
),
PantrixCommand(key = "delete", label = "Delete", group = "File", enabled = false),
PantrixCommand(
key = "copy",
label = "Copy the link",
group = "File",
icon = PantrixIcons.Copy,
keywords = listOf("link", "share"),
supportingText = "An address for those who can see the file"
),
PantrixCommand(
key = "zoom_in",
label = "Zoom in",
group = "View",
icon = PantrixIcons.ZoomIn,
shortcut = PantrixShortcut(Key.Equals, command = true)
),
PantrixCommand(
key = "zoom_out",
label = "Zoom out",
group = "View",
icon = PantrixIcons.ZoomOut,
shortcut = PantrixShortcut(Key.Minus, command = true)
),
PantrixCommand(
key = "appearance",
label = "Appearance settings",
group = "View",
icon = PantrixIcons.Eye,
keywords = listOf("theme", "dark", "light"),
supportingText = "Theme, text size, line spacing"
),
PantrixCommand(
key = "search",
label = "Search the files",
icon = PantrixIcons.Search,
shortcut = PantrixShortcut(Key.F, command = true, shift = true)
)
)
The commands' own shortcuts
The commands' own shortcuts: one list is what the palette shows and what the keyboard runs. While typing in the field, ⌘B or Ctrl+B runs "Bold" and ⌘I or Ctrl+I "Italic"; the letter is not typed in the field. They are heard while the focus is inside this card: for the whole window they belong on its root.
var open by rememberSaveable { mutableStateOf(false) }
var ran by rememberSaveable { mutableStateOf<String?>(null) }
val note = rememberTextFieldState()
val run = { command: PantrixCommand -> ran = command.label }
Column(
modifier = Modifier.fillMaxWidth().commandShortcuts(TEXT_COMMANDS, onRun = run),
verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md)
) {
PantrixTextField(
state = note,
modifier = Modifier.fillMaxWidth(),
label = "Note",
placeholder = "Type here, then ⌘B or Ctrl+B",
tagKey = "note"
)
PantrixButton(
text = "Formatting commands",
onClick = { open = true },
variant = ButtonVariant.Outline,
tagKey = "open_text"
)
PantrixText(
text = "Last run: ${ran ?: "none"}",
color = PantrixTheme.colors.textSecondary,
tagKey = "ran_text"
)
}
PantrixCommandPalette(
visible = open,
onDismissRequest = { open = false },
commands = TEXT_COMMANDS,
onRun = { command ->
open = false
run(command)
},
tagKey = "text"
)
private val TEXT_COMMANDS = listOf(
PantrixCommand(
key = "bold",
label = "Bold",
group = "Format",
shortcut = PantrixShortcut(Key.B, command = true)
),
PantrixCommand(
key = "italic",
label = "Italic",
group = "Format",
shortcut = PantrixShortcut(Key.I, command = true)
),
PantrixCommand(
key = "clear",
label = "Clear the formatting",
group = "Format",
shortcut = PantrixShortcut(Key.Backslash, command = true)
)
)
Words of its own and colours of its own
The palette's own words and colours: the field's placeholder and what is written when nothing matches are the caller's, and the highlighted row is in the app's own violet. Type "zzz" to see the words for an empty list.
var open by rememberSaveable { mutableStateOf(false) }
val own = ownColors()
PantrixButton(text = "Go somewhere", onClick = { open = true }, variant = ButtonVariant.Outline, tagKey = "open_go")
PantrixCommandPalette(
visible = open,
onDismissRequest = { open = false },
commands = PLACES,
onRun = { open = false },
placeholder = "Type the name of a page…",
emptyText = "No page has that name",
colors = CommandPaletteColors(highlightedContainer = own.container, highlightedContent = own.onContainer),
tagKey = "go"
)
private val PLACES = listOf(
PantrixCommand(key = "home", label = "Home", icon = PantrixIcons.Inbox),
PantrixCommand(key = "profile", label = "Profile", icon = PantrixIcons.Person),
PantrixCommand(key = "favourites", label = "Favourites", icon = PantrixIcons.Star),
PantrixCommand(key = "calendar", label = "Calendar", icon = PantrixIcons.Calendar)
)
In its own style
In its own style: CommandPaletteStyle changes the palette's width, its corners and its rows. Here the palette is at most 420dp wide, its corners are medium round and its rows 40dp high; every field not given is the platform's preset.
var open by rememberSaveable { mutableStateOf(false) }
PantrixButton(text = "Open the narrow palette", onClick = { open = true }, tagKey = "open_narrow")
PantrixCommandPalette(
visible = open,
onDismissRequest = { open = false },
commands = EDITOR_COMMANDS,
onRun = { open = false },
style = CommandPaletteStyle(maxWidth = 420.dp, shape = ShapeRole.Medium, itemHeight = 40.dp, iconGap = 12.dp),
tagKey = "narrow"
)
private val EDITOR_COMMANDS = listOf(
PantrixCommand(
key = "new",
label = "New file",
group = "File",
icon = PantrixIcons.Plus,
shortcut = PantrixShortcut(Key.N, command = true)
),
PantrixCommand(
key = "open",
label = "Open file",
group = "File",
icon = PantrixIcons.Folder,
shortcut = PantrixShortcut(Key.O, command = true)
),
PantrixCommand(
key = "rename",
label = "Rename",
group = "File",
shortcut = PantrixShortcut(Key.R, command = true, shift = true)
),
PantrixCommand(key = "delete", label = "Delete", group = "File", enabled = false),
PantrixCommand(
key = "copy",
label = "Copy the link",
group = "File",
icon = PantrixIcons.Copy,
keywords = listOf("link", "share"),
supportingText = "An address for those who can see the file"
),
PantrixCommand(
key = "zoom_in",
label = "Zoom in",
group = "View",
icon = PantrixIcons.ZoomIn,
shortcut = PantrixShortcut(Key.Equals, command = true)
),
PantrixCommand(
key = "zoom_out",
label = "Zoom out",
group = "View",
icon = PantrixIcons.ZoomOut,
shortcut = PantrixShortcut(Key.Minus, command = true)
),
PantrixCommand(
key = "appearance",
label = "Appearance settings",
group = "View",
icon = PantrixIcons.Eye,
keywords = listOf("theme", "dark", "light"),
supportingText = "Theme, text size, line spacing"
),
PantrixCommand(
key = "search",
label = "Search the files",
icon = PantrixIcons.Search,
shortcut = PantrixShortcut(Key.F, command = true, shift = true)
)
)
API
visible
Every command of the app, found by its name and run from the keyboard: what Cmd+K or Ctrl+K opens.
| Parameter | Type | Default | What it is |
|---|---|---|---|
visible | Boolean | whether the palette is shown. It never hides itself. | |
onDismissRequest | () -> Unit | called for back, Escape and a click on the scrim. | |
commands | List<PantrixCommand> | every command, in the order they are listed under their groups. No two have one key. | |
onRun | (PantrixCommand) -> Unit | called with the command run, by Enter, the keyboard's action or a press. Close the palette here. | |
modifier | Modifier | Modifier | the panel's modifier. |
placeholder | String? | null | what the empty field shows, or null for the theme's words for searching commands. |
emptyText | String? | null | what stands in the list's place while nothing matches, or null for the theme's words for it. |
style | CommandPaletteStyle? | null | this call's own style: over every enclosing one and under the density. Only the fields it sets change, on every platform. |
colors | CommandPaletteColors? | null | this palette's own colours, where they are not the theme's: the ones set here, with the theme's for the rest. |
tagKey | String? | null | the palette's automation key: the panel is CommandPalette_<tagKey>, its field CommandPalette_<tagKey>.TextField_query and a command's row CommandPalette_<tagKey>.Command_<key>. What is done is reported as a CommandPalette event: open, dismiss with via (back or scrim), and run with the command's key and via (key or click). What is typed is never reported. |
CommandPaletteStyle
PantrixCommandPalette's geometry: the panel, where it stands, and the rows of its list.
| Parameter | Type | Default | What it is |
|---|---|---|---|
shape | ShapeSpec | ShapeSpec.Unspecified | The panel's corners: a step of the brand's shape scale, or a fixed shape. |
maxWidth | Dp | Dp.Unspecified | The widest the panel grows in a wide window. |
maxHeight | Dp | Dp.Unspecified | The tallest the panel grows in a tall window. A narrower window, and the keyboard, make it less. |
elevation | Dp | Dp.Unspecified | The panel's shadow. iOS sets a panel apart by the dimming alone. |
margin | Dp | Dp.Unspecified | The least room between the panel and the window's safe area. |
topSpace | Dp | Dp.Unspecified | The room above the panel, past margin. |
itemHeight | Dp | Dp.Unspecified | The least height of a command's row. A larger type makes it taller. |
itemHorizontalPadding | Dp | Dp.Unspecified | The room between a row's sides and what it holds, and a heading's. |
iconSize | Dp | Dp.Unspecified | The width and height of a command's icon. |
iconGap | Dp | Dp.Unspecified | Between a command's icon, its label and its shortcut. |
CommandPaletteColors
What a PantrixCommandPalette is painted with, in place of the theme's colours: a colour that is set is used, and one that is left out stays the theme's.
| Parameter | Type | Default | What it is |
|---|---|---|---|
container | Color | Color.Unspecified | The panel's fill. The theme's surface. |
content | Color | Color.Unspecified | A command's label and its icon, and what is typed. The theme's textPrimary, or the colour a role's fill given as container gives what stands on it. |
highlightedContainer | Color | Color.Unspecified | The fill behind the command Enter runs. The primary role's container. |
highlightedContent | Color | Color.Unspecified | That command's label, icon and shortcut: the colour that reads on highlightedContainer. The primary role's onContainer. |
heading | Color | Color.Unspecified | A group's heading, a shortcut and what stands in the list's place when nothing matches. The theme's textSecondary on the theme's own grounds, and content on another fill. |
scrim | Color | Color.Unspecified | The dimming laid over the app behind the palette, with its own alpha. The theme's scrim. |
Accessibility
- Keyboard. The field keeps the focus; the down and up arrows go between the commands that can be run and stop at the ends, Page Down and Page Up skip a page, and the mouse highlights the row it passes over. Enter, the keyboard's action and a press on the row run the command. Each time it opens the field is empty and the highlight is on the first command that can be run.
- Screen reader. As it opens, the palette is announced by the theme's name for a list of commands. The highlighted row is selected, the groups are headings, the shortcut is read as the row's state; as you type, how many commands are left is said.