# ContextMenu

A right-click context menu anchored to the pointer, with keyboard shortcuts, checkable items, and nested submenus.

```slint
import { ContextMenu } from "@glint/components/context-menu.slint";
import { Tokens } from "@glint/theme/tokens.slint";
import { IconSet } from "@lucide";

export component Demo inherits Window {
    width: 560px;
    height: 320px;
    background: Tokens.color-background;

    in-out property <string> last-action: "None";

    VerticalLayout {
        alignment: center;
        padding: 24px;
        spacing: 12px;

        HorizontalLayout {
            alignment: center;

            menu := ContextMenu {
                items: [
                    { label: "Back", icon: IconSet.ArrowLeft, shortcut: "Alt+Left" },
                    { label: "Forward", icon: IconSet.ArrowRight, shortcut: "Alt+Right" },
                    { label: "Reload", icon: IconSet.RotateCw, shortcut: "Ctrl+R" },
                    {
                        label: "View",
                        separator-before: true,
                        children: [
                            { label: "Source", shortcut: "Ctrl+U" },
                            { label: "Inspect", shortcut: "Ctrl+Shift+I" }
                        ]
                    }
                ];
                selected(row, child) => {
                    if (child >= 0) {
                        root.last-action = "Submenu item " + child + " in row " + row;
                    } else {
                        root.last-action = "Row " + row;
                    }
                }

                Rectangle {
                    width: 380px;
                    height: 180px;
                    background: Tokens.color-surface-1;
                    border-width: 1px;
                    border-color: Tokens.color-border-hairline;
                    border-radius: Tokens.radius-lg;

                    VerticalLayout {
                        alignment: center;
                        spacing: 8px;

                        Text {
                            text: "Right-click anywhere in this area";
                            color: Tokens.color-foreground;
                            font-size: Tokens.typography-body-size;
                            horizontal-alignment: center;
                        }
                        Text {
                            text: "Opens context menu at the pointer position";
                            color: Tokens.color-muted-foreground;
                            font-size: Tokens.typography-body-sm-size;
                            horizontal-alignment: center;
                        }
                    }
                }
            }
        }

        Text {
            text: "Last action: " + root.last-action;
            color: Tokens.color-muted-foreground;
            font-size: Tokens.typography-body-sm-size;
            horizontal-alignment: center;
        }
    }
}
```

## Usage

```slint
import { ContextMenu } from "@glint/components/context-menu.slint";

export component AppWindow inherits Window {
    VerticalLayout {
        alignment: center;

        menu := ContextMenu {
            items: [
                { label: "Cut" },
                { label: "Copy" },
                { label: "Paste" },
            ];
            selected(row, child) => {
                // handle choice
            }

            Rectangle {
                width: 300px;
                height: 150px;
                // your right-clickable canvas or content
            }
        }
    }
}
```

`ContextMenu` wraps an interactive region in its `@children` slot. Right-clicking anywhere within that area captures the pointer coordinate and opens the menu popup powered by [MenuPanel](/docs/components/menu-panel) anchored to the cursor location.

The menu dismisses on click-outside or when `Escape` is pressed. Calling `show()` programmatically opens the menu at the last known pointer position or the area’s top-left corner.

## Examples

### Submenu alignment and positioning

Submenus open beside the parent row. Use `submenu-side` (`PanelSide.right`, `PanelSide.left`) and `submenu-align` (`PanelAlign.start`, `PanelAlign.center`, `PanelAlign.end`) to configure placement.

```slint
import { ContextMenu } from "@glint/components/context-menu.slint";
import { PanelSide, PanelAlign } from "@glint/components/panel.slint";
import { Tokens } from "@glint/theme/tokens.slint";
import { IconSet } from "@lucide";

export component Demo inherits Window {
    width: 560px;
    height: 320px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            menu := ContextMenu {
                submenu-side: PanelSide.right;
                submenu-align: PanelAlign.start;
                items: [
                    { label: "Inspect Element", icon: IconSet.Search },
                    {
                        label: "Developer Tools",
                        separator-before: true,
                        children: [
                            { label: "Console" },
                            { label: "Network" },
                            { label: "Performance" }
                        ]
                    }
                ];

                Rectangle {
                    width: 360px;
                    height: 160px;
                    background: Tokens.color-surface-1;
                    border-width: 1px;
                    border-color: Tokens.color-border-hairline;
                    border-radius: Tokens.radius-lg;

                    VerticalLayout {
                        alignment: center;

                        Text {
                            text: "Right-click to inspect submenus";
                            color: Tokens.color-foreground;
                            horizontal-alignment: center;
                        }
                    }
                }
            }
        }
    }
}
```

### Checkable options and radio groups

Items can act as toggleable checkboxes or mutually exclusive radio choices. Group headers and separators follow the unified [grouped rows](/docs/components/select#grouped-rows) model. Activating a checkbox toggles its state, while choosing a radio option clears other options sharing the same `radio-group`.

```slint
import { ContextMenu } from "@glint/components/context-menu.slint";
import { MenuEntry } from "@glint/components/menu-entry.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 320px;
    background: Tokens.color-background;

    in-out property <[MenuEntry]> editor-items: [
        { label: "Word Wrap", checkable: true, checked: true },
        { label: "Show Line Numbers", checkable: true, checked: true },
        { label: "Tabs", separator-before: true, heading-before: "Indentation", checkable: true, checked: false, radio-group: "indent" },
        { label: "Spaces (2)", checkable: true, checked: true, radio-group: "indent" },
        { label: "Spaces (4)", checkable: true, checked: false, radio-group: "indent" },
    ];

    VerticalLayout {
        alignment: center;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            menu := ContextMenu {
                items <=> root.editor-items;

                Rectangle {
                    width: 360px;
                    height: 160px;
                    background: Tokens.color-surface-1;
                    border-width: 1px;
                    border-color: Tokens.color-border-hairline;
                    border-radius: Tokens.radius-lg;

                    VerticalLayout {
                        alignment: center;

                        Text {
                            text: "Right-click for editor settings";
                            color: Tokens.color-foreground;
                            horizontal-alignment: center;
                        }
                    }
                }
            }
        }
    }
}
```

### Programmatic keyboard opening

Calling `show()` opens the context menu for keyboard users. You can bind `show()` to application keyboard shortcuts or dedicated context menu keys.

```slint
import { Button } from "@glint/components/button.slint";
import { ContextMenu } from "@glint/components/context-menu.slint";
import { Tokens } from "@glint/theme/tokens.slint";
import { IconSet } from "@lucide";

export component Demo inherits Window {
    width: 560px;
    height: 320px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        padding: 24px;
        spacing: 12px;

        HorizontalLayout {
            alignment: center;

            menu := ContextMenu {
                items: [
                    { label: "Copy", icon: IconSet.Copy, shortcut: "Ctrl+C" },
                    { label: "Paste", icon: IconSet.Clipboard, shortcut: "Ctrl+V" },
                    { label: "Select All", shortcut: "Ctrl+A" }
                ];

                Rectangle {
                    width: 340px;
                    height: 140px;
                    background: Tokens.color-surface-1;
                    border-width: 1px;
                    border-color: Tokens.color-border-hairline;
                    border-radius: Tokens.radius-lg;

                    VerticalLayout {
                        alignment: center;

                        Text {
                            text: "Right-click area or use button below";
                            color: Tokens.color-foreground;
                            horizontal-alignment: center;
                        }
                    }
                }
            }
        }

        HorizontalLayout {
            alignment: center;

            Button {
                text: "Open via show()";
                clicked => { menu.show(); }
            }
        }
    }
}
```

## API Reference

### Properties

| Property            | Type                 | Default            | Description                                                                                                                                                                         |
| ------------------- | -------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `items`             | `in-out [MenuEntry]` | no default         | Menu rows shown on right-click. `in-out` because activating a checkbox or radio row writes its new `checked` back here.                                                             |
| `min-content-width` | `in length`          | `200px`            | The floor under the popup panel's width; the panel grows past it to fit its widest row, icon and shortcut hint included.                                                            |
| `submenu-side`      | `in PanelSide`       | `PanelSide.right`  | Where a row's submenu panel opens: which side of the menu, and how it lines up against the row it hangs off. A submenu opens beside its row, so `top` and `bottom` read as `right`. |
| `submenu-align`     | `in PanelAlign`      | `PanelAlign.start` |                                                                                                                                                                                     |
| `highlighted-index` | `in-out int`         | `0`                | Row highlighted by ↑/↓ inside the open menu; consumers rarely set this.                                                                                                             |
| `is-open`           | `out bool`           | no default         | True while the menu is on screen; mirrors `PopupWindow.is-open`.                                                                                                                    |
| `menu-x`            | `in-out length`      | no default         | Internal — where the menu opens horizontally (set on right-click).                                                                                                                  |
| `menu-y`            | `in-out length`      | no default         | Internal — where the menu opens vertically (set on right-click).                                                                                                                    |

### Callbacks

| Callback              | Description                                                                                                                                   |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `selected(int , int)` | Fired with the chosen row's index, and with the index of the submenu leaf that was taken or -1 when the row itself was; the menu auto-closes. |

### Functions

| Function  | Description                                                                                                                                                         |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `show()`  | Opens the menu with the highlight back at the top. Mirrors `PopupWindow.show()` — the keyboard path into a menu that otherwise only the right mouse button reaches. |
| `close()` | Closes the menu. Mirrors `PopupWindow.close()`.                                                                                                                     |

### Enums

| Enum         | Values                           |
| ------------ | -------------------------------- |
| `PanelSide`  | `top`, `right`, `bottom`, `left` |
| `PanelAlign` | `start`, `center`, `end`         |

## Accessibility

- **Pointer location.** The context menu positions its floating surface at the right-click pointer coordinates while remaining clamped within window bounds.
- **Accessible list.** The menu is reported as an accessible list structure containing individual list items with index information.
- **Keyboard operation.** When opened via keyboard `show()`, focus lands on the first item. `↑`/`↓` navigate, `→` enters submenus, `←` returns to the parent menu, and `Escape` closes the menu.
- **State indicators.** Checked states on toggleable and radio options are announced directly to assistive technologies.
