* Fix rendering, gradient, sampling, etc issues on older GPUs
* Fix bug in `box_y` sdf function, which caused gradients to be split
into two bands incorrectly. Mostly a problem on lower-res screens.
* Use per-texture filtering instead of GL sampler objects on Linux,
especially for Mesa drivers that ignore min filter samplers.
This should help prevent blocky/pixellated things like emoji/avatars
* Improve rendering sharpness on low-DPI screens (icons, emoji, images)
On 1.0-DPI screens, emoji/SVG-icons/avatars were minified without
adequate sampling and SVG AA was sub-pixel, producing blocky/aliased
output. This reworks each path and adds optional full-window SSAA.
SVG icons (device-aware AA + round caps):
- draw_svg.rs/draw_vector.rs/render.rs: size the fill & stroke AA
fringe and the curve-flatten tolerance in DEVICE pixels (≈constant
regardless of icon size), so edges resolve via the analytic
d/fwidth coverage and curves stay smooth at any scale; re-tessellate
on scale change.
- triangulate.rs: thread the flatten tolerance through path fill/stroke.
- tessellate.rs: emit round caps as a solid disc (u=0.5) instead of a
radial fade that collapsed to a square at small sizes.
- widgets/icon.rs: don't clip the Icon to its Fit bounds, so round
caps that extend past the box render fully instead of being sheared.
Emoji:
- glyph_raster_image.rs/rasterizer.rs: rasterize color emoji near the
on-screen size with an alpha-weighted box downscale (geometric-mean
scale factor) instead of the font's native PNG strike.
Images / avatars (mipmaps):
- image_cache.rs/texture.rs/draw_list.rs/lib.rs: optionally emit a CPU
mipmap chain (VecMipBGRAu8_32) for non-animated images so minified
avatars/thumbnails sample cleanly. Env-gated MAKEPAD_IMAGE_MIPMAPS;
default on for GL on Linux.
- metal.rs: real per-level mip upload. d3d11.rs/vulkan.rs/web_gl.rs:
safe single-level fallback (no crash; real mips TODO).
Full-window supersampling (optional):
- window.rs: render the whole UI into an offscreen target at
MAKEPAD_SUPERSAMPLE× device resolution and downscale-resolve into the
window. Default 2×, env-tunable, 1× disables. Modeled on the existing
GaussStack render-to-texture path.
* don't unconditionally enable supersampling SSAA of 2x by default
it's too expensive and too slow for most older devices
* cleanup, reduce comment verbosity
* improve SVG anti aliasing
* Windows: fix laggy/juddery scroll performance
- Pace the render loop to the display refresh using a DXGI frame-latency
waitable object and present with vsync, replacing the free-spinning,
uncapped Poll loop that caused uneven scroll cadence.
- Coalesce consecutive WM_MOUSEMOVE messages and paint once per loop pass
to stop the judder when moving the mouse during fling deceleration.
- Cache get_dpi_factor() and the WM_NCHITTEST WindowDragQuery result to
avoid per-mouse-move GetDeviceCaps syscalls and widget-tree hit-tests.
- Throttle XInput/DirectInput polling of empty/disconnected controller
slots, which was stalling the UI thread.
- Rework the momentum fling to a native exponential model with a
frame-interval EMA, and stop the tail auto-scroll from fighting an
active fling/drag.
- D3D11: update the glyph atlas and image textures in place via
UpdateSubresource instead of recreating them on every change, and
spread D3D11 shader-object creation across frames.
- Slug atlas: only force a full re-layout on a width change; append rows
on height growth.
* Windows: correctness fixes from review (off the scroll hot path)
- d3d11: close the DXGI frame-latency waitable HANDLE in Drop (it was
leaked once per main-window lifecycle and the field comment was wrong);
keep popup swap chains at frame-latency 1; track the waitable-swapchain
flag for ResizeBuffers instead of inferring it from the handle; present
without the vsync interval during a live resize.
- windows.rs: poll game input on the idle signal tick so a gamepad button
can be serviced while the app is otherwise idle.
- win32_window / window: invalidate the WM_NCHITTEST / WindowDragQuery
caches on window move and on a caption relayout, with a generation
counter guarding against a reentrant invalidation being clobbered.
- windows_game_input: detect a controller already plugged in at launch via
a one-shot full scan on the first poll, probe slot 0 (Player 1) first,
and offset the DirectInput enumeration so it never stacks with the
XInput probe.
- comment/doc corrections.
* Image cache: accept any Arc<D: AsRef<[u8]> + ?Sized> for async image data
The load_image_from_data_async family required Arc<Vec<u8>>, forcing callers
that already hold the bytes as Arc<[u8]> (e.g. a content-addressed media cache)
to copy the whole buffer via .to_vec() just to satisfy the type. Generalize the
data parameter to Arc<D> where D: AsRef<[u8]> + ?Sized, so those callers can pass
their existing Arc by refcount-clone with no byte copy. The decode path only ever
borrowed the bytes (&[u8]), so this is purely a signature relaxation; existing
Arc<Vec<u8>> callers are unaffected (D = Vec<u8>).
* Linux: GL glyph-atlas in-place texture update + X11/Wayland mouse-move coalescing
* Scroll: unified fling model + native trackpad momentum deceleration
Share one kinetic-scroll model between PortalList and ScrollBar (and thus
ScrollXView/ScrollYView/ScrollXYView) via a new widgets/src/scroll_motion.rs:
- Touch-drag flicks use an iOS-style exponential self-decay, frame-rate
independent via a per-frame integrator with dt smoothing.
- Trackpad scrolling applies the OS momentum directly while fast (responsive,
full native speed), then hands off to a gentler self-decaying tail once it
slows past a threshold, so the deceleration is longer and smoother than the
OS's short, choppy tail. Handoff is seeded at the current speed for a
continuous transition; the seed is clamped against degenerate event timing.
- Add ScrollPhase to scroll events, mapped from NSEventPhase/momentumPhase on
macOS and wl_pointer AxisStop on Wayland; None elsewhere (wheels/X11/Windows
behave as before). MAKEPAD_RAW_TRACKPAD_MOMENTUM=1 bypasses the smoothed tail.
- A press catches an in-progress fling (stops the scroll, consumes the press so
it doesn't also activate a child), matching iOS/Android/macOS.
* Shader codegen: prefix Metal/WGSL locals to avoid reserved-word collisions
The Metal/WGSL backends emitted user-declared shader locals verbatim, so a
local named after a reserved type keyword (e.g. `half`) produced invalid
shader source and failed to compile at runtime. Prefix them with `l_` like the
HLSL/GLSL backends already do.
* TextFlow: don't panic on unbalanced HTML close tags
end_code/end_quote unwrapped the area stack, so a stray `</pre>` or
`</blockquote>` in untrusted content (e.g. a chat message) panicked. Return
early instead, and drop a vestigial per-list-item area-stack push that leaked
an entry and could hand a stray close tag the wrong block's area.
* Scroll: expose fling decel, handoff threshold, & tail-decel as `#[live]` fields
This allows app devs to override the scroll feel per-widget in the DSL,
or globally by overriding the base widget's defaults — verified that a DSL
override takes effect.
* Windows: fix frame pacing, paste crash, and wheel input backlog
- Wait on the frame-latency waitable right before each window's vsync
present instead of on every Paint, so input no longer stalls behind
waits that have no matching present. Drain leftover credits after a
live resize.
- Pasting when the clipboard has no text no longer panics.
- The poll loop now handles up to 32 messages (2 ms) per frame and
merges consecutive mouse-wheel messages, so fast wheels can't build a
backlog that keeps scrolling after the gesture ends. Sleep 1 ms when a
frame presents nothing so animation polling doesn't spin a core.
* Image: don't build unused mip chains; fix stale images in recycled widgets
- Only build the CPU mip chain when a backend actually uploads it
(Metal, behind its env var), and build it on the decode thread instead
of the UI thread. Linux GL still gets its mipmaps via glGenerateMipmap
and now retains less CPU memory per image.
- Recycled Image widgets no longer show the previous item's image or
apply an old decode result. Placeholder textures set via set_texture
(like blurhashes) stay visible while the real image decodes, and a
failed load clears the widget instead of leaving old content up.
* Widgets: avoid needless caption redraws; cheaper PortalList height tracking
- Label::set_text does nothing when the text hasn't changed, and the
window caption title is only synced when it actually changes, so mouse
moves and animation ticks no longer redraw the whole window every
event. The caption centering padding requests its own redraw now.
- PortalList records item heights only when new or changed, and only
re-applies the default height after it drifts by half a pixel, so big
lists don't walk every unmeasured item on every scroll frame.
* Text: cache layouts of long texts; stop cloning glyph outlines every frame
- The layout cache now accepts texts of any length (long messages and
code blocks used to re-layout on every scroll frame). It is a real LRU
with a byte budget on top of the entry cap, and texts drawn in the
current frame are never evicted, so one heavy frame can't thrash the
cache into a permanent miss cycle. The shaper cache is LRU now too.
- Glyph outlines are shared via Rc, so drawing a cached glyph no longer
copies its command list, and outline complexity is computed once when
the outline is built instead of every frame.
* Html: fix stale links/spans in recycled widgets and <details> renumbering
- set_text only rebuilds when the content actually changed, so a
recycled link can't open the previous message's URL, and re-setting
identical content keeps the user's <details> open/closed state.
- Custom widgets and <details> are keyed by their node index instead of
a visit-order counter, so toggling a collapsed <details> can't
renumber the widgets after it and rebind them to the wrong nodes.
item_with_scope also recreates its widget when the template changes.
* Linux: fixed-distance wheel scrolling; Wayland frame-callback pacing
- Wheel scrolling moves a fixed 60 px per detent on X11 and Wayland
instead of a timing-based guess that flipped between 12 px and 240 px
depending on how events batched. Wayland reads real detent counts via
AxisValue120 (wl_seat v9, with AxisDiscrete as the older fallback) and
maps keymaps MAP_PRIVATE as v7+ requires. Touchpads are unchanged.
- Wayland frames are paced with wl_surface frame callbacks and swap
interval 0, so redrawing a hidden or minimized window can no longer
hang the whole app inside eglSwapBuffers (compositors withhold frame
callbacks for hidden windows). Windows with a callback in flight skip
presenting and stay dirty; X11 keeps vsync exactly as before.
* Text: bigger layout cache budget, reclaimed at the end of each frame
A maximal ~60 KB message lays out to roughly 4 MB of glyphs, so the 4 MB
budget couldn't hold even one alongside a normal screen. Raise it to
16 MB, and run eviction at the end of every frame so memory over the
budget is freed one frame after its content leaves the screen, instead
of lingering until some later layout happens to insert a new entry.
* Fix oversized uniform slices: the array lengths were in bytes, not f32 elements
* Wayland: flush buffered mouse motion before scroll events, and drop motion for closed windows
* GL: fall back to non-mipmapped filtering when glGenerateMipmap fails on strict GLES3 drivers
* Text: bucket emoji raster scales so zooming reuses atlas slots instead of re-decoding every step
* Image: add has_content() and record texture provenance on cache-hit loads too
* Scroll: native trackpad momentum, Chrome-model bounce; presses catch motion, never click children
* Dock: redraw the newly selected tab immediately when the active tab is closed
* Html: standard link colors with pressed precedence; skip re-parsing unchanged text; color setters
* Image: don't redraw on cache-hit loads of the already-bound image (per-draw reloaders looped forever)
* Scroll: log macOS momentum-end phase bits to check Cancelled (touch-cut) vs Ended (natural fade)
* Scroll: momentum state machine; flicks survive pagination; edge sentinels & once-per-frame actions
* Scroll: remove the MAKEPAD_SCROLL_DEBUG diagnostics
* Scroll: time-based fling velocity window; pointer fan-out guard; parked flings survive pagination
* PortalList: optional reached-start/end margins (Some(0) default); repositioning re-announces the edges
* Linux/Wayland: honor UI zoom across window resizes; scale caption bar with zoom
Keep the wayland-side window geom in native units so the zoomed dpi isn't
read back as "native" on the next Configure — UI zoom no longer resets or
flickers on maximize/tile. Also let the caption bar height scale with the
zoom (pin to native only on macOS, where the buttons are OS traffic lights).
* Windows: D3D11 fixes for drawlist mgmt
trying to help with the `new_batch` bugs
* draw_list.rs — `set_zbias` now returns whether it changed
* d3d11.rs — uploads draw_call_uniforms on the given condition:
`uniforms_dirty || zbias_changed || buffer.is_none()`
and the zbias advance is hoisted above the early-continues
* Scroll: hard flicks carry farther and boost on re-flick; iOS-style touch rubber band with per-edge bounce gating; Android fling spline behind a flag
* Splitter: redraw both pane subtrees on drag; cached views keep fresh fixed sizes in the dirty check
* iOS: re-deliver window-geometry changes dropped by re-entrant UIKit callbacks; Init only ever from the first draw
* final cleanup fixes. Ensure all examples, experiments, `studio` all work
* Image: skip re-parsing an SVG that is already shown, keyed on the caller's shared bytes
* CachedView: fix upside-down offscreen texture on GL/GL-ES
GL/GL-ES store offscreen FBOs bottom-up, unlike Metal/D3D/WGSL.
The DSL->script-shader migration switched the CachedView composite to plain
.sample() (non-flipping sample2d on GL), so cached views rendered
vertically mirrored on GL/GL-ES; Metal was fine on macOS.
Add a `sample_rt` script sampler (emits the V-flipping sample2d_rt on
GLSL, plain no-flip sample elsewhere) and use it in the CachedView and
CachedRoundedView composites.
* Fix `AdaptiveView::redraw()` to actually do something
* Run script-VM gc in the desktop and mobile event loops, not just macOS
Only macOS was calling the script VM's garbage collector.
Now we call it everywhere, based on the original implementation in macOS.
* Limit cache growth for text/script, reclaim memory after gc
This PR includes several misc improvements to reduce memory usage and/or
return unused memory to the OS properly.
- slug atlas: reset the append-only curve buffer past a cap (mirrors the raster
atlas reset: cleared at the prepare_textures boundary, forcing a rebuild), so
it no longer accumulates every distinct large glyph ever rendered.
- script heap: in gc(), truncate the String reuse pool and shrink over-allocated
free-list/slot capacity (never moves a live slot, so all refs stay valid).
- font outline cache: cap per-font distinct-glyph entries (clear-on-exceed).
- image cache: evict Loaded entries past a cap; widgets keep their own texture
clones so displayed images are unaffected, and in-flight loads are preserved.
The string intern table is intentionally left unbounded: it backs stable
pointer-based FontId/FontFamilyId, and is bounded by the few distinct font names.
* Support standard keyboard navg shortcuts/keys in TextInput
Implement platform-standard TextInput navigation and deletion behavior,
including Home, End, PageUp, PageDown, word movement, line/document
boundaries, and Shift-based selection.
* Use Apple Option/Cmd conventions on Apple targets
* Use Ctrl conventions on non-Apple targets
* Web accepts both shortcut styles for now, since we don't have a way
to query the host OS from within a makepad web env.
Also, be extremely careful to ensure that we respect Unicode grapheme boundaries
when doing all the selection/navigation logic.
Fix `Delete`, which was erroneously handled before.
Add lots of missing keys in Linux X11 & Wayland backends, e.g.,
Home, End, Delete, Insert, PageUp/PageDown, and arrow keys
* Add `CropToFill` image fit variant, improve ImageFit docs
This allows you to easily achieve the "centered cropped fit" that most apps
want for things like avatars or small thubmnails that get masked.
* Detect and support hardware keyboards, distinguish from soft/virtual kbd
Mimic desktop behavior on mobile systems as much as possible.
This is esp important for tablets like iPad OS where you're more likely
to have a real physical keyboard attached.
For iOS:
* Arrow keys and Home/End/PageUp/PageDown navigate and auto-repeat
at the system-defined rate (connected via `UIKeyCommand`)
* Cmd+Enter to submit a `TextInput` and Cmd+C/X/V clipboard shortcuts now work.
* Ensure the pop-up diacritic/accent menu is properly placed using a hidden
`UITextInput` native widget, which acts as a sort of "proxy"
* Proactively drain `ShowTextIME` after each draw so the IME position will be
properly updated after each keystroke.
* Importnatly, don't mark the IME dismissed when a hardware keyboard is attached.
For both iOS & Android:
* Add a `has_physical_keyboard()` detection mechanism across both backends,
and fix platform-specific key repeat behavior
For Android:
* Ensure clipboard cut/copy works using the same Ctrl shortcuts (API 26+)
Soft/virtual keyboard/IME changes:
* For multiline TExtInputs, a soft keyboard Enter/Return key will always just
insert a new line, to avoid complexity with keyboard shortcut cfgs.
* CJK keyboard character selection should also be properly positioned now
* minor optimization to avoid re-setting IME pos if it didn't change
* Ensure inline composition is still shown in TextInput on all platforms
This is mostly relevant when using CJK and other similar IMEs.
Previously Makepad didn't shown any "echoes" of the single latin chars
that the user would type, but it would correctly input the selected CJK
glyph. So if you typed `nihao` and then selected `你哈`, then you would
see the proper chinese characters but not the latin "nihao".
Tha't s a bit confusing while typing.
Summary of the fixes per platform:
macOS:
- `set_marked_text` now forwards the marked (composition) text to the
focused TextInput with `replace_last = true`; previously it only stored
it in an ivar. `unmark_text` clears the preview, and both it and
`insert_text` share a new `clear_marked_text_ivar` helper so a commit
doesn't emit a second, destructive text-input event.
Windows:
- Add `WM_IME_COMPOSITION` handling: commit `GCS_RESULTSTR`
(`replace_last = false`) and show `GCS_COMPSTR` inline
(`replace_last = true`), and clear on `WM_IME_ENDCOMPOSITION`. The
message is consumed so DefWindowProc neither draws its own composition
window nor synthesizes a duplicate WM_CHAR for the result.
- Extend the vendored `windows` binding with `ImmGetCompositionStringW`,
`GCS_COMPSTR`/`GCS_RESULTSTR`, and `WM_IME_COMPOSITION`/
`WM_IME_ENDCOMPOSITION`, which it didn't previously generate.
Linux (Wayland):
- Handle the `zwp_text_input_v3` `PreeditString` event (previously empty)
and apply the double-buffered preedit/commit state on `Done`, in the
protocol-mandated order (commit, then preedit), clearing the preview
when a cycle carries no preedit.
Linux (X11):
- Create the input context with XIM on-the-spot (`XIMPreeditCallbacks`)
and forward the preedit string from the draw/start/done callbacks,
falling back to `XIMPreeditNothing` if the IM server doesn't support
callbacks. Callbacks run inside `XFilterEvent`, so they only mutate a
thread-local that the event loop drains into the widget afterward,
avoiding re-entrant access to the app.
Android:
- Don't mark the IME dismissed when a physical keyboard is attached
(mirrors the iOS guard). Android was unconditionally calling
`text_ime_was_dismissed()` on soft-keyboard hide, which tore down the
IME connection that hardware-key composition relies on.
* iOS: replace custom `UITextInput` with a native `UITextView`
`UITextView` is a full system-native keyboard client, so we get all the
major features for free: language HUD pill and the complete globe/Ctrl+Space
shortcut to cycle between IMEs/languages.
Makepad basically just mirrors the state of the system native text view,
via the `full_state_sync`, but the actual native text view is kept invisible
so it doesn't interfere with what we render in Makepad's TextInput.
Notably, the Full Keyboard Accessibility setting now does work properly,
whereas it did not before with our UITextInput-based approach.
We also make sure that arrow keys, nav keys, auto-repeat, and modifiers
are properly hanlded so we can retain the expected kbd shortcuts,
like other desktop platforms.
* iOS: remove the old `UITextInput` connection with the Makepad TextInput
We've now switched to the native UITextview, so we don't need this any more.
* iOS: fix desync during fast typing
Ensure there's no race between the native UITextView and
Makepad's TextInput, as the Enter/REturn key needs special handling
w.r.t. how `pressesBegan` gets it (From a real hardware kbd).
* TextInput: more iOS integration, and text input types
more native integration for things like username/password,
new password fields, email, address, URLs, etc.
These tell iOS to change the keyboard layout/type for the text input.
* iOS: don't let Full Keyboard Access focus on our hidden native cursor
* cleanup
* iOS/TextInput: fix perf issues
* iOS TextInput: more fixes for read-only efficiency, and filtered input
Also port some of these fixes to Android's IME integration layer
* iOS/TextInput: hide the native caret iOS draws during autocorrect
but still allow the "decline autocorrect" bubble to popup where that
hidden caret is located (and the CJK candidate window in the same spot)
* Avoid script VM re-entrant panic: defer animator_cut/play if script VM is held
`animator_cut` / `animator_play` call `cx.with_vm`, which panics
(*"Script VM swapped off"*) when invoked during an apply walk — e.g. a
widget's `on_after_apply` on `ScriptReapply` / `Reload` — because the VM
is already taken for the duration of that walk's enclosing `cx.with_vm`.
- The derive macro's `animator_cut_scoped` / `animator_play_scoped` now
check `cx.is_script_vm_held()`; when held, they queue the op
(`defer_cut` / `defer_play`) and return instead of re-entering the VM.
- `animator_handle_event_scoped` replays the queue via `flush_deferred`
on the next frame, once the VM is free.
The defer path runs **only** in the formerly-panicking case, so VM-free
animations are byte-for-byte unchanged.
Also adds a re-entrancy-naming panic (`VmHolderGuard`) plus
`Cx::try_with_vm` / `Cx::is_script_vm_held` for diagnosing and handling
this class of bug.
* Better spacing/positioning for IME popups like the CJK candidate menu
applied to all platforms, but primarily an issue on macOS/iOS.
The candidate/conversion window (e.g. CJK pinyin) was covering the line of
text being composed. Carry the caret-line rect (not just a point) through
ShowTextIME and feed each backend its native "keep clear of this line" API,
so the OS places the candidate directly above/below the line with a small gap:
- macOS: firstRectForCharacterRange returns the line rect via AppKit
convertRect:toView:nil + convertRectToScreen (drops the hand-rolled
screen-coord math + fudge offsets); invalidate on caret move.
- Windows: ImmSetCandidateWindow with a CFS_EXCLUDE line rect.
- Wayland: set_cursor_rectangle with the real line rect.
- X11: XNSpotLocation/XNArea at the line.
- iOS: return the true composing-line box from firstRectForRange so iOS flips
around the real edges (consistent at any screen position) instead of a
degenerate point; only while marked text is active, to avoid an oversized
autocorrect highlight when typing normally.
* Fix Linux X11 behavior: Ctrl-based kbd shortcuts didn't work in TextInput
also trying to fix X11 behavior for positioning the CJK candidate window,
turns out there was an X11 bug for Ubuntu 22 and older so it's not always
possible, but we can attempt a workaround if errors occur (based on that,
we try to auto-detect the version of X11)
* fix X11 event loop latency by draining only a max of 64 events before redrawing
still working on X11 CJK candidate window positioning...
* add logs to X11 ime to figure out wtf is going on
* more robust fallbacks for X11 CJK candidate window positioning... grr
* maybe try to set the XFontSet attribute? for CJK candidate positioning
* positioning works now but there is a bit of overlap still
* now that X11 CJK candidate positioning works in some cases,
we need to pass the full rectangle containing the current line of text
to the X11 library so that it can position the window both on top
and beneath the current line of text, if needed.
* tweaking X11 CJK candidate positioning
* abandon the screen-positioning heuristic
Instead, we just send the bounding rect of the current text character
and hopefully let the X11 platform libs decide where to put the
CJK candidate popup
* add more spacing to the bounding rect on X11
* tweak for a bit more space between CJK candidate window
* more tweaks, rect height isn't being respected for some reason...
* attempting to add more instrumentation to figure out wtf is going on with X11 CJK positioning
* remove bad instrumentation that was causing freezes. ugh
* different approach for IME placement on X11
* previous positioning attempts for X11 didn't work.
New strategy: let it be positioned, and then try to move it
* still trying to fix X11 CJK candidate window positoining...
* trying to find CJK candidate window with X11 queries (To move it)
* abandon window scanning approach
* better approach, now just tweaking it
* fix one case where the candidate window was flipped but it pointing too low
* tweaking more
* trying to fix above-text line positioning
* still trying to tweak CJK candidates ABOVE the text line
* be more conservative when guessing whether X11 will show the CJK candidate above or below
* improve size heuristic for CJK candidate height
* calling X11 as complete now. jfc. Cleanup, remove debug logs, etc
* Simplify tooltip logic, fix positioning to respect safe inset areas
And other misc positioning/formatting fixes, like wrapping
after a hard line break, as well as ensuring that the callout
arrow thing itself is centered (to the fullest extent possible)
w.r.t. the rest of the tooltip body.
Basically, now it looks good again.
* Fix CheckBox/Toggle `set_active` to animate like others
All other widgets allow you to pass an `animate` arg when setting
them as active, except CheckBox (and by proxy, its wrapper Toggle).
This is necessary for proper non-animated thigns like restoring the
state of a toggle from persistent storage, or other similar examples
where you don't really want the animation to occur (because that'll
look like the user did it accidentally or some kind of phantom movement).
* PortalList: pass "touch stop" (FingerUp) events to children, always
I had recently implemented a feature where PortalList would not pass
events down to its children if those events were being direclty handled
by the PortalList as part of its scroll-action. That was generally correct,
but it missed one rare case where a child widget was waiting on a
finger up (touch stop/release) for something like stopping a hover/down
animation.
So the child, like a button, could capture the initial FingerDown
but never the FingerUp, so they'd get stuck on the hover or down
animation. This fixes that issue by passing FingerUp-causing events
down to the child widgets.
Note that the children must use `was_tap()` on the FingerUp in order
to handle a regular click -- but they should have already been doing that.
So this doesn't break anything, it's just strictly a proper fix
for something that i should've covered previously.
* Separate apply-reload and script-reapply into different concepts
The goal here is to differentiate between "applies" that change the actual
Splash script "DSL" (i.e., the template) from a "re-apply" that doesn't
change the DSL template but does change runtime heap objects.
Mostly, we want to ensure that LiveEdit (the former) is different from
things that require heap updates (the latter), such as changing a theme
value or doing screen rotation that changes safe inset areas on mobile.
I don't know that I love this approach as a permanent solution,
but it's a good stepping stone until we can redesign LiveEdit/Apply
to funnel all of these various events through the same singular system.
We probably want to use different attributes on widget fields in order
to have more fine-grained control over what happens on an Apply action.
Generated list of brief details here:
* `Apply::ScriptReapply` variant + `is_script_reapply` /
`is_live_edit_reload` predicates; `Event::ScriptReapply` now applies
via `Apply::ScriptReapply`.
* `String` / `ArcStringMut` `script_apply` early-return on
`ScriptReapply`.
* Codegen: `#[deref]` runs before `#[apply_default]`'s recursive call
so animator state wins over template defaults.
* `Animator::script_apply_default` returns `state_object` on
`ScriptReapply`; new `current_state_apply()` helper for
`on_after_apply` hooks.
* `Cx::request_live_edit()` + `pending_live_edit_request` for primitive
heap mutations (safe-area insets baked into `script_mod!`
expressions).
* `handle_live_edit()` returns `LiveEditTrigger {None, FileChange,
Manual}`; `run_live_edit_if_needed` skips shader-cache reset and
same-tick `ScriptReapply` follow-up for `Manual` (fixes ~1s rotation
lag).
* iOS/Android post-event hook now drains both flags via
`run_live_edit_if_needed` (was firing `LiveEdit` indiscriminately).
* `Window` `WindowGeomChange` uses `request_live_edit()` for
safe-area.
* `StackNavigationView` gains `runtime_title` field re-asserted in
`on_after_apply`; new `StackNavigation::set_title` API.
* `app_main!` collapses 4 duplicate platform branches into a shared
`_app_main_event_closure!` macro.
* Switch to SLUG/DrawGlyph font drawing stack (via Codex)
* Improve same-frame new glyph caching and SLUG packed instances
* avoid performance regression by batch updating slug atlas cache
* TextFlow: separate SLUG glyph batches to avoid interleaving HTML text drawing
* SLUG optimization: append instead of a full clone each generation
* trying out codex perf fix for linux wayland
* Disable SLUG glyps on Linux for now
* Fix Linux SLUG rendering, warmup, and promotion behavior
- re-enable Linux SLUG through a separate Linux-only DrawText helper
instead of bloating the normal DrawText shader path
- preserve widget text styling on Linux SLUG by syncing common DrawText
state into the helper, including base colors, gradients, and interactive
states such as hover, focus, down, active, pressed, drag, empty, and
disabled
- keep normal Linux UI text on the raster/MSDF path and only switch to
SLUG above the Linux cutoff, while still falling back cleanly when SLUG
data is unavailable
- fix Linux SLUG glyph placement so promoted text uses the correct glyph
origin, layout position, and atlas packing
- add progressive SLUG warmup on Linux by budgeting glyph generation and
uploads across redraws and falling back to raster/MSDF until SLUG data
is actually ready
- lazily register and prewarm the shared Linux SLUG helper so app startup
and first-use latency stay low
- opt only the Linux SLUG helper into async GL shader compilation and use
parallel shader compile support when available, avoiding the large
startup and first-tab stalls seen before
- fix Linux runtime shader issues caused by copied widget text shaders and
custom get_color logic by using a shared helper shader with the expected
text-state inputs
- stabilize Linux SLUG promotion by preventing stale retained areas,
cleaning up raster/helper ownership correctly during draw, and
shadow-promoting the first ready SLUG frame before making it visible
- eliminate the visible SLUG handoff flicker so Linux text now switches
from raster/MSDF to SLUG without freezes or noticeable visual artifacts
- add a dedicated UIZoo SLUG tab with side-by-side below-cutoff and
above-cutoff examples, plus diagnostic cases for plain labels,
gradients, custom text shaders, and glyph/color probes
- keep the final diff focused by removing unrelated formatting-only churn
from the worktree during cleanup
* Tighten Linux SLUG promotion and helper sync
- make Linux SLUG promotion state local to each DrawText instance
instead of using a global per-redraw gate
- cache Linux SLUG helper shader field intersections so helper state
syncing avoids repeated per-draw allocations and linear membership checks
- harden the shadow-promotion fallback path so failed helper batching
only takes a single raster fallback path
- preserve DrawText memory alignment after adding Linux SLUG bookkeeping
* try to fix emoji on Android
* emoji fix take 2
* uizoo example: allow touch/drag scroll. Don't panic in FileTree demo
* Fix emoji on Android
* Windows: async HLSL shader compile + extend SLUG helper path to Windows
Fixes two major performance issues on Windows that made uizoo unusable on
first launch:
1. **60-75s startup stall** caused by synchronous `D3DCompile` of ~30+
SLUG-bearing text shader variants on the UI thread before the window
could present.
2. **3-4s hang when opening the SLUG tab** caused by synchronous compile
of the fat DrawTextSlug helper shader the first time a SLUG glyph was
needed.
Also fixes a latent HLSL-only `CreateInputLayout` E_INVALIDARG crash
triggered by shaders with >26 instance inputs (exposed by DrawTextSlug).
`DrawTextLinuxSlug` → `DrawTextSlug` and all `linux_slug_*` /
`LinuxSlug*` symbols dropped their `Linux` prefix. Cfg guards expanded
from `target_os = "linux"` to `any(target_os = "linux", target_os = "windows")`
so Windows now:
- uses the same lean base `DrawText` shader (SDF/MSDF only, no SLUG
curve-solver HLSL inlined)
- uses a separate `DrawTextSlug` helper shader for the SLUG path
- has the same progressive glyph-build budget and DPI cutoff
(`default_slug_new_glyphs_per_redraw`, `default_slug_min_dpxs_per_em`
in `fonts.rs` now match `OsType::Windows` alongside the Linux variants)
- falls back to raster/MSDF while the SLUG helper shader or its glyph
data isn't yet ready
macOS/iOS/Android/WASM paths are untouched — they still use the fat
all-in-one `DrawText` shader via `cfg(not(any(linux, windows)))`.
Added `AsyncHlslCompile` in `d3d11.rs` and an `async_hlsl_compile` field
on `CxOs`. Shaders flagged `async_compile: true` (the SLUG helper) now
dispatch to a background thread per shader via `std:🧵:Builder`
(named `hlsl-compile-<id>` for debugging). Workers call `D3DCompile`
off the UI thread, write the resulting DXBC to the on-disk cache, and
send only a status result (not the bytes themselves — SLUG is ~240 KB
and ferrying it through the channel is wasteful) back via an mpsc
channel guarded by a `Mutex`.
`hlsl_compile_shaders` drains completed results at the top of each
call, constructs `CxOsDrawShader` objects on the main thread (the
bytes come from the cache-hit path in `CxOsDrawShader::new`), and
triggers `redraw_all()` so widgets whose shaders just became ready
get re-rendered. The existing `sh.os_shader_id.is_none()` guard in
`render_view` handles skipping the draw call while a shader is
pending.
Added `Cx::is_draw_shader_window_ready()` on Windows for the SLUG
helper's readiness check; on Windows it's simply
`os_shader_id.is_some()` since HLSL compile is either synchronous
(cache hit) or tracked via the async path.
Cold-start still compiled 30+ shaders synchronously (parallelized via
`std:🧵:scope`) which took ~5-10s because FXC's per-call speed is
the bottleneck and parallelism helps less than expected. Now
`hlsl_compile_shaders` partitions queued shaders by cache state:
- cache hit → sync path: disk read + D3D11 object creation, a few ms
- cache miss OR `async_compile: true` → async path: worker thread
On a fully cold cache, every shader is a cache miss → the window
presents on the first frame with no compile work on the UI thread.
Widgets fill in over the next ~1-2s as their shaders become ready.
On a warm cache every shader is a hit → instant startup as before.
Added `shader_bytes_cached()` for cheap existence checking of the
cache entries.
`d3d_compile_hlsl` now passes `D3DCOMPILE_SKIP_OPTIMIZATION`. FXC's
optimizer is what makes individual compiles burn hundreds of ms to
seconds on text shaders with loops; UI shaders don't benefit enough
from it to justify the cold-cache cost. If a specific shader is later
shown to be a runtime hotspot, the fix is to recompile it optimized
on a background thread and hot-swap, not to pay the cost upfront for
every shader.
Bumped `CACHE_KEY_VERSION` to 2 so pre-existing `.dxbc` blobs compiled
with the old flags are invalidated cleanly on upgrade.
`d3d11.rs` used its own `index_to_char(i) = i + 'A'` which produced
invalid HLSL semantic names (`[`, `\`, `]`, …) past 26 inputs, while
the HLSL generator in `shader_hlsl.rs` already used a correct
multi-character scheme (`A..Z, AA..AZ, BA..`). `CreateInputLayout`
returned E_INVALIDARG because the names didn't match. Replaced with
`makepad_script::shader_hlsl::index_to_semantic` so both sides of
the binding agree.
This was a latent bug — no existing shader had >26 instance inputs
until `DrawTextSlug` (which inherits many interactive-state fields
from Label-derived shaders). Linux/GLSL is unaffected because GLSL
binds by identifier, not semantic name.
- Hoisted `d3d_compile_hlsl`, `hlsl_cache_key`, and
`get_or_compile_shader_bytes` out of `CxOsDrawShader::new` to module
scope so they can be shared with the async worker.
- Compute `hlsl_cache_key` once per shader during partition and reuse
at dispatch instead of recomputing.
- Scoped borrows in the async drain loop eliminate mapping/bindings
clones.
- `platform/src/os/windows/d3d11.rs` — compile pipeline, async infra,
semantic-name fix
- `platform/src/os/windows/windows.rs` — `async_hlsl_compile` field on
`CxOs`
- `draw/src/shader/draw_text.rs` — rename `LinuxSlug*` → `Slug*`,
expand cfg gates
- `draw/src/text/fonts.rs` — extend SLUG cutoff/budget defaults to
Windows
No changes to macOS, iOS, Android, or WASM paths.
TL;DR: the shader compiler needed explicit casts in for loop bounds,
and the window positioning was messed up, causing the app-level title bar
to overlap with the native OS-level title bar (which means you couldn't
see or press the window chrome buttons)
--------
here's an AI-generated summary of the changes, for more details:
On OpenGL ES 3.0 targets (Linux/EGL, Android), shaders containing `for` loops
over `uint` variables failed to compile with a GLSL type-error. The shader
compiler emitted the loop header as:
```glsl
for(uint i = 0; i < 4; i++) { … }
```
The integer literals `0` and `4` are of type `int` in GLSL. GLSL ES 3.0 forbids
implicit casts between `int` and `uint`, so the initialiser and the comparison
both produce a compile error. The other shader backends (Metal/WGSL/Rust) do not
have this restriction, so no corresponding arm existed for GLSL.
**`platform/script/src/shader_control.rs`** — Add a `ShaderBackend::Glsl` arm to
`handle_for_1` that wraps both loop bounds in an explicit constructor call for the
loop variable's type:
```glsl
for(uint i = uint(0); i < uint(4); i++) { … }
```
This satisfies GLSL ES 3.0's strict no-implicit-cast rule and matches the
behavior already implemented for the WGSL backend (which uses typed variable
declarations for the same reason).
**`platform/src/os/linux/opengl.rs`** — Gate the helper functions
`shader_source_hash` and `shader_source_preview` (and their call-sites) behind
`#[cfg(target_os = "android")]`. These functions are only referenced from
Android-specific shader-cache code paths; without the attribute the compiler
emits dead-code warnings on every other Linux/OpenGL build.
- `platform/script/src/shader_control.rs`
- `platform/src/os/linux/opengl.rs`
----------------------------------------------------------------------------
On GNOME (and likely other modern WMs), restoring a saved window position would
consistently produce two visual artifacts:
1. **No WM title bar visible** — the client area was rendered where the title bar
should appear.
2. **Black bar at the bottom** — the bottom portion of the window surface was not
covered by rendered content.
The root cause was two related issues in `xlib_window.rs`:
**Issue 1 — `XMoveWindow` called after `XMapWindow` (races with WM reparenting)**
After `XMapWindow`, the window manager asynchronously reparents the client window
into a decoration frame. If `XMoveWindow` is called after reparenting has occurred,
the coordinates are interpreted relative to the WM frame rather than the root
window. For example, calling `XMoveWindow(client, 23, 89)` after GNOME reparents
places the client 89 px from the top of the WM frame. Since the title bar is only
~37 px tall, the client ends up 52 px below the title bar, and its bottom edge
extends 52 px *beyond* the bottom of the WM frame. GNOME responds by resizing or
repositioning the client, producing the rendering artifacts described above.
**Issue 2 — No `USPosition` hint set**
Without the `USPosition` flag in `WM_NORMAL_HINTS`, GNOME ignores the position
provided to `XCreateWindow` and applies its own smart-placement algorithm. This
meant the application relied entirely on the post-map `XMoveWindow` call described
above, which was itself broken.
**`platform/src/os/linux/x11/x11_sys.rs`** — Add the standard `XSizeHints` flag
constants:
- `USPosition` (`1 << 0`) — user-specified x, y
- `USSize` (`1 << 1`) — user-specified width, height
- `PPosition` (`1 << 2`) — program-specified position
- `PSize` (`1 << 3`) — program-specified size
**`platform/src/os/linux/x11/xlib_window.rs`** — Two changes in `XlibWindow::init()`:
1. Before calling `Xutf8SetWMProperties`, populate an `XSizeHints` struct with
`flags = USPosition | PPosition` (and `x`/`y` set to the requested coordinates)
when a position was provided. Pass this struct as the `WM_NORMAL_HINTS` argument
instead of the previous `ptr::null_mut()`. This tells GNOME/Mutter to honor the
requested position rather than running its own placement heuristic.
2. Move the `XMoveWindow` call to *before* `XMapWindow`. At that point the window is
still a direct child of the root window, so the coordinates are unambiguously
root-relative. This eliminates the race with WM reparenting entirely.
- Android log levels: map log! macro levels to Android log priorities
(ERROR=6, WARN=5, INFO=4). Some devices suppress DEBUG by default.
- cargo-makepad android: show help text instead of panicking on missing
or invalid subcommand.
- Android build: discover .class files dynamically instead of hardcoding
~25 individual paths. Add Java 8 source/target flags.
- Remove deprecated AsyncTask import from MakepadNetwork.java.
- Studio stdout: add newline after JSON messages for JSON-lines parsing.
- Studio stdout: skip profiler timing in stdout mode to avoid overhead.
- Script thread: fix panic on empty call stack in call_has_me/call_has_try.
- Cursor: reset to Default on FingerHoverOut in TextFlow and TextInput.
Co-authored-by: ant <ant@offline.click>
* Add support for Video widget on WASM
* Add support for Video widget on Linux
* Linux video: GStreamer appsink pipeline, GL texture upload, accurate seeking
* Add support for Video widget on Windows
* Harden cross-platform video playback and error handling
* Cleanup video playback logs
* Restore wasm builds
* Restore linux builds
* Restore linux builds
* Remove unused import
When script_mod! is used with 'let app = startup() do ...' but the
block omits the final 'app' expression, the module returns nil. This
silently creates an App with an empty WidgetRef -- no window, no UI,
no error. The app runs indefinitely doing nothing.
Panic with an actionable message instead of silently succeeding.
Co-authored-by: ant <ant@offline.click>