feat(ui): popovers, tooltips, progress; the render3d backend installs itself; duplicate names caught first

- `popover`: a top layer that keeps the pointer and the keyboard, closed by a press outside or Esc.
- `title` tooltips after half a second's rest, styled by .ui-tooltip.
- `<progress>` and `<meter>`.
- Atlases take rows and number cells, and importing ludic.ui/render3d.ludic is enough to draw with
  render3d.
- The compiler reports two declarations of one name before it type-checks, so a package global
  clashing with a program's reads as that, not as 29 type errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-24 17:41:02 +03:00
parent 1dad96102a
commit 0a7525b125
24 changed files with 243 additions and 21 deletions

View file

@ -0,0 +1,32 @@
# popover.ludic - HTML's popover: `<div popover on-close="...">` sits where its position puts it
# (absolutely, in its parent) and is drawn on the top layer, over everything. While one is up the
# pointer and the keyboard are its: a press outside it, or Esc, closes it (on-close), and Tab
# walks only its own controls.
var pop_list: []UiNode = new []UiNode
function pp_find(n: UiNode, out: []UiNode) -> void {
if n.popover {
push(out, n)
return
}
for i in 0 .. len(n.children) { pp_find(n.children[i], out) }
}
# the topmost popover open in this tree, or null
function pp_top(root: UiNode) -> UiNode {
let all = new []UiNode
pp_find(root, all)
if len(all) == 0 { return null }
return all[len(all) - 1]
}
# the pointer against the popover: inside it is as usual; a press outside closes it and is spent
function pp_pointer(top: UiNode) -> bool {
let i = in_now
if not (i.down and not in_was_down) { return false }
if i.x >= top.x and i.y >= top.y and i.x < top.x + top.cw and i.y < top.y + top.ch { return false }
ui_fire(top, "close", Value.null())
return true
}
function pp_draw_top() -> void {
let list = pop_list
pop_list = new []UiNode
for i in 0 .. len(list) { fr_draw(list[i]) }
}