Skip to content

Context Menu

Runnable example

python/examples/context_menu_interact.py — opens the T-key radial menu → get_state → highlight → invoke → close.

resoio.context_menu.ContextMenuClient

ContextMenuClient(socket_path: str | None = None)

Bases: _BaseClient[ContextMenuStub]

Async client for the Resonite IO ContextMenu service over a UDS.

Use as an async context manager so the gRPC channel is closed deterministically. Socket resolution mirrors :class:resoio.ConnectionClient.

Source code in src/resoio/_client.py
def __init__(self, socket_path: str | None = None) -> None:
    # Defer resolution to __aenter__ so env vars patched between
    # construction and connection are honoured, and so resolution
    # errors surface at the connect site.
    self._explicit_path: str | None = socket_path
    self._channel: Channel | None = None
    self._stub: TStub | None = None
    self._resolved_path: str | None = None

open async

open(*, hand: ContextMenuHandArg = 'primary') -> ContextMenuState

Open the radial context menu and return the resulting state.

Waits server-side until the menu has finished opening. Calling when already open is a no-op that returns the current state. gRPC failures surface as :class:grpclib.exceptions.GRPCError.

Source code in src/resoio/context_menu.py
async def open(self, *, hand: ContextMenuHandArg = "primary") -> ContextMenuState:
    """Open the radial context menu and return the resulting state.

    Waits server-side until the menu has finished opening. Calling
    when already open is a no-op that returns the current state.
    gRPC failures surface as :class:`grpclib.exceptions.GRPCError`.
    """
    request = ContextMenuOpenRequest(hand=_hand_to_proto(hand))
    return await self._dispatch(lambda stub: stub.open(request))

close async

close(*, hand: ContextMenuHandArg = 'primary') -> ContextMenuState

Close the radial context menu and return the resulting state.

Source code in src/resoio/context_menu.py
async def close(self, *, hand: ContextMenuHandArg = "primary") -> ContextMenuState:
    """Close the radial context menu and return the resulting state."""
    request = ContextMenuCloseRequest(hand=_hand_to_proto(hand))
    return await self._dispatch(lambda stub: stub.close(request))

get_state async

get_state(*, hand: ContextMenuHandArg = 'primary') -> ContextMenuState

Return the current menu state without modifying it.

Source code in src/resoio/context_menu.py
async def get_state(
    self, *, hand: ContextMenuHandArg = "primary"
) -> ContextMenuState:
    """Return the current menu state without modifying it."""
    request = ContextMenuGetStateRequest(hand=_hand_to_proto(hand))
    return await self._dispatch(lambda stub: stub.get_state(request))

highlight async

highlight(index: int, *, hand: ContextMenuHandArg = 'primary') -> ContextMenuState

Select (preview) the item at index without triggering it.

Highlight only moves the visual selection; it never presses the item, so it has no side effect on the world or active tool. Use :meth:invoke to actually act on an item.

gRPC failures (e.g. out-of-range index, menu not open) surface as :class:grpclib.exceptions.GRPCError.

Source code in src/resoio/context_menu.py
async def highlight(
    self, index: int, *, hand: ContextMenuHandArg = "primary"
) -> ContextMenuState:
    """Select (preview) the item at ``index`` without triggering it.

    Highlight only moves the visual selection; it never presses the
    item, so it has no side effect on the world or active tool. Use
    :meth:`invoke` to actually act on an item.

    gRPC failures (e.g. out-of-range index, menu not open) surface as
    :class:`grpclib.exceptions.GRPCError`.
    """
    request = ContextMenuHighlightRequest(hand=_hand_to_proto(hand), index=index)
    return await self._dispatch(lambda stub: stub.highlight(request))

invoke async

invoke(index: int, *, hand: ContextMenuHandArg = 'primary') -> ContextMenuState

Press the item at index and return the resulting state.

Unlike :meth:highlight, this fires the item's button action, so it may open a submenu, switch the active tool, or otherwise mutate world state. The returned state reflects the menu after the press (e.g. a submenu's items, or an empty menu if the action closed it).

gRPC failures (e.g. out-of-range index, menu not open) surface as :class:grpclib.exceptions.GRPCError.

Source code in src/resoio/context_menu.py
async def invoke(
    self, index: int, *, hand: ContextMenuHandArg = "primary"
) -> ContextMenuState:
    """Press the item at ``index`` and return the resulting state.

    Unlike :meth:`highlight`, this fires the item's button action, so
    it may open a submenu, switch the active tool, or otherwise mutate
    world state. The returned state reflects the menu *after* the press
    (e.g. a submenu's items, or an empty menu if the action closed it).

    gRPC failures (e.g. out-of-range index, menu not open) surface as
    :class:`grpclib.exceptions.GRPCError`.
    """
    request = ContextMenuInvokeRequest(hand=_hand_to_proto(hand), index=index)
    return await self._dispatch(lambda stub: stub.invoke(request))

resoio.context_menu.ContextMenuItem dataclass

ContextMenuItem(
    index: int,
    label: str,
    enabled: bool,
    has_icon: bool,
    color: tuple[float, float, float, float],
)

A single entry in the radial context menu.

index is the enumeration order (ArcLayout child order) and is what :meth:ContextMenuClient.highlight / :meth:ContextMenuClient.invoke address. color is the RGBA tuple (r, g, b, a).

resoio.context_menu.ContextMenuState dataclass

ContextMenuState(
    is_open: bool, items: tuple[ContextMenuItem, ...], highlighted_index: int
)

Snapshot of the current context menu state.

items is empty when the menu is closed. highlighted_index is -1 when no item is highlighted.