feat(ui): tree-owned scroll gestures and geometry hit-testing
Drag, flick and tap-vs-scroll now live on the scrollX/scrollY flag in ui.lua, so any scroll box pans with no app code. tree.hit returns the deepest node by geometry and dispatch bubbles to the nearest handler, dropping the now-unused CAPTURE flag. keyboard moves in as ui.keyboard, and embed compiles nested lib dirs to dotted module names.
This commit is contained in:
@@ -32,7 +32,6 @@ enum Type : uint8_t { BOX, TEXT, BUTTON, CUSTOM };
|
||||
|
||||
enum Flag : uint8_t {
|
||||
ROW = 1 << 0, // main axis is horizontal
|
||||
CAPTURE = 1 << 1, // swallows the taps its children missed
|
||||
INTERACTIVE = 1 << 2, // has an on_press
|
||||
DIRTY = 1 << 3,
|
||||
PRESSED = 1 << 4,
|
||||
@@ -204,8 +203,10 @@ public:
|
||||
return error == nullptr;
|
||||
}
|
||||
|
||||
// Deepest interactive node wins, so a tappable child beats its tappable
|
||||
// parent. The list is singly linked, so "last match walking forward" stands
|
||||
// Deepest node by geometry wins, so a child beats its parent and a later sibling
|
||||
// beats an earlier one it overlaps. Whether that node responds to a press or a pan is
|
||||
// dispatch's business, which bubbles up from here -- hit-testing is not fused with who
|
||||
// handles the gesture. The list is singly linked, so "last match walking forward" stands
|
||||
// in for "first match walking backward"; they name the same node.
|
||||
uint16_t hit(uint16_t id, int px, int py) const {
|
||||
const Node& n = nodes[id];
|
||||
@@ -222,9 +223,7 @@ public:
|
||||
}
|
||||
if (found != NONE)
|
||||
return found;
|
||||
if (n.flags & CAPTURE)
|
||||
return id;
|
||||
return (n.flags & INTERACTIVE) ? id : NONE;
|
||||
return id;
|
||||
}
|
||||
|
||||
bool scrolls(uint16_t id) const {
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
// @lua-preamble ---@field justify? "start"|"center"|"end"|"between"
|
||||
// @lua-preamble ---@field row? boolean
|
||||
// @lua-preamble ---@field at? table Absolute-position fields.
|
||||
// @lua-preamble ---@field capture? boolean
|
||||
// @lua-preamble ---@field interactive? boolean
|
||||
// @lua-preamble ---@field label? string
|
||||
// @lua-preamble ---@field font? ScreenFont
|
||||
@@ -173,8 +172,6 @@ int create(lua_State* state) {
|
||||
uint8_t flags = 0;
|
||||
if (readFlag(state, 2, "row"))
|
||||
flags |= ui::ROW;
|
||||
if (readFlag(state, 2, "capture"))
|
||||
flags |= ui::CAPTURE;
|
||||
if (readFlag(state, 2, "interactive"))
|
||||
flags |= ui::INTERACTIVE;
|
||||
if (readFlag(state, 2, "scrollX"))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -173,7 +173,8 @@ int main() {
|
||||
"assert(x == 4 and y == 4 and w == 312 and h == 16)\n"
|
||||
"assert(tree.getLabel(label) == 'Hello')\n"
|
||||
"assert(tree.hit(root, 10, 40) == button)\n"
|
||||
"assert(tree.hit(root, 10, 200) == nil)\n"
|
||||
// Hit is geometry: a point inside root but over no child answers root, not nil.
|
||||
"assert(tree.hit(root, 10, 200) == root)\n"
|
||||
"assert(tree.focusFirst(root) == button)\n"
|
||||
"assert(tree.getFocus() == button)\n"
|
||||
"assert(tree.moveFocus(root, 'up') == button)\n"
|
||||
@@ -227,6 +228,14 @@ int main() {
|
||||
"assert(bx == 0 and by == 0)\n"
|
||||
// Row 1 is scrolled above the box, so a tap at the top hits row 2.
|
||||
"assert(tree.hit(list, 10, 10) == rows[2], 'scrolled-out row is not hit')\n"
|
||||
// Hit is pure geometry: the deepest node covering the point, else the container. A
|
||||
// scroll pan finds its box by bubbling up from here, not by a flag on hit.
|
||||
"local bare = tree.create(nil, {type = 'box', w = 'fill', h = 'fill', scrollY = true})\n"
|
||||
"local child = tree.create(bare, {type = 'box', w = 100, h = 900})\n"
|
||||
"assert(tree.layout(bare, 0, 0, 320, 240))\n"
|
||||
"assert(tree.hit(bare, 10, 10) == child, 'deepest node by geometry wins')\n"
|
||||
"assert(tree.hit(bare, 200, 10) == bare, 'the container answers where no child covers it')\n"
|
||||
"tree.dropScratch()\n"
|
||||
// Clamped, never past the content's far edge or before its start.
|
||||
"tree.setScroll(list, 9999, 9999)\n"
|
||||
"local cx, cy = tree.getScroll(list)\n"
|
||||
|
||||
Reference in New Issue
Block a user