Drag and context menus
Let people drag things with a mouse or a finger, and open an item's actions from any device, with one handler each.
A note to move and recolor
This program puts a note on a board. Drag it anywhere, or ask for its actions to change its color.
local ui = datumhue.ui
local token = datumhue.theme.token
local board = ui.root:panel({ width = "100%", height = "100%" })
local note = board:button({
accessible_label = "Note",
left = 40,
top = 40,
width = 200,
height = 120,
})
note.color = token("accent_data")
note:label({
text = "Drag me, or right-click me",
color = token("panel_background"),
})
local menu = note:popover({ side = "right", light_dismiss = true })
local colors = {
Orange = "accent_data",
Green = "accent_secondary",
Blue = "info",
}
for _, name in ipairs({ "Orange", "Green", "Blue" }) do
menu:button({
text = name,
on_click = function()
note.color = token(colors[name])
menu.visible = false
end,
})
end
local from
note:on_drag(function(e)
if e.phase == "start" then
from = note.pos
end
if e.phase == "cancel" then
note.pos = from
else
note.pos = from + e.total
end
end)
note:on_context(function()
menu.visible = true
end)
In this example: datumhue.ui datumhue.theme.token datumhue.ui.root UiElement:panel UiElement:button UiElement.color UiElement:label UiElement:popover UiElement.visible UiElement:on_drag UiElement.pos UiElement:on_context
- The note is a card, so it takes focus and the keyboard and gamepad can ask for its actions.
leftandtoptake the note out of the board's layout and place it at that offset.UiElement.posreads and moves the offset.- The drag handler remembers where the note started, then moves it by
the drag's
e.totalon every event. A drag that is canceled puts the note back. - The context handler shows a popover of colors, hung from the note.
Drag
UiElement:on_drag hears a press that moves a few UI px before it lifts,
from the mouse or a touch. e.total is the movement since the press, in
UI px of your content area, so moving an element by it keeps the element
under the finger at every zoom. e.phase is "cancel" when a drag stops
without a release, such as when the window loses focus.
Once a drag starts, it owns the pointer:
- A press on a button inside the element drags the element once it moves, and nothing under the pointer clicks.
- The drag follows the pointer out of the element and the window until it lifts.
- A slider, text field or color picker inside the element keeps its own drag, and so does a scrollbar under the mouse. On touch, a scrolling list inside keeps a movement along the way it scrolls.
- A popover or menu hung from the element is not part of it: a press there never drags the element.
Offer actions
UiElement:on_context hears each way of asking for an item's actions:
| Device | Request |
|---|---|
| Mouse | a right-click |
| Touch | a touch held half a second |
| Keyboard | the Menu key or Shift+F10 on the focused element |
| Gamepad | X on the focused element, while focus navigation is on |
e.pos is where the request was made, in UI px of your content area:
the pointer, or the center of the focused element for a key or a gamepad.
A held touch that asked for actions clicks nothing when it lifts, and a
right-click never clicks. Only the nearest element with a handler hears a
request, so a row in a list can offer its own actions ahead of the
list's.
See also
- Lay out a screen: cards, and placing an element yourself.
- Keyboard and gamepad: focus, which the keyboard and gamepad requests follow.
ui.DragEventandui.ContextEvent: what each event carries.
Reference used on this page: datumhue.ui datumhue.theme.token datumhue.ui.root UiElement:panel UiElement:button UiElement.color UiElement:label UiElement:popover UiElement.visible UiElement:on_drag UiElement.pos UiElement:on_context ui.DragEvent ui.ContextEvent