Context Menus and the Canvas#

The context menu and the canvas are secondary GUI elements that can round out a more complete experience for the end-users.

The Canvas#

The canvas is a collapsable canvas that can show text and images to users. It is useful because it does not get buried by the chat messages.

The canvas is a list of CanvasElement objects that specify either a text path or image URL path. Each element becomes one pane and can include one text string (markdown) and/or one image URL.

Similar to handling buttons, it is best to call send_update_canvas from within on_fetch_canvas.

This example creates a simple canvase with three elements:

async def on_refresh(self, action):
    canvas_elements = [CanvasElement(text="Some **text** here, along with an image", path="https://www.moobius.net/images/index/indexH2.png")]
    canvas_elements.append(CanvasElement(path="https://www.moobius.net/images/index/indexBg.png"))
    canvas_elements.append(CanvasElement(text="More `text` here.\nWith multiple lines.\nSeperated by newlines."))
    await self.send_update_canvas(canvas_elements, action.channel_id, [action.sender])
    await self.send_update_style([StyleElement(widget="canvas", display="visible", expand=True)], action.channel_id, [action.sender])

This canvas is not interactive, it is just a place to display information to the user.

The Context Menu#

The context menu controls right-click behavior when a user clicks a message. This is the primary way to make messages interactive.

The menu is a list of ContextMenuElement objects. Each object specifies the name and which message type (text, image, audio, file, and card) it applies to.

ContextMenuElement(item_name='Text here', item_id='use this in your program', support_subtype=[types.TEXT, types.IMAGE, etc])

Similar to handling buttons and the canvas, it is best to call send_update_context_menu from within on_fetch_context_menu. This example will respond differently to each message type:

async def on_refresh(self, action):
    elements = []
    menu_types = [types.TEXT, types.IMAGE, types.AUDIO, types.FILE, types.CARD]
    await self.send_message('types supported: '+str(menu_types), action.channel_id, action.sender, [action.sender])

    for i in range(len(menu_types)):
        for j in range(3):
            elements.append(ContextMenuElement(item_name=menu_types[i]+' item '+str(j), item_id=menu_types[i]+str(j), support_subtype=[menu_types[i]]))
    await self.send_update_context_menu(elements, action.channel_id, [action.sender])

To respond to the message, use the on_context_menu_click callback:

async def on_context_menu_click(self, context_click: MenuClick):
    txt = 'Menu choice: '+context_click.item_id+' Message content: '+str(context_click.message_content)
    await self.send_message(txt, context_click.channel_id, context_click.sender, [context_click.sender])

There is also a way to put pop-up input argumnets inside the menu option using the arguments flag, should this advance use-case be needed:

menu_element.arguments = [ButtonArgument(name='popup', type='string', values=None, placeholder='Write the input as a string.')]

Demo code#

The demo code is available on

the public repo <https://github.com/groupultra/sdk-public/tree/main/projects/Menu Canvas>.