makepad/platform/tests
Kevin Boos b5bd9bc6b9
Ten fixes from moving an app onto current dev: macOS drawable/resize, the UI signal broadcast, internal drag on Linux, and log noise (#1239)
* Button: add `label_align` to center a wrapped label

`Button::draw_walk` passed `Align::default()` to `draw_text.draw_walk`, so
a label that wrapped onto more rows left-aligned them under each other even
when the button itself centered its content, and no script property could
reach that argument.

* New `#[live] label_align: Align`, mirroring the one `TextInput` already has.
* Defaults to left, so every existing button draws exactly as before.
* Pair it with a `Fill`-width `label_walk` to give the rows room to move.

* macOS: don't paint into a drawable the layer has since outgrown

The prefetching `DrawableWorker` hands back a drawable that `nextDrawable`
acquired on the previous beat. A frame that straddles a resize therefore
attaches a texture of the old size to a viewport derived from the new one.

* moving a window between displays of different DPI drew the whole UI at
  the old scale, and it stuck: a dpi change dirties the pass exactly once
* dragging a border fast left the strip the old texture didn't cover
  unpainted, which macOS shows as magenta

Check the invariant where the drawable is consumed: a texture whose size
isn't the layer's `cal_size` is dropped and one is acquired on this beat,
the way the pre-worker path did every beat. The pool was just rebuilt for
the new size, so that acquire doesn't block. Traced on the `present` topic.

* Window: don't put an app icon in the caption bar by default

`AppIcon` falls back to a generic placeholder tile for any app id makepad
ships no artwork for, so every third-party app got a meaningless icon next
to its title, and an off-centre title with it.

* `AppIcon` gains `visible`, which it had no way to express before
* the caption icon defaults to hidden; apps opt in with
  `caption_icon +: {visible: true}`

* Quiet the startup and per-decode log spam

A plain run printed ~1500 lines before anything happened.

* `zune-qoi` was the only zune crate with `log` in its default features,
  and cargo unifies that onto `zune-core`, so every zune decoder logged
  per image. zune-core's macros became real logging in this tree, so what
  used to be inert now floods the log
* memory budget, task-pool priority/summary and the Metal retained-upload
  budgets move onto the `MAKEPAD_TRACE` topics this tree added
  (`memory`, `pool`, `gpu.upload`)
* drop the studio-websocket line, which only says a disabled thing is off

* Make the UI-hang sampler opt-in

It started with every `Cx`, so a shipped app carried a thread waking ~16
times a second forever, and any stall over 250ms got the UI thread
suspended once per sample while its stack was walked.

`MAKEPAD_UI_HANG_MS` is now the switch as well as the threshold. Unset, no
thread starts and the phase guards see a null registration, which is a TLS
read and a null check.

* Wake the event loop from render workers without raising the UI signal

`Event::Signal` means "a worker has something for you" and is dispatched to
the whole widget tree. The submitter, the instance allocator and the
drawable worker raised it after every commit, so an app painting at 120fps
walked its tree 120 extra times a second. Measured in robrix: 119 signals
against 111 repaints, down to ~0.3 per frame.

They only ever wanted the loop awake, so give them `wake_ui_loop()`, which
is what `set_ui_signal` already called underneath. A dirty pass is what
keeps the paint clock armed, so nothing depends on the flag to get painted.

* Compare the resident instance bytes instead of hashing them

`immediate_payload_hash` FNV'd every byte of every dirty draw call to skip
the upload when nothing changed. That suits a few big payloads, not a 2D
frame: robrix scrolls ~1090 draw calls of ~110 bytes, and the hash cost
2.3ms a frame in a debug build to skip ~12% of 110KB of uploads.

Instance buffers are StorageModeShared, so the resident copy can just be
compared. `memcmp` stays fast in an unoptimized build, and an exact
comparison can't collide into a stale frame the way a hash can.

* Install a platform stylesheet only when an app asks for one

`current()` picked "ios"/"android" straight off `OsType`, so any app built
for a phone was silently restyled: ~270 theme tokens including the fonts,
over whatever the app had already set.

Worse, it only half-landed. `apply_theme` runs from `widgets_mod` but
`apply_widgets` runs from `script_mod`, so an app that calls the
`theme_mod` + `widgets_mod` pair got the mobile palette with desktop
metrics. Every in-tree user already calls `install` or sets
`MAKEPAD_WIDGET_STYLE`, so that variable is now the only implicit route.

* Let an internal drag deliver its pointer events on Linux and wasm

Moving internal drag handling into shared code changed it in two ways that
the macOS and old X11 paths never had, and a dock tab shows both.

The pointer event was replaced by the drag event rather than followed by
it, so no widget saw the `MouseUp`. `Tab` sets `is_dragging` on FingerMove
and clears it only on FingerUp, so a tab could be reordered once and then
never dragged again. Dispatch the pointer event first and append the drag
one, with a flag so that dispatch doesn't produce the drag event again.

A release that never moved after `start_dragging` also produced no Drop and
no DragEnd at all, so `Dock` never cleared `dragging_tab` and kept painting
the ghost. `Tab::min_drag_dist` has no default, so a press, one motion and a
release reaches it. Every other backend ends the drag unconditionally.

* Split the UI signal so makepad's own queues don't broadcast Event::Signal

`set_ui_signal` was the one wake for everything, and every platform loop
answered it by running makepad's handlers AND broadcasting `Event::Signal`
to the whole tree. So termination, the network runtime, live reload and
every pool completion (label shaping, per frame) woke every widget.

* `set_internal_signal`: the loops run their handlers and don't broadcast
* `TaskPool::submit_internal`: a job whose result makepad polls at draw
* `Event::Signal` is documented, and the loops treat the app flag as a
  superset of the internal one, so nothing left on it can regress

The scheduler keeps the app signal on purpose: `service_scheduler` re-arms
the platform timer from `call_event_handler`, which only the app half runs.
Media device changes still go through `SignalToUI::set`, whose instance API
is app-facing; they are hotplug-rare, so splitting that is left alone.

* Harden the drawable re-acquire, the internal drag and the opt-in sampler

Follow-ups from reviewing the five commits above.

* the resize re-acquire only runs while the drawable pool has a free slot.
  Exhausted, `nextDrawable` blocks the UI thread on the compositor, which is
  what the worker exists to avoid; skip the beat and stay dirty instead
* the byte compare no longer skips an item the GPU has evicted, which would
  leave it invisible in a pass that then repaints forever
* the internal drag suspends its items across the pointer dispatch instead
  of holding a flag. An unwound dispatch now ends the drag rather than
  wedging it for the life of the process, and a widget that starts a new
  drag from that dispatch keeps it instead of tripping "start drag twice"
* `tests/ui_hang.rs` opts the sampler in, since it is the thing under test
2026-09-18 09:06:31 +02:00
..
fixtures draw: geometry, shaders, WGSL, fonts, turtle/layout 2026-09-15 13:39:02 +02:00
macos_cursors.rs platform: retained draw lists, shared publications and GPU residency (DL-0..DL-5) 2026-09-15 13:40:23 +02:00
metal_alloc_probe.rs platform: retained draw lists, shared publications and GPU residency (DL-0..DL-5) 2026-09-15 13:40:23 +02:00
pool_latency.rs platform: rename the CPU simulated-GPU backend from headless to gpusim 2026-09-15 13:40:28 +02:00
texture_lifetime.rs platform: topic hunks the per-commit assembly could not place, the Sep 2–15 dev PRs (#1208–#1236) as adapted to this tree, and the zero-warning chore 2026-09-15 13:40:33 +02:00
ui_hang.rs Ten fixes from moving an app onto current dev: macOS drawable/resize, the UI signal broadcast, internal drag on Linux, and log noise (#1239) 2026-09-18 09:06:31 +02:00
ui_waker_headless.rs platform: macOS display-link, App Nap, waker, IME-adjacent event loop 2026-09-15 13:40:24 +02:00