rtk.NativeMenu
¶
A utility class that opens an OS-native popup menu.
Here's an example that opens a context menu when the mouse is right-clicked anywhere in the window.
local menu = rtk.NativeMenu()
menu:set({
-- Here is an example of passing custom user data (the url field in this case) to
-- the handler below.
{'Visit Website', id='website', url='https://www.reaper.fm/'},
-- The items in the window submenu depend on the js_ReaScriptAPI, so disable
-- the entire submenu if that's not available.
{'Window', disabled=not rtk.has_js_reascript_api, submenu={
-- Use dynamic discovery of the checked value by passing a function. The custom
-- 'win' flag is used by the menu handler later to determine if this is a simple
-- boolean window attribute.
{'Docked', id='docked', checked=function() return window.docked end, win=true},
{'Pinned', id='pinned', checked=function() return window.pinned end, win=true},
{'Borderless', id='borderless', checked=function() return window.borderless end, win=true},
{'Translucent', id='translucent', checked=function() return window.opacity < 1.0 end},
}},
rtk.NativeMenu.SEPARATOR,
{'Exit', id='exit'},
})
window.onclick = function(self, event)
if event.button == rtk.mouse.BUTTON_RIGHT then
menu:open_at_mouse():done(function(item)
if not item then
-- User clicked off of menu, nothing selected.
return
end
if item.id == 'website' then
rtk.open_url(item.url)
elseif item.win then
window:attr(item.id, not window[item.id])
elseif item.id == 'translucent' then
window:attr('opacity', window.opacity == 1.0 and 0.5 or 1.0)
elseif item.id == 'exit' then
rtk.quit()
end
end)
end
end
rtk.NativeMenu() | Constructor to create a new NativeMenu |
set() | Sets the menu according to the supplied table |
item() | Retrieves an individual menu item table by either its user-defined id or its auto-generated index |
items() | Iterator over all items |
open() | Opens the popup menu at the given window coordinates |
open_at_mouse() | Opens the menu at the current mouse position |
open_at_widget() | Opens the menu relative to the given widget |
Constructor to create a new NativeMenu
menu | (table) | a menu table as defined by |
(rtk.NativeMenu) | the new instance |
Sets the menu according to the supplied table.
The table contains a sequence of items, where each individual menu item is itself a table in the form:
{label, id=(any), disabled=(boolean), checked=(boolean), hidden=(boolean), ...}
where:
label
(string): the label as seen in the popup menu (required).
Can be rtk.NativeMenu.SEPARATOR
to display a separator. Unlike the other
fields, this does not need to be explicitly named and can be positional.
However, label='Foo'
is allowed if preferred.id
(any): an arbitrary (but tostring()
able) user-defined value
which can be accessed via the value returned by the open()
functions.
Optional, but if not specified, the item can be fetched with item()
using
an index
field that will be added to the item table.checked
(boolean or function): if true, a checkmark will show next to the item.
Optional, and assumes false if not specified.disabled
(boolean or function): if true, the menu item will be visible but grayed
out and cannot be selected. Optional, and assumes false if not specified.hidden
(boolean or function): if true, the item will not be visible at all in
the popup menu. Can be used to easily toggle item visibility without having to
reset the menu with set()
. Optional, and assumes false if not specified....
: you may pass any other named fields in the table as user data and they
will be included in the table returned by the open*() functions.For fields above so marked, if a function is provided it is invoked at the time the popup menu is opened, which allows for dynamic evaluation of those properties.
As a convenience, the menu item element can be a string, where it's simply used as the label and all other fields above are assumed nil.
Submenus are also supported, where the item table takes the form:
{label, submenu=(table), checked=(boolean), disabled=(boolean), hidden=(boolean)}
where:
label
(string): the label for the submenu item (required).submenu
(table): a table of menu items as described abovechecked
(boolean or function): as abovedisabled
(boolean or function): as abovehidden
(boolean or function): as aboveSubmenus can be aribtrarily nested.
Retrieves an individual menu item table by either its user-defined id or its auto-generated index.
local menu = rtk.NativeMenu{'Foo', {'Bar', id='bar'}, 'Baz'}
-- We assigned an id to Bar so we can fetch it by its id
local bar = menu:item('bar')
-- Baz doesn't have its own id, but its the third item in the list so we can
-- retrieve it by index.
local baz = menu:item(3)
Note that the table returned (assuming the item is found) is a shallow copy of
the menu item's table you passed to set()
. It will contain all the same fields
you provided, but the top-level table itself is a different instance.
idx_or_id | (number or string) | either the numeric positional index of the menu item (with indices starting at 1 as usual for Lua), or its user-defined id |
(table or nil) | the menu item table as passed to |
Iterator over all items.
For submenus, only the inner items are returned here, not the parent item that contains
the submenu. If you need to access the parent item of a submenu, you'll need to ensure
it has an id
field defined and explicitly retrieve it with item()
.
local menu = rtk.NativeMenu{'Foo', 'Bar', {'Baz', checked=true}}
-- Loop over items and invert the checked field for each item
for item in menu:items() do
item.checked = not item.checked
end
menu:open_at_mouse()
(function) | iterator function |
Opens the popup menu at the given window coordinates.
One noteworthy detail about this method is that it returns an rtk.Future
and opens
the menu asynchronously. This is because REAPER's underlying function to open menus is
blocking, and we want the update cycle to have completed before opening the menu to
ensure any related visual state (e.g. a button being pressed) has a chance to draw
before the application becomes blocked by the menu.
local menu = rtk.NativeMenu{'Foo', 'Bar', 'Baz'}
menu:open(10, 10):done(function(item)
if item then
log.info('selected item: %s', table.tostring(item))
end
end)
x | (number) | the x coordinate for the menu relative to the window, where 0 is far left, and negative values are allowed |
y | (number) | the y coordinate for the menu relative to the window, where 0 is the top, and negative values are allowed |
(rtk.Future) | a Future which is completed when the menu is closed, and which receives either the menu item table of the selected item, or nil if no item was selected |
Opens the menu at the current mouse position.
This calls open()
based on current mouse position.
(rtk.Future) | a Future which is completed when the menu is closed, and which receives either the menu item table of the selected item, or nil if no item was selected |
Opens the menu relative to the given widget.
This calls open()
based on current widget location and the supplied alignment
values (if any).
local menu = rtk.NativeMenu{'Foo', 'Bar', 'Baz'}
local button = window:add(rtk.Button{"Open Menu"})
button.onclick = function()
-- Popup will obscure the button because of top alignment.
menu:open_at_widget(button, 'left', 'top')
end
The widget must have been drawn or this method will fail because the client coordinates of the widget won't be known.
The widget must have been drawn or this will fail because the client coordinates wouldn't be known.
widget | (rtk.Widget) | the widget to open the menu in relation to |
halign | (string or nil) |
|
valign | (string or nil) |
|
(rtk.Future) | a Future which is completed when the menu is closed, and which receives either the menu item table of the selected item, or nil if no item was selected |