#!/usr/bin/env dub
/+ dub.sdl:
name "anchored_overlays_tooltip_timing"
targetPath "build"
dependency "sparkles:base" path="../../../.."
dependency "sparkles:ui" path="../../../.."
dflags "-preview=in" "-preview=dip1000"
buildType "checked" {
buildOptions "optimize" "inline" "debugInfo"
}
+/
/**
* The tooltip warm-up / cool-down machine, **composed from `Timeline` (`STM6`)**
* and stepped over a scripted event list.
*
* Nine subjects in the survey implement the behaviour the field converged on —
* the *first* tooltip in a group pays a warm-up delay, a *subsequent* one appears
* instantly while the group is still warm, and the group goes cold again after a
* cool-down (WPF's `BetweenShowDelay`, React Aria's `globalWarmedUp` +
* `TOOLTIP_COOLDOWN`, Radix's `skipDelayDuration`, Ariakit's `skipTimeout`,
* Base UI's `FloatingDelayGroup`, Qt, GTK, WinUI, Avalonia). They also converged
* on its *shape*: **one shared arbiter holding two integers, with no per-widget
* timer state.** This program is that machine, built the way `PRN8` demands —
* the clock is the toolkit's existing $(REF Timeline, sparkles,ui,state), not a
* second hand-rolled timer:
*
* * `Timeline.Phase.fadeIn` **is** `warming` (the arm-to-deadline interval),
* * `Timeline.Phase.hold` **is** `shown`, pinned `holdUntilDismissed`,
* * the only genuinely new state is `DwellGroup` — `openId` plus one integer.
*
* Section 2 shows the three seams where `STM6`'s own semantics do *not* line up
* with a warm-up, each demonstrated by running it: `visible()` is already true
* in `fadeIn`, `dismissed()` from `fadeIn` plays a full-opacity fade-out, and a
* backend with no frame clock never advances `stepped` at all. Composing
* `Timeline` therefore means using it as the **counter**, and deriving
* visibility from `phase == hold` rather than from `visible()`.
*
* The trace tables are the demonstration. The same scripted event list is run
* through three targets, so the collapses are visible as *divergence in one
* column* rather than as prose:
*
* 1. a pointer target (the full machine),
* 2. a touch target — `caps.hover == false`, where the hover trigger is
* substituted rather than degraded,
* 3. tier-0 static HTML — no script, **no timers at all**, where the warm-up
* survives as `transition-delay` and the group behaviour is honestly absent.
*
* Companion to [../proposal.md](../proposal.md) § 4.1 "Tooltip" and § 4.5
* "The one genuinely new machine, and its `PRN8` justification"; the field
* evidence it implements is in [../comparison.md](../comparison.md), dimension 6.
*
* Run with: dub run --single tooltip-timing.d
*
* Portability: pure computation over an injected `dtMs` — no clock, no display,
* no terminal. Deterministic everywhere.
*/
module (module) anchored_overlays_tooltip_timingThe tooltip warm-up / cool-down machine, composed from Timeline (STM6)
and stepped over a scripted event list.
Nine subjects in the survey implement the behaviour the field converged on —
the first tooltip in a group pays a warm-up delay, a subsequent one appears
instantly while the group is still warm, and the group goes cold again after a
cool-down (WPF's BetweenShowDelay, React Aria's globalWarmedUp +
TOOLTIP_COOLDOWN, Radix's skipDelayDuration, Ariakit's skipTimeout,
Base UI's FloatingDelayGroup, Qt, GTK, WinUI, Avalonia). They also converged
on its shape: one shared arbiter holding two integers, with no per-widget
timer state. This program is that machine, built the way PRN8 demands —
the clock is the toolkit's existing Timeline, not a
second hand-rolled timer:
Timeline.Phase.fadeIn is warming (the arm-to-deadline interval),
Timeline.Phase.hold is shown, pinned holdUntilDismissed,
the only genuinely new state is DwellGroup — openId plus one integer.
Section 2 shows the three seams where STM6's own semantics do not line up
with a warm-up, each demonstrated by running it: visible() is already true
in fadeIn, dismissed() from fadeIn plays a full-opacity fade-out, and a
backend with no frame clock never advances stepped at all. Composing
Timeline therefore means using it as the counter, and deriving
visibility from phase == hold rather than from visible().
The trace tables are the demonstration. The same scripted event list is run
through three targets, so the collapses are visible as divergence in one
column rather than as prose:
a pointer target (the full machine),
a touch target — caps.hover == false, where the hover trigger is
substituted rather than degraded,
tier-0 static HTML — no script, no timers at all, where the warm-up
survives as transition-delay and the group behaviour is honestly absent.
Companion to ../proposal.md § 4.1 "Tooltip" and § 4.5
"The one genuinely new machine, and its PRN8 justification"; the field
evidence it implements is in ../comparison.md, dimension 6.
Run with: dub run --single tooltip-timing.d
Portability
pure computation over an injected dtMs — no clock, no display,
no terminal. Deterministic everywhere.
anchored_overlays_tooltip_timing;
import (package) stdstd.(package) std.rangerange.(module) std.range.primitivesThis module is a submodule of std.range.
It defines the bidirectional and forward range primitives for arrays:
empty, front, back, popFront, popBack and save.
It provides basic range functionality by defining several templates for testing
whether a given object is a range, and what kind of range it is:
| isInputRange |
Tests if something is an input range, defined to be
something from which one can sequentially read data using the
primitives front, popFront, and empty.
|
| isOutputRange |
Tests if something is an output range, defined to be
something to which one can sequentially write data using the
put primitive.
|
| isForwardRange |
Tests if something is a forward range, defined to be an
input range with the additional capability that one can save one's
current position with the save primitive, thus allowing one to
iterate over the same range multiple times.
|
| isBidirectionalRange |
Tests if something is a bidirectional range, that is, a
forward range that allows reverse traversal using the primitives back and popBack.
|
| isRandomAccessRange |
Tests if something is a random access range, which is a
bidirectional range that also supports the array subscripting
operation via the primitive opIndex.
|
It also provides number of templates that test for various range capabilities:
| hasMobileElements |
Tests if a given range's elements can be moved around using the
primitives moveFront, moveBack, or moveAt.
|
| ElementType |
Returns the element type of a given range.
|
| ElementEncodingType |
Returns the encoding element type of a given range.
|
| hasSwappableElements |
Tests if a range is a forward range with swappable elements.
|
| hasAssignableElements |
Tests if a range is a forward range with mutable elements.
|
| hasLvalueElements |
Tests if a range is a forward range with elements that can be
passed by reference and have their address taken.
|
| hasLength |
Tests if a given range has the length attribute.
|
| isInfinite |
Tests if a given range is an infinite range.
|
| hasSlicing |
Tests if a given range supports the array slicing operation R[x .. y].
|
Finally, it includes some convenience functions for manipulating ranges:
| popFrontN |
Advances a given range by up to n elements.
|
| popBackN |
Advances a given bidirectional range from the right by up to
n elements.
|
| popFrontExactly |
Advances a given range by up exactly n elements.
|
| popBackExactly |
Advances a given bidirectional range from the right by exactly
n elements.
|
| moveFront |
Removes the front element of a range.
|
| moveBack |
Removes the back element of a bidirectional range.
|
| moveAt |
Removes the i'th element of a random-access range.
|
| walkLength |
Computes the length of any range in O(n) time.
|
| put |
Outputs element e to a range.
|
Source
std/range/primitives.d
primitives : (alias template) anchored_overlays_tooltip_timing.put = std.range.primitives.put(R, E)(ref R r, E e)Outputs e to r. The exact effect is dependent upon the two
types. Several cases are accepted, as described below. The code snippets
are attempted in order, and the first to compile "wins" and gets
evaluated.
In this table "doPut" is a method that places e into r, using the
correct primitive: r.put(e) if R defines put, r.front = e
if r is an input range (followed by r.popFront()), or r(e)
otherwise.
Code Snippet
Scenario
| r.doPut(e); |
R specifically accepts an E. |
| r.doPut([ e ]); |
R specifically accepts an E[]. |
| r.putChar(e); |
R accepts some form of string or character. put will
transcode the character e accordingly. |
| for (; !e.empty; e.popFront()) put(r, e.front); |
Copying range E into R. |
Tip
put should not be used "UFCS-style", e.g. r.put(e).
Doing this may call R.put directly, by-passing any transformation
feature provided by `Range.`put. ``put(r, e) is prefered.
put;
import (package) stdstd.(module) std.stdioCategory Symbols File handles _popen File isFileHandle openNetwork stderr stdin stdout Reading chunks lines readf readfln readln Writing toFile write writef writefln writeln Misc KeepTerminator LockType StdioException
Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio
is publically imported when importing std.stdio.
There are three layers of I/O:
The lowest layer is the operating system layer. The two main schemes are Windows and Posix.
C's stdio.h which unifies the two operating system schemes.
std.stdio, this module, unifies the various stdio.h implementations into
a high level package for D programs.
Source
std/stdio.d
stdio : (alias template) anchored_overlays_tooltip_timing.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln, (alias template) anchored_overlays_tooltip_timing.writeln = std.stdio.writeln(T...)(T args)Equivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
import (package) sparklessparkles.(package) sparkles.basebase.(module) sparkles.base.smallbufferA @nogc container with Small Buffer Optimization (SBO).
Provides an append-only buffer that stores small amounts of data inline
(avoiding heap allocation) and automatically switches to heap storage
when capacity is exceeded.
Primary use case: appending elements in a temporary scope where GC
allocation is not desired.
smallbuffer : (alias struct) anchored_overlays_tooltip_timing.SmallBuffer = sparkles.base.smallbuffer.SmallBuffer(T, ulong N = max(size_t(1), (T[]).sizeof / T.sizeof), bool unique = false)A @nogc container with Small Buffer Optimization and copy-on-write.
Elements are stored inline up to N elements, then automatically
allocated on the heap (via AffixAllocator!(Mallocator, ControlBlock), which
keeps the reference count in an allocation prefix; the element capacity is the
heap slice length) when capacity is exceeded. Heap blocks are managed with the
std.experimental.allocator makeArray/expandArray/dispose helpers.
The buffer is copyable. Copying an inline buffer duplicates its elements
(independent copies). Copying a heap buffer shares the allocation and bumps a
reference count; the shared block is cloned copy-on-write the first time a
mutable copy is written. This suits the common pattern of one producer
building a buffer mutably, then handing out many const reader copies — read
via const (e.g. through borrow) never clones. Mutating accessors on a
shared mutable copy clone first, so a mutable slice/reference taken from a
shared buffer and held across a later mutation may be invalidated (the usual
copy-on-write caveat) — read through const to share without that risk.
Note
storage location is tied to length (data is inline whenever
length <= N), so reserve pre-grows only once on the heap,
and clear/popBack that drop the length back to <= N revert
to inline storage.
SmallBuffer;
import (package) sparklessparkles.(package) sparkles.basebase.(package) sparkles.base.texttext.(module) sparkles.base.text.writers@nogc-compatible text writing utilities for output ranges.
Provides functions for writing integers, floats, escaped characters/strings,
and ANSI escape sequences to output ranges without GC allocation.
writers : (alias template) anchored_overlays_tooltip_timing.writeValue = sparkles.base.text.writers.writeValue(Writer, T)(ref Writer w, auto ref const T val)Writes any value to an output range using best-effort @nogc conversion.
Dispatch order:
bool — writes "true" or "false"
Integral types — uses writeInteger
Floating-point types — uses writeFloat
char — writes the character directly
String/char slices — writes directly via put
User types with @nogc output range toString — calls t.toString(writer)
User types with @nogc sink toString — calls with a forwarding delegate
User types with @nogc toString() returning string — writes the result
User types with @nogc string cast — writes cast(string) t
Fallback — uses std.conv.to!string (GC-allocating)
writeValue;
import (package) sparklessparkles.(module) sparkles.inputsparkles:input`` — the abstract, capability-tiered input vocabulary shared by
every sparkles:ui target (docs/specs/ui/input.md).
Interaction is the other half of "one definition, three targets": events are
values in one vocabulary (sparkles.input.events), classified into
capability tiers (sparkles.input.tier), in a package both the toolkit
and the terminal library can depend on (sparkles:base only, plus
sparkles:math import-only for the position type). Producers adapt their
native input to it — the terminal decodes its wire formats straight into
Event; a pixel backend synthesizes events from
polled state.
input : (struct) sparkles.input.capability.InputCapabilitiesA target's declared input affordances.
The defaults describe a mouse: the historical assumption, so a producer
that has not thought about this yet keeps today's behavior rather than
silently claiming a capability it lacks in the other direction.
Declare with one of the profiles below where one fits — mousePointer,
touchPointer, cellPointer, staticPointer — so the
vocabulary stays small and two targets that mean the same thing say it the
same way.
InputCapabilities;
import (package) sparklessparkles.(package) sparkles.uiui.(module) sparkles.ui.stateThe state level (STM) of sparkles.ui: presentation-free interaction
state machines fed the shared sparkles:input events — pure logic over
abstract input, producing state and derived geometry in abstract units, with
no draw calls and no device units.
Machines are advanced by transformations and are intended to be Regular
values. Scalar and immutable-payload machines meet that contract;
DisclosureState's exposed slice still needs the ownership/copy policy tracked
by UI-O1. They advance as state`.stepped(…) → `state (the caller assigns),
never by mutating shared locals — so behavior can be snapshotted, replayed and
diffed in tests. And
every machine exists once: where behavior was written per backend it
diverged (two scrollbar thumb formulas scrolling the same document
differently; one copy affordance flashing on a timer while another held until
the next event), which is precisely the "correctness does not compose" defect
this level removes.
HoverState + hoverTargets — which element is hot (STM4)
scrollbarThumb + ScrollState — one thumb formula (STM2)
Selection — normalized anchor/focus, no -1 sentinel (STM3)
DisclosureState — one opened/closed set for tree expansion
and content folding (STM5)
Timeline — transient effects as modes, not bare counters (STM6)
FocusState — keyboard focus + deterministic traversal (STM7)
SplitState — a draggable pane divider (STM8)
PressState — press arms, release-over-the-same-target
activates (STM10)
CaptureState — press owns the drag; one affordance holds the
pointer until release (STM11)
state : (struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline;
// ---------------------------------------------------------------------------
// Configuration — the numbers live beside the other popup metrics, never inline
// ---------------------------------------------------------------------------
/**
The two durations the whole dimension reduces to. Both belong in `Palette`
beside `popupPadX`, exactly as WinUI puts them in the OS and Qt in `QStyle`;
they are parameters here so the trace can name them.
`skipMs == 0` must **statically disable** warmth rather than arm a zero-length
timer — Radix shipped that bug (`#3873`: a zero-length timer left every tooltip
instant forever) and fixed it with early returns in both provider callbacks.
$(LREF DwellConfig.warmthEnabled) is that early return, expressed as a value.
*/
struct (struct) anchored_overlays_tooltip_timing.DwellConfigThe two durations the whole dimension reduces to. Both belong in Palette
beside popupPadX, exactly as WinUI puts them in the OS and Qt in QStyle;
they are parameters here so the trace can name them.
skipMs == 0 must statically disable warmth rather than arm a zero-length
timer — Radix shipped that bug (#3873: a zero-length timer left every tooltip
instant forever) and fixed it with early returns in both provider callbacks.
``DwellConfig.warmthEnabled is that early return, expressed as a value.
DwellConfig
{
int (field) int anchored_overlays_tooltip_timing.DwellConfig.warmUpMsthe first tooltip in a cold group waits this long
warmUpMs = 500; /// the first tooltip in a cold group waits this long
int (field) int anchored_overlays_tooltip_timing.DwellConfig.skipMsthe group stays warm this long after one closes
skipMs = 300; /// the group stays warm this long after one closes
/// Warmth is a *feature*, switched on by a positive skip window.
bool bool anchored_overlays_tooltip_timing.DwellConfig.warmthEnabled() const pure nothrow @nogc @safeWarmth is a feature, switched on by a positive skip window.
warmthEnabled() const @safe pure nothrow @nogc => (field) int anchored_overlays_tooltip_timing.DwellConfig.skipMsthe group stays warm this long after one closes
skipMs > 0;
}
// ---------------------------------------------------------------------------
// The machine: Timeline (STM6) as the clock + a two-integer group arbiter
// ---------------------------------------------------------------------------
/// The `Timeline` config a dwell arms with. `fadeInMs` is the *resolved* delay
/// (0 when the group was warm), and `holdUntilDismissed` is pinned so a
/// hover-triggered surface can never self-close — the WCAG 1.4.13 defaults trap
/// that `Timeline.Config.holdMs = 1200` sets for an unwary caller.
(struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline.(struct) sparkles.ui.state.Timeline.ConfigPhase durations. holdUntilDismissed is the event-scoped mode: hold
persists until dismissed — a mode, not a magic duration.
Config sparkles.ui.state.Timeline.Config anchored_overlays_tooltip_timing.lifeCfg(int resolvedDelayMs) pure nothrow @nogc @safeThe Timeline config a dwell arms with. fadeInMs is the resolved delay
(0 when the group was warm), and holdUntilDismissed is pinned so a
hover-triggered surface can never self-close — the WCAG 1.4.13 defaults trap
that Timeline.Config.holdMs = 1200 sets for an unwary caller.
lifeCfg(int (parameter) int resolvedDelayMsresolvedDelayMs) @safe pure nothrow @nogc
=> (struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline.(struct) sparkles.ui.state.Timeline.ConfigPhase durations. holdUntilDismissed is the event-scoped mode: hold
persists until dismissed — a mode, not a magic duration.
Config(fadeInMs: (parameter) int resolvedDelayMsresolvedDelayMs, holdUntilDismissed: true);
/// One tooltip's timing state: a composed `Timeline` plus the anchor it belongs
/// to. `armedDelayMs` is stored because the deadline must be **absolute** —
/// measured from the arm instant, never recomputed as `now + delay` — which
/// `Timeline.stepped` gives for free as long as the config does not change
/// under it.
struct (struct) anchored_overlays_tooltip_timing.TooltipDwellOne tooltip's timing state: a composed Timeline plus the anchor it belongs
to. armedDelayMs is stored because the deadline must be absolute —
measured from the arm instant, never recomputed as now + delay — which
Timeline.stepped gives for free as long as the config does not change
under it.
TooltipDwell
{
(struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline (field) sparkles.ui.state.Timeline anchored_overlays_tooltip_timing.TooltipDwell.lifefadeIn == warming, hold == shown
life; /// `fadeIn` == warming, `hold` == shown
(alias) object.size_t = ulongsize_t (field) ulong anchored_overlays_tooltip_timing.TooltipDwell.anchorId0 == no anchor
anchorId; /// 0 == no anchor
int (field) int anchored_overlays_tooltip_timing.TooltipDwell.armedDelayMsthe delay resolved at arm time
armedDelayMs; /// the delay resolved at arm time
@safe pure nothrow @nogc:
(struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline.(struct) sparkles.ui.state.Timeline.ConfigPhase durations. holdUntilDismissed is the event-scoped mode: hold
persists until dismissed — a mode, not a magic duration.
Config sparkles.ui.state.Timeline.Config anchored_overlays_tooltip_timing.TooltipDwell.cfg() const pure nothrow @nogc @safecfg() const => sparkles.ui.state.Timeline.Config anchored_overlays_tooltip_timing.lifeCfg(int resolvedDelayMs) pure nothrow @nogc @safeThe Timeline config a dwell arms with. fadeInMs is the resolved delay
(0 when the group was warm), and holdUntilDismissed is pinned so a
hover-triggered surface can never self-close — the WCAG 1.4.13 defaults trap
that Timeline.Config.holdMs = 1200 sets for an unwary caller.
lifeCfg((field) int anchored_overlays_tooltip_timing.TooltipDwell.armedDelayMsthe delay resolved at arm time
armedDelayMs);
/// Counting down to the deadline, and **not** in the display or hit list.
bool bool anchored_overlays_tooltip_timing.TooltipDwell.warming() const pure nothrow @nogc @safeCounting down to the deadline, and not in the display or hit list.
warming() const => (field) sparkles.ui.state.Timeline anchored_overlays_tooltip_timing.TooltipDwell.lifefadeIn == warming, hold == shown
life.(field) sparkles.ui.state.Timeline.Phase sparkles.ui.state.Timeline.phasephase == (struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline.(enum) sparkles.ui.state.Timeline.PhaseThe phase of the effect.
Phase.(enum value) sparkles.ui.state.Timeline.Phase.fadeIn = 1appearing
fadeIn;
/// Painted. Note this is `phase == hold`, *not* `life.visible()` — see § 2.
bool bool anchored_overlays_tooltip_timing.TooltipDwell.shown() const pure nothrow @nogc @safePainted. Note this is phase == hold, not life.visible() — see § 2.
shown() const => (field) sparkles.ui.state.Timeline anchored_overlays_tooltip_timing.TooltipDwell.lifefadeIn == warming, hold == shown
life.(field) sparkles.ui.state.Timeline.Phase sparkles.ui.state.Timeline.phasephase == (struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline.(enum) sparkles.ui.state.Timeline.PhaseThe phase of the effect.
Phase.(enum value) sparkles.ui.state.Timeline.Phase.hold = 2fully visible
hold;
}
/// The whole cross-instance protocol: which anchor owns the open surface, and
/// how much warmth is left once it closes. Nine independent lineages converged
/// on exactly this much state. With an injected wall clock the second field is
/// the absolute `warmUntilMs` the proposal specifies; this driver supplies
/// `dtMs` (the shape `Timeline.stepped` already consumes), so it counts down.
/// One integer either way.
struct (struct) anchored_overlays_tooltip_timing.DwellGroupThe whole cross-instance protocol: which anchor owns the open surface, and
how much warmth is left once it closes. Nine independent lineages converged
on exactly this much state. With an injected wall clock the second field is
the absolute warmUntilMs the proposal specifies; this driver supplies
dtMs (the shape Timeline.stepped already consumes), so it counts down.
One integer either way.
DwellGroup
{
(alias) object.size_t = ulongsize_t (field) ulong anchored_overlays_tooltip_timing.DwellGroup.openIdthe anchor currently showing, 0 == none
openId; /// the anchor currently showing, 0 == none
int (field) int anchored_overlays_tooltip_timing.DwellGroup.warmMsLeft0 ⇒ the next tooltip in the group opens instantly
warmMsLeft; /// > 0 ⇒ the next tooltip in the group opens instantly
}
/// A group and its at-most-one live tooltip. One record suffices because
/// opening closes every peer (React Aria's `closeOthers`), so a registry of
/// per-instance timers — which is not expressible in `@safe pure` value code
/// anyway — has nothing left to hold.
struct (struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell
{
(struct) anchored_overlays_tooltip_timing.TooltipDwellOne tooltip's timing state: a composed Timeline plus the anchor it belongs
to. armedDelayMs is stored because the deadline must be absolute —
measured from the arm instant, never recomputed as now + delay — which
Timeline.stepped gives for free as long as the config does not change
under it.
TooltipDwell (field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive;
(struct) anchored_overlays_tooltip_timing.DwellGroupThe whole cross-instance protocol: which anchor owns the open surface, and
how much warmth is left once it closes. Nine independent lineages converged
on exactly this much state. With an injected wall clock the second field is
the absolute warmUntilMs the proposal specifies; this driver supplies
dtMs (the shape Timeline.stepped already consumes), so it counts down.
One integer either way.
DwellGroup (field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup;
@safe pure nothrow @nogc:
bool bool anchored_overlays_tooltip_timing.Dwell.visible() const pure nothrow @nogc @safevisible() const => (field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.bool anchored_overlays_tooltip_timing.TooltipDwell.shown() const pure nothrow @nogc @safePainted. Note this is phase == hold, not life.visible() — see § 2.
shown;
(alias) object.size_t = ulongsize_t ulong anchored_overlays_tooltip_timing.Dwell.visibleId() const pure nothrow @nogc @safevisibleId() const => (field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.bool anchored_overlays_tooltip_timing.TooltipDwell.shown() const pure nothrow @nogc @safePainted. Note this is phase == hold, not life.visible() — see § 2.
shown ? (field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.(field) ulong anchored_overlays_tooltip_timing.TooltipDwell.anchorId0 == no anchor
anchorId : 0;
/// `true` while a *subsequent* tooltip would open with no delay.
bool bool anchored_overlays_tooltip_timing.Dwell.warm() const pure nothrow @nogc @safetrue while a subsequent tooltip would open with no delay.
warm() const => (field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) ulong anchored_overlays_tooltip_timing.DwellGroup.openIdthe anchor currently showing, 0 == none
openId != 0 || (field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) int anchored_overlays_tooltip_timing.DwellGroup.warmMsLeft0 ⇒ the next tooltip in the group opens instantly
warmMsLeft > 0;
}
/// Post-transition invariant: a shown tooltip **is** the group's open surface,
/// and an open surface makes the countdown redundant (React Aria clears the
/// pending cool-down on show).
(struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.settled(in anchored_overlays_tooltip_timing.Dwell s) pure nothrow @nogc @safePost-transition invariant: a shown tooltip is the group's open surface,
and an open surface makes the countdown redundant (React Aria clears the
pending cool-down on show).
settled(in (struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss) @safe pure nothrow @nogc
{
(struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell (local variable) anchored_overlays_tooltip_timing.Dwell nn = (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss;
if ((local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.bool anchored_overlays_tooltip_timing.TooltipDwell.shown() const pure nothrow @nogc @safePainted. Note this is phase == hold, not life.visible() — see § 2.
shown)
{
(local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) ulong anchored_overlays_tooltip_timing.DwellGroup.openIdthe anchor currently showing, 0 == none
openId = (local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.(field) ulong anchored_overlays_tooltip_timing.TooltipDwell.anchorId0 == no anchor
anchorId;
(local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) int anchored_overlays_tooltip_timing.DwellGroup.warmMsLeft0 ⇒ the next tooltip in the group opens instantly
warmMsLeft = 0;
}
return (local variable) anchored_overlays_tooltip_timing.Dwell nn;
}
/// Whatever was live yields the group.
///
/// The cool-down is armed **only if something was actually shown** — abandoning
/// a hover mid-warm-up leaves the group cold and the next hover pays full price
/// (React Aria arms its cool-down inside `if (globalWarmedUp)`). And a cancelled
/// warm-up resets to `Timeline.init`, never through `Timeline.dismissed` — § 2.b.
(struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.closedActive(in anchored_overlays_tooltip_timing.Dwell s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeWhatever was live yields the group.
The cool-down is armed only if something was actually shown — abandoning
a hover mid-warm-up leaves the group cold and the next hover pays full price
(React Aria arms its cool-down inside if (globalWarmedUp)). And a cancelled
warm-up resets to Timeline.init, never through Timeline.dismissed — § 2.b.
closedActive(in (struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss, in (struct) anchored_overlays_tooltip_timing.DwellConfigThe two durations the whole dimension reduces to. Both belong in Palette
beside popupPadX, exactly as WinUI puts them in the OS and Qt in QStyle;
they are parameters here so the trace can name them.
skipMs == 0 must statically disable warmth rather than arm a zero-length
timer — Radix shipped that bug (#3873: a zero-length timer left every tooltip
instant forever) and fixed it with early returns in both provider callbacks.
``DwellConfig.warmthEnabled is that early return, expressed as a value.
DwellConfig (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc) @safe pure nothrow @nogc
{
(struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell (local variable) anchored_overlays_tooltip_timing.Dwell nn = (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss;
const (local variable) const(bool) wasShownwasShown = (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.bool anchored_overlays_tooltip_timing.TooltipDwell.shown() const pure nothrow @nogc @safePainted. Note this is phase == hold, not life.visible() — see § 2.
shown;
(local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive = (struct) anchored_overlays_tooltip_timing.TooltipDwellOne tooltip's timing state: a composed Timeline plus the anchor it belongs
to. armedDelayMs is stored because the deadline must be absolute —
measured from the arm instant, never recomputed as now + delay — which
Timeline.stepped gives for free as long as the config does not change
under it.
TooltipDwell.(constant) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.TooltipDwell.init = TooltipDwell(Timeline(Phase.idle, 0), 0LU, 0)init;
(local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) ulong anchored_overlays_tooltip_timing.DwellGroup.openIdthe anchor currently showing, 0 == none
openId = 0;
if ((local variable) const(bool) wasShownwasShown && (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc.bool anchored_overlays_tooltip_timing.DwellConfig.warmthEnabled() const pure nothrow @nogc @safeWarmth is a feature, switched on by a positive skip window.
warmthEnabled)
(local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) int anchored_overlays_tooltip_timing.DwellGroup.warmMsLeft0 ⇒ the next tooltip in the group opens instantly
warmMsLeft = (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc.(field) int anchored_overlays_tooltip_timing.DwellConfig.skipMsthe group stays warm this long after one closes
skipMs;
return (local variable) anchored_overlays_tooltip_timing.Dwell nn;
}
/// The pointer came to rest on an anchor.
(struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.entered(in anchored_overlays_tooltip_timing.Dwell s, ulong id, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe pointer came to rest on an anchor.
entered(in (struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss, (alias) object.size_t = ulongsize_t (parameter) ulong idid, in (struct) anchored_overlays_tooltip_timing.DwellConfigThe two durations the whole dimension reduces to. Both belong in Palette
beside popupPadX, exactly as WinUI puts them in the OS and Qt in QStyle;
they are parameters here so the trace can name them.
skipMs == 0 must statically disable warmth rather than arm a zero-length
timer — Radix shipped that bug (#3873: a zero-length timer left every tooltip
instant forever) and fixed it with early returns in both provider callbacks.
``DwellConfig.warmthEnabled is that early return, expressed as a value.
DwellConfig (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc) @safe pure nothrow @nogc
in ((parameter) ulong idid != 0, "anchor id 0 means 'no anchor'")
{
// Re-entry on the live anchor is a no-op, not a restart: a `retarget`, never
// a close+open, or the surface flashes.
if ((parameter) const(anchored_overlays_tooltip_timing.Dwell) ss.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.(field) ulong anchored_overlays_tooltip_timing.TooltipDwell.anchorId0 == no anchor
anchorId == (parameter) ulong idid && (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.(field) sparkles.ui.state.Timeline anchored_overlays_tooltip_timing.TooltipDwell.lifefadeIn == warming, hold == shown
life.(field) sparkles.ui.state.Timeline.Phase sparkles.ui.state.Timeline.phasephase != (struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline.(enum) sparkles.ui.state.Timeline.PhaseThe phase of the effect.
Phase.(enum value) sparkles.ui.state.Timeline.Phase.idle = cast(ubyte)0unot showing
idle)
return (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss;
// Read the group *before* the outgoing surface yields — the swap is instant
// because a peer is open, which is the same warmth the cool-down preserves.
const (local variable) const(bool) instantinstant = (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc.bool anchored_overlays_tooltip_timing.DwellConfig.warmthEnabled() const pure nothrow @nogc @safeWarmth is a feature, switched on by a positive skip window.
warmthEnabled && (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss.bool anchored_overlays_tooltip_timing.Dwell.warm() const pure nothrow @nogc @safetrue while a subsequent tooltip would open with no delay.
warm;
auto (local variable) anchored_overlays_tooltip_timing.Dwell nn = anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.closedActive(in anchored_overlays_tooltip_timing.Dwell s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeWhatever was live yields the group.
The cool-down is armed only if something was actually shown — abandoning
a hover mid-warm-up leaves the group cold and the next hover pays full price
(React Aria arms its cool-down inside if (globalWarmedUp)). And a cancelled
warm-up resets to Timeline.init, never through Timeline.dismissed — § 2.b.
closedActive((parameter) const(anchored_overlays_tooltip_timing.Dwell) ss, (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc);
const (local variable) const(int) delaydelay = (local variable) const(bool) instantinstant ? 0 : (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc.(field) int anchored_overlays_tooltip_timing.DwellConfig.warmUpMsthe first tooltip in a cold group waits this long
warmUpMs;
(local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive = (struct) anchored_overlays_tooltip_timing.TooltipDwellOne tooltip's timing state: a composed Timeline plus the anchor it belongs
to. armedDelayMs is stored because the deadline must be absolute —
measured from the arm instant, never recomputed as now + delay — which
Timeline.stepped gives for free as long as the config does not change
under it.
TooltipDwell((struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline.sparkles.ui.state.Timeline sparkles.ui.state.Timeline.triggered(in sparkles.ui.state.Timeline.Config cfg) pure nothrow @nogc @safeA freshly-triggered effect (restarts if already running).
triggered(sparkles.ui.state.Timeline.Config anchored_overlays_tooltip_timing.lifeCfg(int resolvedDelayMs) pure nothrow @nogc @safeThe Timeline config a dwell arms with. fadeInMs is the resolved delay
(0 when the group was warm), and holdUntilDismissed is pinned so a
hover-triggered surface can never self-close — the WCAG 1.4.13 defaults trap
that Timeline.Config.holdMs = 1200 sets for an unwary caller.
lifeCfg((local variable) const(int) delaydelay)), (parameter) ulong idid, (local variable) const(int) delaydelay);
return anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.settled(in anchored_overlays_tooltip_timing.Dwell s) pure nothrow @nogc @safePost-transition invariant: a shown tooltip is the group's open surface,
and an open surface makes the countdown redundant (React Aria clears the
pending cool-down on show).
settled((local variable) anchored_overlays_tooltip_timing.Dwell nn);
}
/// The pointer left the anchor (and the group's region).
(struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.left(in anchored_overlays_tooltip_timing.Dwell s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe pointer left the anchor (and the group's region).
left(in (struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss, in (struct) anchored_overlays_tooltip_timing.DwellConfigThe two durations the whole dimension reduces to. Both belong in Palette
beside popupPadX, exactly as WinUI puts them in the OS and Qt in QStyle;
they are parameters here so the trace can name them.
skipMs == 0 must statically disable warmth rather than arm a zero-length
timer — Radix shipped that bug (#3873: a zero-length timer left every tooltip
instant forever) and fixed it with early returns in both provider callbacks.
``DwellConfig.warmthEnabled is that early return, expressed as a value.
DwellConfig (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc) @safe pure nothrow @nogc
=> anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.closedActive(in anchored_overlays_tooltip_timing.Dwell s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeWhatever was live yields the group.
The cool-down is armed only if something was actually shown — abandoning
a hover mid-warm-up leaves the group cold and the next hover pays full price
(React Aria arms its cool-down inside if (globalWarmedUp)). And a cancelled
warm-up resets to Timeline.init, never through Timeline.dismissed — § 2.b.
closedActive((parameter) const(anchored_overlays_tooltip_timing.Dwell) ss, (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc);
/// A press, a key, or a scroll: closes the surface **and clears warmth**.
///
/// Nothing in React Aria or Radix clears warmth except its own cool-down, so a
/// click or a scroll leaves the next tooltip in instant mode; Ariakit patched
/// exactly this symptom on `onBlur`. Warmth is a property of an uninterrupted
/// hover context, so the interruption ends it.
(struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.interrupted(in anchored_overlays_tooltip_timing.Dwell s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeA press, a key, or a scroll: closes the surface and clears warmth.
Nothing in React Aria or Radix clears warmth except its own cool-down, so a
click or a scroll leaves the next tooltip in instant mode; Ariakit patched
exactly this symptom on onBlur. Warmth is a property of an uninterrupted
hover context, so the interruption ends it.
interrupted(in (struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss, in (struct) anchored_overlays_tooltip_timing.DwellConfigThe two durations the whole dimension reduces to. Both belong in Palette
beside popupPadX, exactly as WinUI puts them in the OS and Qt in QStyle;
they are parameters here so the trace can name them.
skipMs == 0 must statically disable warmth rather than arm a zero-length
timer — Radix shipped that bug (#3873: a zero-length timer left every tooltip
instant forever) and fixed it with early returns in both provider callbacks.
``DwellConfig.warmthEnabled is that early return, expressed as a value.
DwellConfig (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc) @safe pure nothrow @nogc
{
auto (local variable) anchored_overlays_tooltip_timing.Dwell nn = anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.closedActive(in anchored_overlays_tooltip_timing.Dwell s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeWhatever was live yields the group.
The cool-down is armed only if something was actually shown — abandoning
a hover mid-warm-up leaves the group cold and the next hover pays full price
(React Aria arms its cool-down inside if (globalWarmedUp)). And a cancelled
warm-up resets to Timeline.init, never through Timeline.dismissed — § 2.b.
closedActive((parameter) const(anchored_overlays_tooltip_timing.Dwell) ss, (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc);
(local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) int anchored_overlays_tooltip_timing.DwellGroup.warmMsLeft0 ⇒ the next tooltip in the group opens instantly
warmMsLeft = 0;
return (local variable) anchored_overlays_tooltip_timing.Dwell nn;
}
/// Advance by `dtMs`. The warm-up elapses inside `Timeline`; the cool-down is
/// the one subtraction this machine adds.
(struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.stepped(in anchored_overlays_tooltip_timing.Dwell s, int dtMs, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeAdvance by dtMs. The warm-up elapses inside Timeline; the cool-down is
the one subtraction this machine adds.
stepped(in (struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss, int (parameter) int dtMsdtMs, in (struct) anchored_overlays_tooltip_timing.DwellConfigThe two durations the whole dimension reduces to. Both belong in Palette
beside popupPadX, exactly as WinUI puts them in the OS and Qt in QStyle;
they are parameters here so the trace can name them.
skipMs == 0 must statically disable warmth rather than arm a zero-length
timer — Radix shipped that bug (#3873: a zero-length timer left every tooltip
instant forever) and fixed it with early returns in both provider callbacks.
``DwellConfig.warmthEnabled is that early return, expressed as a value.
DwellConfig (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc) @safe pure nothrow @nogc
{
(struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell (local variable) anchored_overlays_tooltip_timing.Dwell nn = (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss;
(local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.(field) sparkles.ui.state.Timeline anchored_overlays_tooltip_timing.TooltipDwell.lifefadeIn == warming, hold == shown
life = (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.(field) sparkles.ui.state.Timeline anchored_overlays_tooltip_timing.TooltipDwell.lifefadeIn == warming, hold == shown
life.sparkles.ui.state.Timeline sparkles.ui.state.Timeline.stepped(int dtMs, in sparkles.ui.state.Timeline.Config cfg) const pure nothrow @nogc @safeAdvanced by dtMs milliseconds.
stepped((parameter) int dtMsdtMs, (parameter) const(anchored_overlays_tooltip_timing.Dwell) ss.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.sparkles.ui.state.Timeline.Config anchored_overlays_tooltip_timing.TooltipDwell.cfg() const pure nothrow @nogc @safecfg);
if (!(local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.bool anchored_overlays_tooltip_timing.TooltipDwell.shown() const pure nothrow @nogc @safePainted. Note this is phase == hold, not life.visible() — see § 2.
shown && (local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) ulong anchored_overlays_tooltip_timing.DwellGroup.openIdthe anchor currently showing, 0 == none
openId == 0)
(local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) int anchored_overlays_tooltip_timing.DwellGroup.warmMsLeft0 ⇒ the next tooltip in the group opens instantly
warmMsLeft = (local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) int anchored_overlays_tooltip_timing.DwellGroup.warmMsLeft0 ⇒ the next tooltip in the group opens instantly
warmMsLeft > (parameter) int dtMsdtMs ? (local variable) anchored_overlays_tooltip_timing.Dwell nn.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) int anchored_overlays_tooltip_timing.DwellGroup.warmMsLeft0 ⇒ the next tooltip in the group opens instantly
warmMsLeft - (parameter) int dtMsdtMs : 0;
return anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.settled(in anchored_overlays_tooltip_timing.Dwell s) pure nothrow @nogc @safePost-transition invariant: a shown tooltip is the group's open surface,
and an open surface makes the countdown redundant (React Aria clears the
pending cool-down on show).
settled((local variable) anchored_overlays_tooltip_timing.Dwell nn);
}
// ---------------------------------------------------------------------------
// The scripted event list
// ---------------------------------------------------------------------------
/// What a target can deliver. `enter`/`leave` exist only where `caps.hover`.
enum (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev : ubyte
{
(enum value) anchored_overlays_tooltip_timing.Ev.tick = cast(ubyte)0utime passes, nothing happens
tick, /// time passes, nothing happens
(enum value) anchored_overlays_tooltip_timing.Ev.enter = 1the pointer rests on an anchor
enter, /// the pointer rests on an anchor
(enum value) anchored_overlays_tooltip_timing.Ev.leave = 2the pointer leaves the group
leave, /// the pointer leaves the group
(enum value) anchored_overlays_tooltip_timing.Ev.tap = 3press+release on an anchor (or on 0 == outside)
tap, /// press+release on an anchor (or on 0 == outside)
(enum value) anchored_overlays_tooltip_timing.Ev.press = 4a press elsewhere; also stands in for key/scroll
press, /// a press elsewhere; also stands in for key/scroll
}
/// One driver step: advance the clock by `dtMs`, then deliver `ev`.
struct (struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step
{
int (field) int anchored_overlays_tooltip_timing.Step.dtMsdtMs;
(enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev (field) anchored_overlays_tooltip_timing.Ev anchored_overlays_tooltip_timing.Step.evev;
(alias) object.size_t = ulongsize_t (field) ulong anchored_overlays_tooltip_timing.Step.anchoranchor;
(alias) object.string = stringstring (field) string anchored_overlays_tooltip_timing.Step.whywhat the step is meant to prove
why; /// what the step is meant to prove
}
(alias) object.string = stringstring string anchored_overlays_tooltip_timing.evName(anchored_overlays_tooltip_timing.Ev e) pure nothrow @nogc @safeevName((enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev (parameter) anchored_overlays_tooltip_timing.Ev ee) @safe pure nothrow @nogc
{
final switch ((parameter) anchored_overlays_tooltip_timing.Ev ee) with ((enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev)
{
case (enum value) anchored_overlays_tooltip_timing.Ev.tick = cast(ubyte)0utime passes, nothing happens
tick: return "tick";
case (enum value) anchored_overlays_tooltip_timing.Ev.enter = 1the pointer rests on an anchor
enter: return "enter";
case (enum value) anchored_overlays_tooltip_timing.Ev.leave = 2the pointer leaves the group
leave: return "leave";
case (enum value) anchored_overlays_tooltip_timing.Ev.tap = 3press+release on an anchor (or on 0 == outside)
tap: return "tap";
case (enum value) anchored_overlays_tooltip_timing.Ev.press = 4a press elsewhere; also stands in for key/scroll
press: return "press";
}
}
(alias) object.string = stringstring string anchored_overlays_tooltip_timing.anchorLabel(ulong id) pure nothrow @nogc @safeanchorLabel((alias) object.size_t = ulongsize_t (parameter) ulong idid) @safe pure nothrow @nogc
=> (parameter) ulong idid == 0 ? "-" : (parameter) ulong idid == 1 ? "A" : (parameter) ulong idid == 2 ? "B" : "C";
/// `enter A`, `tick`, `tap outside` … — built through an output range, so the
/// label costs no allocation even though `main` only prints it.
void void anchored_overlays_tooltip_timing.writeEvent!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false))(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) w, in anchored_overlays_tooltip_timing.Step s) pure nothrow @nogc @safeenter A, tick, tap outside … — built through an output range, so the
label costs no allocation even though main only prints it.
writeEvent(Writer)(ref (alias) Writer = sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false)Writer (parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) ww, in (struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step (parameter) const(anchored_overlays_tooltip_timing.Step) ss)
{
void std.range.primitives.put!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false), string)(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) r, string e) pure nothrow @nogc @safeOutputs e to r. The exact effect is dependent upon the two
types. Several cases are accepted, as described below. The code snippets
are attempted in order, and the first to compile "wins" and gets
evaluated.
In this table "doPut" is a method that places e into r, using the
correct primitive: r`.`put`(`e`)` if `R` defines put, r.front = e
if r is an input range (followed by r.popFront()), or ``r(e)
otherwise.
Code Snippet
Scenario
| ``r.doPut(e); |
R specifically accepts an E. |
| ``r.doPut([ e ]); |
R specifically accepts an E[]. |
| r`.putChar(`e`);` |
`R` accepts some form of string or character. `put` will
transcode the character e`` accordingly. |
| for (; !e.empty; e.popFront()) put(r, e.front); |
Copying range E into R. |
Tip
put should not be used "UFCS-style", e.g. r`.`put`(`e`)`.
Doing this may call `R.`put directly, by-passing any transformation
feature provided by Range.put. put(r, e) is prefered.
Examples
When an output range's put method only accepts elements of type
T, use the global put to handle outputting a T[] to the range
or vice-versa.
import std.traits : isSomeChar;
static struct A
{
string data;
void put(C)(C c)
if (isSomeChar!C)
{
data ~= c;
}
}
static assert(isOutputRange!(A, char));
auto a = A();
put(a, "Hello");
assert(a.data == "Hello");
put treats dynamic arrays as array slices, and will call popFront
on the slice after an element has been copied.
Be sure to save the position of the array before calling put.
int[] a = [1, 2, 3], b = [10, 20];
auto c = a;
put(a, b);
assert(c == [10, 20, 3]);
// at this point, a was advanced twice, so it only contains
// its last element while c represents the whole array
assert(a == [3]);
It's also possible to put any width strings or characters into narrow
strings -- put does the conversion for you.
Note that putting the same width character as the target buffer type is
nothrow, but transcoding can throw a UTFException.
// the elements must be mutable, so using string or const(char)[]
// won't compile
char[] s1 = new char[13];
auto r1 = s1;
put(r1, "Hello, World!"w);
assert(s1 == "Hello, World!");
put((parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) ww, string anchored_overlays_tooltip_timing.evName(anchored_overlays_tooltip_timing.Ev e) pure nothrow @nogc @safeevName((parameter) const(anchored_overlays_tooltip_timing.Step) ss.(field) anchored_overlays_tooltip_timing.Ev anchored_overlays_tooltip_timing.Step.evev));
if ((parameter) const(anchored_overlays_tooltip_timing.Step) ss.(field) ulong anchored_overlays_tooltip_timing.Step.anchoranchor != 0)
{
void std.range.primitives.put!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false), string)(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) r, string e) pure nothrow @nogc @safeOutputs e to r. The exact effect is dependent upon the two
types. Several cases are accepted, as described below. The code snippets
are attempted in order, and the first to compile "wins" and gets
evaluated.
In this table "doPut" is a method that places e into r, using the
correct primitive: r`.`put`(`e`)` if `R` defines put, r.front = e
if r is an input range (followed by r.popFront()), or ``r(e)
otherwise.
Code Snippet
Scenario
| ``r.doPut(e); |
R specifically accepts an E. |
| ``r.doPut([ e ]); |
R specifically accepts an E[]. |
| r`.putChar(`e`);` |
`R` accepts some form of string or character. `put` will
transcode the character e`` accordingly. |
| for (; !e.empty; e.popFront()) put(r, e.front); |
Copying range E into R. |
Tip
put should not be used "UFCS-style", e.g. r`.`put`(`e`)`.
Doing this may call `R.`put directly, by-passing any transformation
feature provided by Range.put. put(r, e) is prefered.
Examples
When an output range's put method only accepts elements of type
T, use the global put to handle outputting a T[] to the range
or vice-versa.
import std.traits : isSomeChar;
static struct A
{
string data;
void put(C)(C c)
if (isSomeChar!C)
{
data ~= c;
}
}
static assert(isOutputRange!(A, char));
auto a = A();
put(a, "Hello");
assert(a.data == "Hello");
put treats dynamic arrays as array slices, and will call popFront
on the slice after an element has been copied.
Be sure to save the position of the array before calling put.
int[] a = [1, 2, 3], b = [10, 20];
auto c = a;
put(a, b);
assert(c == [10, 20, 3]);
// at this point, a was advanced twice, so it only contains
// its last element while c represents the whole array
assert(a == [3]);
It's also possible to put any width strings or characters into narrow
strings -- put does the conversion for you.
Note that putting the same width character as the target buffer type is
nothrow, but transcoding can throw a UTFException.
// the elements must be mutable, so using string or const(char)[]
// won't compile
char[] s1 = new char[13];
auto r1 = s1;
put(r1, "Hello, World!"w);
assert(s1 == "Hello, World!");
put((parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) ww, " ");
void std.range.primitives.put!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false), string)(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) r, string e) pure nothrow @nogc @safeOutputs e to r. The exact effect is dependent upon the two
types. Several cases are accepted, as described below. The code snippets
are attempted in order, and the first to compile "wins" and gets
evaluated.
In this table "doPut" is a method that places e into r, using the
correct primitive: r`.`put`(`e`)` if `R` defines put, r.front = e
if r is an input range (followed by r.popFront()), or ``r(e)
otherwise.
Code Snippet
Scenario
| ``r.doPut(e); |
R specifically accepts an E. |
| ``r.doPut([ e ]); |
R specifically accepts an E[]. |
| r`.putChar(`e`);` |
`R` accepts some form of string or character. `put` will
transcode the character e`` accordingly. |
| for (; !e.empty; e.popFront()) put(r, e.front); |
Copying range E into R. |
Tip
put should not be used "UFCS-style", e.g. r`.`put`(`e`)`.
Doing this may call `R.`put directly, by-passing any transformation
feature provided by Range.put. put(r, e) is prefered.
Examples
When an output range's put method only accepts elements of type
T, use the global put to handle outputting a T[] to the range
or vice-versa.
import std.traits : isSomeChar;
static struct A
{
string data;
void put(C)(C c)
if (isSomeChar!C)
{
data ~= c;
}
}
static assert(isOutputRange!(A, char));
auto a = A();
put(a, "Hello");
assert(a.data == "Hello");
put treats dynamic arrays as array slices, and will call popFront
on the slice after an element has been copied.
Be sure to save the position of the array before calling put.
int[] a = [1, 2, 3], b = [10, 20];
auto c = a;
put(a, b);
assert(c == [10, 20, 3]);
// at this point, a was advanced twice, so it only contains
// its last element while c represents the whole array
assert(a == [3]);
It's also possible to put any width strings or characters into narrow
strings -- put does the conversion for you.
Note that putting the same width character as the target buffer type is
nothrow, but transcoding can throw a UTFException.
// the elements must be mutable, so using string or const(char)[]
// won't compile
char[] s1 = new char[13];
auto r1 = s1;
put(r1, "Hello, World!"w);
assert(s1 == "Hello, World!");
put((parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) ww, string anchored_overlays_tooltip_timing.anchorLabel(ulong id) pure nothrow @nogc @safeanchorLabel((parameter) const(anchored_overlays_tooltip_timing.Step) ss.(field) ulong anchored_overlays_tooltip_timing.Step.anchoranchor));
}
else if ((parameter) const(anchored_overlays_tooltip_timing.Step) ss.(field) anchored_overlays_tooltip_timing.Ev anchored_overlays_tooltip_timing.Step.evev == (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tap = 3press+release on an anchor (or on 0 == outside)
tap)
void std.range.primitives.put!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false), string)(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) r, string e) pure nothrow @nogc @safeOutputs e to r. The exact effect is dependent upon the two
types. Several cases are accepted, as described below. The code snippets
are attempted in order, and the first to compile "wins" and gets
evaluated.
In this table "doPut" is a method that places e into r, using the
correct primitive: r`.`put`(`e`)` if `R` defines put, r.front = e
if r is an input range (followed by r.popFront()), or ``r(e)
otherwise.
Code Snippet
Scenario
| ``r.doPut(e); |
R specifically accepts an E. |
| ``r.doPut([ e ]); |
R specifically accepts an E[]. |
| r`.putChar(`e`);` |
`R` accepts some form of string or character. `put` will
transcode the character e`` accordingly. |
| for (; !e.empty; e.popFront()) put(r, e.front); |
Copying range E into R. |
Tip
put should not be used "UFCS-style", e.g. r`.`put`(`e`)`.
Doing this may call `R.`put directly, by-passing any transformation
feature provided by Range.put. put(r, e) is prefered.
Examples
When an output range's put method only accepts elements of type
T, use the global put to handle outputting a T[] to the range
or vice-versa.
import std.traits : isSomeChar;
static struct A
{
string data;
void put(C)(C c)
if (isSomeChar!C)
{
data ~= c;
}
}
static assert(isOutputRange!(A, char));
auto a = A();
put(a, "Hello");
assert(a.data == "Hello");
put treats dynamic arrays as array slices, and will call popFront
on the slice after an element has been copied.
Be sure to save the position of the array before calling put.
int[] a = [1, 2, 3], b = [10, 20];
auto c = a;
put(a, b);
assert(c == [10, 20, 3]);
// at this point, a was advanced twice, so it only contains
// its last element while c represents the whole array
assert(a == [3]);
It's also possible to put any width strings or characters into narrow
strings -- put does the conversion for you.
Note that putting the same width character as the target buffer type is
nothrow, but transcoding can throw a UTFException.
// the elements must be mutable, so using string or const(char)[]
// won't compile
char[] s1 = new char[13];
auto r1 = s1;
put(r1, "Hello, World!"w);
assert(s1 == "Hello, World!");
put((parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) ww, " outside");
}
/// `cold` / `warm 180`.
void void anchored_overlays_tooltip_timing.writeGroup!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false))(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) w, in anchored_overlays_tooltip_timing.DwellGroup g) pure nothrow @nogc @safecold / warm 180.
writeGroup(Writer)(ref (alias) Writer = sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false)Writer (parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) ww, in (struct) anchored_overlays_tooltip_timing.DwellGroupThe whole cross-instance protocol: which anchor owns the open surface, and
how much warmth is left once it closes. Nine independent lineages converged
on exactly this much state. With an injected wall clock the second field is
the absolute warmUntilMs the proposal specifies; this driver supplies
dtMs (the shape Timeline.stepped already consumes), so it counts down.
One integer either way.
DwellGroup (parameter) const(anchored_overlays_tooltip_timing.DwellGroup) gg)
{
if ((parameter) const(anchored_overlays_tooltip_timing.DwellGroup) gg.(field) ulong anchored_overlays_tooltip_timing.DwellGroup.openIdthe anchor currently showing, 0 == none
openId != 0)
void std.range.primitives.put!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false), string)(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) r, string e) pure nothrow @nogc @safeOutputs e to r. The exact effect is dependent upon the two
types. Several cases are accepted, as described below. The code snippets
are attempted in order, and the first to compile "wins" and gets
evaluated.
In this table "doPut" is a method that places e into r, using the
correct primitive: r`.`put`(`e`)` if `R` defines put, r.front = e
if r is an input range (followed by r.popFront()), or ``r(e)
otherwise.
Code Snippet
Scenario
| ``r.doPut(e); |
R specifically accepts an E. |
| ``r.doPut([ e ]); |
R specifically accepts an E[]. |
| r`.putChar(`e`);` |
`R` accepts some form of string or character. `put` will
transcode the character e`` accordingly. |
| for (; !e.empty; e.popFront()) put(r, e.front); |
Copying range E into R. |
Tip
put should not be used "UFCS-style", e.g. r`.`put`(`e`)`.
Doing this may call `R.`put directly, by-passing any transformation
feature provided by Range.put. put(r, e) is prefered.
Examples
When an output range's put method only accepts elements of type
T, use the global put to handle outputting a T[] to the range
or vice-versa.
import std.traits : isSomeChar;
static struct A
{
string data;
void put(C)(C c)
if (isSomeChar!C)
{
data ~= c;
}
}
static assert(isOutputRange!(A, char));
auto a = A();
put(a, "Hello");
assert(a.data == "Hello");
put treats dynamic arrays as array slices, and will call popFront
on the slice after an element has been copied.
Be sure to save the position of the array before calling put.
int[] a = [1, 2, 3], b = [10, 20];
auto c = a;
put(a, b);
assert(c == [10, 20, 3]);
// at this point, a was advanced twice, so it only contains
// its last element while c represents the whole array
assert(a == [3]);
It's also possible to put any width strings or characters into narrow
strings -- put does the conversion for you.
Note that putting the same width character as the target buffer type is
nothrow, but transcoding can throw a UTFException.
// the elements must be mutable, so using string or const(char)[]
// won't compile
char[] s1 = new char[13];
auto r1 = s1;
put(r1, "Hello, World!"w);
assert(s1 == "Hello, World!");
put((parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) ww, "open");
else if ((parameter) const(anchored_overlays_tooltip_timing.DwellGroup) gg.(field) int anchored_overlays_tooltip_timing.DwellGroup.warmMsLeft0 ⇒ the next tooltip in the group opens instantly
warmMsLeft > 0)
{
void std.range.primitives.put!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false), string)(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) r, string e) pure nothrow @nogc @safeOutputs e to r. The exact effect is dependent upon the two
types. Several cases are accepted, as described below. The code snippets
are attempted in order, and the first to compile "wins" and gets
evaluated.
In this table "doPut" is a method that places e into r, using the
correct primitive: r`.`put`(`e`)` if `R` defines put, r.front = e
if r is an input range (followed by r.popFront()), or ``r(e)
otherwise.
Code Snippet
Scenario
| ``r.doPut(e); |
R specifically accepts an E. |
| ``r.doPut([ e ]); |
R specifically accepts an E[]. |
| r`.putChar(`e`);` |
`R` accepts some form of string or character. `put` will
transcode the character e`` accordingly. |
| for (; !e.empty; e.popFront()) put(r, e.front); |
Copying range E into R. |
Tip
put should not be used "UFCS-style", e.g. r`.`put`(`e`)`.
Doing this may call `R.`put directly, by-passing any transformation
feature provided by Range.put. put(r, e) is prefered.
Examples
When an output range's put method only accepts elements of type
T, use the global put to handle outputting a T[] to the range
or vice-versa.
import std.traits : isSomeChar;
static struct A
{
string data;
void put(C)(C c)
if (isSomeChar!C)
{
data ~= c;
}
}
static assert(isOutputRange!(A, char));
auto a = A();
put(a, "Hello");
assert(a.data == "Hello");
put treats dynamic arrays as array slices, and will call popFront
on the slice after an element has been copied.
Be sure to save the position of the array before calling put.
int[] a = [1, 2, 3], b = [10, 20];
auto c = a;
put(a, b);
assert(c == [10, 20, 3]);
// at this point, a was advanced twice, so it only contains
// its last element while c represents the whole array
assert(a == [3]);
It's also possible to put any width strings or characters into narrow
strings -- put does the conversion for you.
Note that putting the same width character as the target buffer type is
nothrow, but transcoding can throw a UTFException.
// the elements must be mutable, so using string or const(char)[]
// won't compile
char[] s1 = new char[13];
auto r1 = s1;
put(r1, "Hello, World!"w);
assert(s1 == "Hello, World!");
put((parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) ww, "warm ");
void sparkles.base.text.writers.writeValue!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false), int)(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) w, ref const(int) val) pure nothrow @nogc @safeWrites any value to an output range using best-effort @nogc conversion.
Dispatch order:
bool — writes "true" or "false"
Integral types — uses writeInteger
Floating-point types — uses writeFloat
char — writes the character directly
String/char slices — writes directly via put
User types with @nogc output range toString — calls t.toString(writer)
User types with @nogc sink toString — calls with a forwarding delegate
User types with @nogc toString() returning string — writes the result
User types with @nogc string cast — writes cast(string) t
Fallback — uses std.conv.to!string (GC-allocating)
writeValue((parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) ww, (parameter) const(anchored_overlays_tooltip_timing.DwellGroup) gg.(field) int anchored_overlays_tooltip_timing.DwellGroup.warmMsLeft0 ⇒ the next tooltip in the group opens instantly
warmMsLeft);
}
else
void std.range.primitives.put!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false), string)(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) r, string e) pure nothrow @nogc @safeOutputs e to r. The exact effect is dependent upon the two
types. Several cases are accepted, as described below. The code snippets
are attempted in order, and the first to compile "wins" and gets
evaluated.
In this table "doPut" is a method that places e into r, using the
correct primitive: r`.`put`(`e`)` if `R` defines put, r.front = e
if r is an input range (followed by r.popFront()), or ``r(e)
otherwise.
Code Snippet
Scenario
| ``r.doPut(e); |
R specifically accepts an E. |
| ``r.doPut([ e ]); |
R specifically accepts an E[]. |
| r`.putChar(`e`);` |
`R` accepts some form of string or character. `put` will
transcode the character e`` accordingly. |
| for (; !e.empty; e.popFront()) put(r, e.front); |
Copying range E into R. |
Tip
put should not be used "UFCS-style", e.g. r`.`put`(`e`)`.
Doing this may call `R.`put directly, by-passing any transformation
feature provided by Range.put. put(r, e) is prefered.
Examples
When an output range's put method only accepts elements of type
T, use the global put to handle outputting a T[] to the range
or vice-versa.
import std.traits : isSomeChar;
static struct A
{
string data;
void put(C)(C c)
if (isSomeChar!C)
{
data ~= c;
}
}
static assert(isOutputRange!(A, char));
auto a = A();
put(a, "Hello");
assert(a.data == "Hello");
put treats dynamic arrays as array slices, and will call popFront
on the slice after an element has been copied.
Be sure to save the position of the array before calling put.
int[] a = [1, 2, 3], b = [10, 20];
auto c = a;
put(a, b);
assert(c == [10, 20, 3]);
// at this point, a was advanced twice, so it only contains
// its last element while c represents the whole array
assert(a == [3]);
It's also possible to put any width strings or characters into narrow
strings -- put does the conversion for you.
Note that putting the same width character as the target buffer type is
nothrow, but transcoding can throw a UTFException.
// the elements must be mutable, so using string or const(char)[]
// won't compile
char[] s1 = new char[13];
auto r1 = s1;
put(r1, "Hello, World!"w);
assert(s1 == "Hello, World!");
put((parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) ww, "cold");
}
(alias) object.string = stringstring string anchored_overlays_tooltip_timing.dwellPhase(in anchored_overlays_tooltip_timing.TooltipDwell d) pure nothrow @nogc @safedwellPhase(in (struct) anchored_overlays_tooltip_timing.TooltipDwellOne tooltip's timing state: a composed Timeline plus the anchor it belongs
to. armedDelayMs is stored because the deadline must be absolute —
measured from the arm instant, never recomputed as now + delay — which
Timeline.stepped gives for free as long as the config does not change
under it.
TooltipDwell (parameter) const(anchored_overlays_tooltip_timing.TooltipDwell) dd) @safe pure nothrow @nogc
=> (parameter) const(anchored_overlays_tooltip_timing.TooltipDwell) dd.bool anchored_overlays_tooltip_timing.TooltipDwell.warming() const pure nothrow @nogc @safeCounting down to the deadline, and not in the display or hit list.
warming ? "warming" : (parameter) const(anchored_overlays_tooltip_timing.TooltipDwell) dd.bool anchored_overlays_tooltip_timing.TooltipDwell.shown() const pure nothrow @nogc @safePainted. Note this is phase == hold, not life.visible() — see § 2.
shown ? "shown" : "idle";
/// The hover script: A warms up and shows, B rides the warm group instantly,
/// the cool-down expires before C, a press clears warmth, and an abandoned
/// warm-up leaves the group cold.
immutable (struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step[] (immutable global) immutable(anchored_overlays_tooltip_timing.Step[]) anchored_overlays_tooltip_timing.hoverScriptThe hover script: A warms up and shows, B rides the warm group instantly,
the cool-down expires before C, a press clears warmth, and an abandoned
warm-up leaves the group cold.
hoverScript = [
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(0, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.enter = 1the pointer rests on an anchor
enter, 1, "cold group: A pays the full warm-up"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(250, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tick = cast(ubyte)0utime passes, nothing happens
tick, 0, "mid warm-up: armed, not painted"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(250, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tick = cast(ubyte)0utime passes, nothing happens
tick, 0, "deadline reached: A is shown"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(700, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tick = cast(ubyte)0utime passes, nothing happens
tick, 0, "holdUntilDismissed: no 1.2 s self-close"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(0, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.leave = 2the pointer leaves the group
leave, 0, "A closes and arms the cool-down"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(120, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.enter = 1the pointer rests on an anchor
enter, 2, "group still warm: B is INSTANT"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(300, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tick = cast(ubyte)0utime passes, nothing happens
tick, 0, "B holds"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(0, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.leave = 2the pointer leaves the group
leave, 0, "B closes, cool-down re-armed"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(400, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tick = cast(ubyte)0utime passes, nothing happens
tick, 0, "cool-down expires: the group is cold"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(0, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.enter = 1the pointer rests on an anchor
enter, 3, "C pays the full warm-up again"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(500, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tick = cast(ubyte)0utime passes, nothing happens
tick, 0, "C is shown"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(0, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.press = 4a press elsewhere; also stands in for key/scroll
press, 3, "press closes AND clears warmth"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(50, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.enter = 1the pointer rests on an anchor
enter, 1, "warmth was cleared: A warms up again"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(200, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.leave = 2the pointer leaves the group
leave, 0, "abandoned mid-warm-up: no cool-down armed"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(0, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.enter = 1the pointer rests on an anchor
enter, 2, "so B pays full price, not the skip window"),
];
/// The touch script: the substituted trigger, on the same three anchors.
immutable (struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step[] (immutable global) immutable(anchored_overlays_tooltip_timing.Step[]) anchored_overlays_tooltip_timing.tapScriptThe touch script: the substituted trigger, on the same three anchors.
tapScript = [
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(0, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tap = 3press+release on an anchor (or on 0 == outside)
tap, 1, "tap pins A instantly (openMs is statically 0)"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(900, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tick = cast(ubyte)0utime passes, nothing happens
tick, 0, "pinned: no max-duration, no self-close"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(0, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tap = 3press+release on an anchor (or on 0 == outside)
tap, 2, "tap B swaps the pinned surface"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(0, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tap = 3press+release on an anchor (or on 0 == outside)
tap, 2, "tap the same anchor: triggerReactivate dismisses"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(0, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tap = 3press+release on an anchor (or on 0 == outside)
tap, 3, "tap C pins C"),
(struct) anchored_overlays_tooltip_timing.StepOne driver step: advance the clock by dtMs, then deliver ev.
Step(0, (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tap = 3press+release on an anchor (or on 0 == outside)
tap, 0, "tap outside dismisses"),
];
// ---------------------------------------------------------------------------
// Trigger resolution (the sketch from proposal.md § 3.5, reduced to what a
// tooltip needs)
// ---------------------------------------------------------------------------
/// A declaration, not a behaviour: which triggers the component *wants*.
struct (struct) anchored_overlays_tooltip_timing.TriggerPolicyA declaration, not a behaviour: which triggers the component wants.
TriggerPolicy
{
bool (field) bool anchored_overlays_tooltip_timing.TriggerPolicy.hoverhover, (field) bool anchored_overlays_tooltip_timing.TriggerPolicy.activateactivate, (field) bool anchored_overlays_tooltip_timing.TriggerPolicy.longPresslongPress;
}
/// What the target can actually serve, and what it put in hover's place.
struct (struct) anchored_overlays_tooltip_timing.TriggerPlanWhat the target can actually serve, and what it put in hover's place.
TriggerPlan
{
(struct) anchored_overlays_tooltip_timing.TriggerPolicyA declaration, not a behaviour: which triggers the component wants.
TriggerPolicy (field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.servedserved, (field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.droppeddropped, (field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.substitutedsubstituted;
}
/// `caps.hover == false` moves `hover` to `substituted` and lets `activate`
/// (tap-to-pin) carry it — the only substitution expressible on all three live
/// targets. Long press is **not** the default substitute: it exceeds the cell
/// pointer's tier, and on Android it collides with text selection.
(struct) anchored_overlays_tooltip_timing.TriggerPlanWhat the target can actually serve, and what it put in hover's place.
TriggerPlan anchored_overlays_tooltip_timing.TriggerPlan anchored_overlays_tooltip_timing.resolveTriggers(in anchored_overlays_tooltip_timing.TriggerPolicy want, in sparkles.input.capability.InputCapabilities caps) pure nothrow @nogc @safe``caps.hover == false moves hover to substituted and lets activate
(tap-to-pin) carry it — the only substitution expressible on all three live
targets. Long press is not the default substitute: it exceeds the cell
pointer's tier, and on Android it collides with text selection.
resolveTriggers(in (struct) anchored_overlays_tooltip_timing.TriggerPolicyA declaration, not a behaviour: which triggers the component wants.
TriggerPolicy (parameter) const(anchored_overlays_tooltip_timing.TriggerPolicy) wantwant, in (struct) sparkles.input.capability.InputCapabilitiesA target's declared input affordances.
The defaults describe a mouse: the historical assumption, so a producer
that has not thought about this yet keeps today's behavior rather than
silently claiming a capability it lacks in the other direction.
Declare with one of the profiles below where one fits — mousePointer,
touchPointer, cellPointer, staticPointer — so the
vocabulary stays small and two targets that mean the same thing say it the
same way.
InputCapabilities (parameter) const(sparkles.input.capability.InputCapabilities) capscaps)
@safe pure nothrow @nogc
{
(struct) anchored_overlays_tooltip_timing.TriggerPlanWhat the target can actually serve, and what it put in hover's place.
TriggerPlan (local variable) anchored_overlays_tooltip_timing.TriggerPlan pp;
(local variable) anchored_overlays_tooltip_timing.TriggerPlan pp.(field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.servedserved.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.activateactivate = (parameter) const(anchored_overlays_tooltip_timing.TriggerPolicy) wantwant.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.activateactivate;
if ((parameter) const(anchored_overlays_tooltip_timing.TriggerPolicy) wantwant.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.hoverhover && (parameter) const(sparkles.input.capability.InputCapabilities) capscaps.(field) bool sparkles.input.capability.InputCapabilities.hoverThe pointer can rest on a target without pressing.
false on touch, and it is the one that changes component behavior most:
everything hover-driven needs a second route, because on a touchscreen the
"pointer position" between taps is simply the last place a finger left.
hover)
(local variable) anchored_overlays_tooltip_timing.TriggerPlan pp.(field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.servedserved.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.hoverhover = true;
else if ((parameter) const(anchored_overlays_tooltip_timing.TriggerPolicy) wantwant.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.hoverhover && (parameter) const(anchored_overlays_tooltip_timing.TriggerPolicy) wantwant.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.activateactivate)
(local variable) anchored_overlays_tooltip_timing.TriggerPlan pp.(field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.substitutedsubstituted.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.hoverhover = true;
else if ((parameter) const(anchored_overlays_tooltip_timing.TriggerPolicy) wantwant.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.hoverhover)
(local variable) anchored_overlays_tooltip_timing.TriggerPlan pp.(field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.droppeddropped.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.hoverhover = true;
if ((parameter) const(anchored_overlays_tooltip_timing.TriggerPolicy) wantwant.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.longPresslongPress && (parameter) const(sparkles.input.capability.InputCapabilities) capscaps.(field) bool sparkles.input.capability.InputCapabilities.hoverThe pointer can rest on a target without pressing.
false on touch, and it is the one that changes component behavior most:
everything hover-driven needs a second route, because on a touchscreen the
"pointer position" between taps is simply the last place a finger left.
hover)
(local variable) anchored_overlays_tooltip_timing.TriggerPlan pp.(field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.droppeddropped.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.longPresslongPress = true; // deliberate: never the default substitute
else
(local variable) anchored_overlays_tooltip_timing.TriggerPlan pp.(field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.servedserved.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.longPresslongPress = (parameter) const(anchored_overlays_tooltip_timing.TriggerPolicy) wantwant.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.longPresslongPress;
return (local variable) anchored_overlays_tooltip_timing.TriggerPlan pp;
}
// ---------------------------------------------------------------------------
// The tier-0 model: what `:hover` + `transition-delay` can and cannot express
// ---------------------------------------------------------------------------
/// Static HTML has no cross-element state, so *this is the entire machine*: how
/// long one element has matched `:hover`. There is no group, no arbiter, and
/// nothing to interrupt.
struct (struct) anchored_overlays_tooltip_timing.CssHoverStatic HTML has no cross-element state, so this is the entire machine: how
long one element has matched :hover. There is no group, no arbiter, and
nothing to interrupt.
CssHover
{
(alias) object.size_t = ulongsize_t (field) ulong anchored_overlays_tooltip_timing.CssHover.hoveredIdhoveredId;
int (field) int anchored_overlays_tooltip_timing.CssHover.hoveredMshoveredMs;
}
(struct) anchored_overlays_tooltip_timing.CssHoverStatic HTML has no cross-element state, so this is the entire machine: how
long one element has matched :hover. There is no group, no arbiter, and
nothing to interrupt.
CssHover anchored_overlays_tooltip_timing.CssHover anchored_overlays_tooltip_timing.cssEntered(ulong id) pure nothrow @nogc @safecssEntered((alias) object.size_t = ulongsize_t (parameter) ulong idid) @safe pure nothrow @nogc => (struct) anchored_overlays_tooltip_timing.CssHoverStatic HTML has no cross-element state, so this is the entire machine: how
long one element has matched :hover. There is no group, no arbiter, and
nothing to interrupt.
CssHover((parameter) ulong idid, 0);
(struct) anchored_overlays_tooltip_timing.CssHoverStatic HTML has no cross-element state, so this is the entire machine: how
long one element has matched :hover. There is no group, no arbiter, and
nothing to interrupt.
CssHover anchored_overlays_tooltip_timing.CssHover anchored_overlays_tooltip_timing.cssLeft() pure nothrow @nogc @safecssLeft() @safe pure nothrow @nogc => (struct) anchored_overlays_tooltip_timing.CssHoverStatic HTML has no cross-element state, so this is the entire machine: how
long one element has matched :hover. There is no group, no arbiter, and
nothing to interrupt.
CssHover(0, 0);
/// Leaving reverts the property, so mid-delay cancellation is free — the one
/// piece of the machine tier-0 gets right for nothing.
(struct) anchored_overlays_tooltip_timing.CssHoverStatic HTML has no cross-element state, so this is the entire machine: how
long one element has matched :hover. There is no group, no arbiter, and
nothing to interrupt.
CssHover anchored_overlays_tooltip_timing.CssHover anchored_overlays_tooltip_timing.cssStepped(in anchored_overlays_tooltip_timing.CssHover s, int dtMs) pure nothrow @nogc @safeLeaving reverts the property, so mid-delay cancellation is free — the one
piece of the machine tier-0 gets right for nothing.
cssStepped(in (struct) anchored_overlays_tooltip_timing.CssHoverStatic HTML has no cross-element state, so this is the entire machine: how
long one element has matched :hover. There is no group, no arbiter, and
nothing to interrupt.
CssHover (parameter) const(anchored_overlays_tooltip_timing.CssHover) ss, int (parameter) int dtMsdtMs) @safe pure nothrow @nogc
=> (struct) anchored_overlays_tooltip_timing.CssHoverStatic HTML has no cross-element state, so this is the entire machine: how
long one element has matched :hover. There is no group, no arbiter, and
nothing to interrupt.
CssHover((parameter) const(anchored_overlays_tooltip_timing.CssHover) ss.(field) ulong anchored_overlays_tooltip_timing.CssHover.hoveredIdhoveredId, (parameter) const(anchored_overlays_tooltip_timing.CssHover) ss.(field) ulong anchored_overlays_tooltip_timing.CssHover.hoveredIdhoveredId == 0 ? 0 : (parameter) const(anchored_overlays_tooltip_timing.CssHover) ss.(field) int anchored_overlays_tooltip_timing.CssHover.hoveredMshoveredMs + (parameter) int dtMsdtMs);
bool bool anchored_overlays_tooltip_timing.cssVisible(in anchored_overlays_tooltip_timing.CssHover s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safecssVisible(in (struct) anchored_overlays_tooltip_timing.CssHoverStatic HTML has no cross-element state, so this is the entire machine: how
long one element has matched :hover. There is no group, no arbiter, and
nothing to interrupt.
CssHover (parameter) const(anchored_overlays_tooltip_timing.CssHover) ss, in (struct) anchored_overlays_tooltip_timing.DwellConfigThe two durations the whole dimension reduces to. Both belong in Palette
beside popupPadX, exactly as WinUI puts them in the OS and Qt in QStyle;
they are parameters here so the trace can name them.
skipMs == 0 must statically disable warmth rather than arm a zero-length
timer — Radix shipped that bug (#3873: a zero-length timer left every tooltip
instant forever) and fixed it with early returns in both provider callbacks.
``DwellConfig.warmthEnabled is that early return, expressed as a value.
DwellConfig (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc) @safe pure nothrow @nogc
=> (parameter) const(anchored_overlays_tooltip_timing.CssHover) ss.(field) ulong anchored_overlays_tooltip_timing.CssHover.hoveredIdhoveredId != 0 && (parameter) const(anchored_overlays_tooltip_timing.CssHover) ss.(field) int anchored_overlays_tooltip_timing.CssHover.hoveredMshoveredMs >= (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc.(field) int anchored_overlays_tooltip_timing.DwellConfig.warmUpMsthe first tooltip in a cold group waits this long
warmUpMs;
/// The rule pair a `DwellConfig` compiles to. It needs one change to
/// `interp/html_semantic.d:57-58`: `display` is not transitionable, so the
/// tier-0 reveal must switch to `visibility`/`opacity`.
void void anchored_overlays_tooltip_timing.writeTier0Css!(sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false))(ref sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) w, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe rule pair a DwellConfig compiles to. It needs one change to
interp/html_semantic.d:57-58: display is not transitionable, so the
tier-0 reveal must switch to visibility/opacity.
writeTier0Css(Writer)(ref (alias) Writer = sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false)Writer (parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) ww, in (struct) anchored_overlays_tooltip_timing.DwellConfigThe two durations the whole dimension reduces to. Both belong in Palette
beside popupPadX, exactly as WinUI puts them in the OS and Qt in QStyle;
they are parameters here so the trace can name them.
skipMs == 0 must statically disable warmth rather than arm a zero-length
timer — Radix shipped that bug (#3873: a zero-length timer left every tooltip
instant forever) and fixed it with early returns in both provider callbacks.
``DwellConfig.warmthEnabled is that early return, expressed as a value.
DwellConfig (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc)
{
void std.range.primitives.put!(sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false), string)(ref sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) r, string e) pure nothrow @nogc @safeOutputs e to r. The exact effect is dependent upon the two
types. Several cases are accepted, as described below. The code snippets
are attempted in order, and the first to compile "wins" and gets
evaluated.
In this table "doPut" is a method that places e into r, using the
correct primitive: r`.`put`(`e`)` if `R` defines put, r.front = e
if r is an input range (followed by r.popFront()), or ``r(e)
otherwise.
Code Snippet
Scenario
| ``r.doPut(e); |
R specifically accepts an E. |
| ``r.doPut([ e ]); |
R specifically accepts an E[]. |
| r`.putChar(`e`);` |
`R` accepts some form of string or character. `put` will
transcode the character e`` accordingly. |
| for (; !e.empty; e.popFront()) put(r, e.front); |
Copying range E into R. |
Tip
put should not be used "UFCS-style", e.g. r`.`put`(`e`)`.
Doing this may call `R.`put directly, by-passing any transformation
feature provided by Range.put. put(r, e) is prefered.
Examples
When an output range's put method only accepts elements of type
T, use the global put to handle outputting a T[] to the range
or vice-versa.
import std.traits : isSomeChar;
static struct A
{
string data;
void put(C)(C c)
if (isSomeChar!C)
{
data ~= c;
}
}
static assert(isOutputRange!(A, char));
auto a = A();
put(a, "Hello");
assert(a.data == "Hello");
put treats dynamic arrays as array slices, and will call popFront
on the slice after an element has been copied.
Be sure to save the position of the array before calling put.
int[] a = [1, 2, 3], b = [10, 20];
auto c = a;
put(a, b);
assert(c == [10, 20, 3]);
// at this point, a was advanced twice, so it only contains
// its last element while c represents the whole array
assert(a == [3]);
It's also possible to put any width strings or characters into narrow
strings -- put does the conversion for you.
Note that putting the same width character as the target buffer type is
nothrow, but transcoding can throw a UTFException.
// the elements must be mutable, so using string or const(char)[]
// won't compile
char[] s1 = new char[13];
auto r1 = s1;
put(r1, "Hello, World!"w);
assert(s1 == "Hello, World!");
put((parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) ww, ".spk-reveal{position:absolute;z-index:1;visibility:hidden;opacity:0;"
~ "transition:visibility 0s,opacity 0s;transition-delay:0ms}\n");
void std.range.primitives.put!(sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false), string)(ref sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) r, string e) pure nothrow @nogc @safeOutputs e to r. The exact effect is dependent upon the two
types. Several cases are accepted, as described below. The code snippets
are attempted in order, and the first to compile "wins" and gets
evaluated.
In this table "doPut" is a method that places e into r, using the
correct primitive: r`.`put`(`e`)` if `R` defines put, r.front = e
if r is an input range (followed by r.popFront()), or ``r(e)
otherwise.
Code Snippet
Scenario
| ``r.doPut(e); |
R specifically accepts an E. |
| ``r.doPut([ e ]); |
R specifically accepts an E[]. |
| r`.putChar(`e`);` |
`R` accepts some form of string or character. `put` will
transcode the character e`` accordingly. |
| for (; !e.empty; e.popFront()) put(r, e.front); |
Copying range E into R. |
Tip
put should not be used "UFCS-style", e.g. r`.`put`(`e`)`.
Doing this may call `R.`put directly, by-passing any transformation
feature provided by Range.put. put(r, e) is prefered.
Examples
When an output range's put method only accepts elements of type
T, use the global put to handle outputting a T[] to the range
or vice-versa.
import std.traits : isSomeChar;
static struct A
{
string data;
void put(C)(C c)
if (isSomeChar!C)
{
data ~= c;
}
}
static assert(isOutputRange!(A, char));
auto a = A();
put(a, "Hello");
assert(a.data == "Hello");
put treats dynamic arrays as array slices, and will call popFront
on the slice after an element has been copied.
Be sure to save the position of the array before calling put.
int[] a = [1, 2, 3], b = [10, 20];
auto c = a;
put(a, b);
assert(c == [10, 20, 3]);
// at this point, a was advanced twice, so it only contains
// its last element while c represents the whole array
assert(a == [3]);
It's also possible to put any width strings or characters into narrow
strings -- put does the conversion for you.
Note that putting the same width character as the target buffer type is
nothrow, but transcoding can throw a UTFException.
// the elements must be mutable, so using string or const(char)[]
// won't compile
char[] s1 = new char[13];
auto r1 = s1;
put(r1, "Hello, World!"w);
assert(s1 == "Hello, World!");
put((parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) ww, ".spk-hit:hover>.spk-reveal,.spk-hit:focus-visible>.spk-reveal{"
~ "visibility:visible;opacity:1;transition-delay:");
void sparkles.base.text.writers.writeValue!(sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false), int)(ref sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) w, ref const(int) val) pure nothrow @nogc @safeWrites any value to an output range using best-effort @nogc conversion.
Dispatch order:
bool — writes "true" or "false"
Integral types — uses writeInteger
Floating-point types — uses writeFloat
char — writes the character directly
String/char slices — writes directly via put
User types with @nogc output range toString — calls t.toString(writer)
User types with @nogc sink toString — calls with a forwarding delegate
User types with @nogc toString() returning string — writes the result
User types with @nogc string cast — writes cast(string) t
Fallback — uses std.conv.to!string (GC-allocating)
writeValue((parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) ww, (parameter) const(anchored_overlays_tooltip_timing.DwellConfig) cc.(field) int anchored_overlays_tooltip_timing.DwellConfig.warmUpMsthe first tooltip in a cold group waits this long
warmUpMs);
void std.range.primitives.put!(sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false), string)(ref sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) r, string e) pure nothrow @nogc @safeOutputs e to r. The exact effect is dependent upon the two
types. Several cases are accepted, as described below. The code snippets
are attempted in order, and the first to compile "wins" and gets
evaluated.
In this table "doPut" is a method that places e into r, using the
correct primitive: r`.`put`(`e`)` if `R` defines put, r.front = e
if r is an input range (followed by r.popFront()), or ``r(e)
otherwise.
Code Snippet
Scenario
| ``r.doPut(e); |
R specifically accepts an E. |
| ``r.doPut([ e ]); |
R specifically accepts an E[]. |
| r`.putChar(`e`);` |
`R` accepts some form of string or character. `put` will
transcode the character e`` accordingly. |
| for (; !e.empty; e.popFront()) put(r, e.front); |
Copying range E into R. |
Tip
put should not be used "UFCS-style", e.g. r`.`put`(`e`)`.
Doing this may call `R.`put directly, by-passing any transformation
feature provided by Range.put. put(r, e) is prefered.
Examples
When an output range's put method only accepts elements of type
T, use the global put to handle outputting a T[] to the range
or vice-versa.
import std.traits : isSomeChar;
static struct A
{
string data;
void put(C)(C c)
if (isSomeChar!C)
{
data ~= c;
}
}
static assert(isOutputRange!(A, char));
auto a = A();
put(a, "Hello");
assert(a.data == "Hello");
put treats dynamic arrays as array slices, and will call popFront
on the slice after an element has been copied.
Be sure to save the position of the array before calling put.
int[] a = [1, 2, 3], b = [10, 20];
auto c = a;
put(a, b);
assert(c == [10, 20, 3]);
// at this point, a was advanced twice, so it only contains
// its last element while c represents the whole array
assert(a == [3]);
It's also possible to put any width strings or characters into narrow
strings -- put does the conversion for you.
Note that putting the same width character as the target buffer type is
nothrow, but transcoding can throw a UTFException.
// the elements must be mutable, so using string or const(char)[]
// won't compile
char[] s1 = new char[13];
auto r1 = s1;
put(r1, "Hello, World!"w);
assert(s1 == "Hello, World!");
put((parameter) sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) ww, "ms}\n");
}
// ---------------------------------------------------------------------------
void void D main() @safemain() @safe
{
const (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg = (struct) anchored_overlays_tooltip_timing.DwellConfigThe two durations the whole dimension reduces to. Both belong in Palette
beside popupPadX, exactly as WinUI puts them in the OS and Qt in QStyle;
they are parameters here so the trace can name them.
skipMs == 0 must statically disable warmth rather than arm a zero-length
timer — Radix shipped that bug (#3873: a zero-length timer left every tooltip
instant forever) and fixed it with early returns in both provider callbacks.
``DwellConfig.warmthEnabled is that early return, expressed as a value.
DwellConfig();
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("=== The tooltip timing machine, composed from Timeline (STM6) ===");
void std.stdio.writefln!("warm-up %d ms cool-down (skip window) %d ms warmth enabled: %s", const(int), const(int), bool)(const(int) __param_0, const(int) __param_1, bool __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln!"warm-up %d ms cool-down (skip window) %d ms warmth enabled: %s"(
(local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg.(field) int anchored_overlays_tooltip_timing.DwellConfig.warmUpMsthe first tooltip in a cold group waits this long
warmUpMs, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg.(field) int anchored_overlays_tooltip_timing.DwellConfig.skipMsthe group stays warm this long after one closes
skipMs, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg.bool anchored_overlays_tooltip_timing.DwellConfig.warmthEnabled() const pure nothrow @nogc @safeWarmth is a feature, switched on by a positive skip window.
warmthEnabled);
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("composition: Timeline.Config(fadeInMs: <resolved delay>, "
~ "holdUntilDismissed: true)");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" fadeIn == warming (not painted) hold == shown");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("new state: DwellGroup { openId, warmMsLeft } — two integers, "
~ "no per-widget timer");
// -- 1. the pointer trace ------------------------------------------------
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\n=== 1. The hover script on a pointer target ===");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" t event dwell group shown why");
(struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell (local variable) anchored_overlays_tooltip_timing.Dwell ss;
int (local variable) int tt;
(alias) object.size_t = ulongsize_t (local variable) ulong instantOpensinstantOpens, (local variable) ulong delayedOpensdelayedOpens;
foreach ((parameter) immutable(anchored_overlays_tooltip_timing.Step) stst; (immutable global) immutable(anchored_overlays_tooltip_timing.Step[]) anchored_overlays_tooltip_timing.hoverScriptThe hover script: A warms up and shows, B rides the warm group instantly,
the cool-down expires before C, a press clears warmth, and an abandoned
warm-up leaves the group cold.
hoverScript)
{
(local variable) int tt += (local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) int anchored_overlays_tooltip_timing.Step.dtMsdtMs;
(local variable) anchored_overlays_tooltip_timing.Dwell ss = (local variable) anchored_overlays_tooltip_timing.Dwell ss.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.stepped(in anchored_overlays_tooltip_timing.Dwell s, int dtMs, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeAdvance by dtMs. The warm-up elapses inside Timeline; the cool-down is
the one subtraction this machine adds.
stepped((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) int anchored_overlays_tooltip_timing.Step.dtMsdtMs, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg);
const (local variable) const(anchored_overlays_tooltip_timing.Dwell) beforebefore = (local variable) anchored_overlays_tooltip_timing.Dwell ss;
final switch ((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) anchored_overlays_tooltip_timing.Ev anchored_overlays_tooltip_timing.Step.evev) with ((enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev)
{
case (enum value) anchored_overlays_tooltip_timing.Ev.tick = cast(ubyte)0utime passes, nothing happens
tick: break;
case (enum value) anchored_overlays_tooltip_timing.Ev.enter = 1the pointer rests on an anchor
enter: (local variable) anchored_overlays_tooltip_timing.Dwell ss = (local variable) anchored_overlays_tooltip_timing.Dwell ss.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.entered(in anchored_overlays_tooltip_timing.Dwell s, ulong id, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe pointer came to rest on an anchor.
entered((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) ulong anchored_overlays_tooltip_timing.Step.anchoranchor, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg); break;
case (enum value) anchored_overlays_tooltip_timing.Ev.leave = 2the pointer leaves the group
leave: (local variable) anchored_overlays_tooltip_timing.Dwell ss = (local variable) anchored_overlays_tooltip_timing.Dwell ss.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.left(in anchored_overlays_tooltip_timing.Dwell s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe pointer left the anchor (and the group's region).
left((local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg); break;
case (enum value) anchored_overlays_tooltip_timing.Ev.tap = 3press+release on an anchor (or on 0 == outside)
tap: (local variable) anchored_overlays_tooltip_timing.Dwell ss = (local variable) anchored_overlays_tooltip_timing.Dwell ss.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.entered(in anchored_overlays_tooltip_timing.Dwell s, ulong id, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe pointer came to rest on an anchor.
entered((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) ulong anchored_overlays_tooltip_timing.Step.anchoranchor, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg); break;
case (enum value) anchored_overlays_tooltip_timing.Ev.press = 4a press elsewhere; also stands in for key/scroll
press: (local variable) anchored_overlays_tooltip_timing.Dwell ss = (local variable) anchored_overlays_tooltip_timing.Dwell ss.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.interrupted(in anchored_overlays_tooltip_timing.Dwell s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeA press, a key, or a scroll: closes the surface and clears warmth.
Nothing in React Aria or Radix clears warmth except its own cool-down, so a
click or a scroll leaves the next tooltip in instant mode; Ariakit patched
exactly this symptom on onBlur. Warmth is a property of an uninterrupted
hover context, so the interruption ends it.
interrupted((local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg); break;
}
if ((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) anchored_overlays_tooltip_timing.Ev anchored_overlays_tooltip_timing.Step.evev == (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.enter = 1the pointer rests on an anchor
enter)
{
if ((local variable) anchored_overlays_tooltip_timing.Dwell ss.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.(field) int anchored_overlays_tooltip_timing.TooltipDwell.armedDelayMsthe delay resolved at arm time
armedDelayMs == 0)
++(local variable) ulong instantOpensinstantOpens;
else
++(local variable) ulong delayedOpensdelayedOpens;
}
// The fade-out phase is unreachable here: fadeOutMs is 0 by
// construction, so `shown` and `warming` partition every live state.
assert((local variable) anchored_overlays_tooltip_timing.Dwell ss.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.(field) sparkles.ui.state.Timeline anchored_overlays_tooltip_timing.TooltipDwell.lifefadeIn == warming, hold == shown
life.(field) sparkles.ui.state.Timeline.Phase sparkles.ui.state.Timeline.phasephase != (struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline.(enum) sparkles.ui.state.Timeline.PhaseThe phase of the effect.
Phase.(enum value) sparkles.ui.state.Timeline.Phase.fadeOut = 3disappearing
fadeOut);
assert((local variable) const(anchored_overlays_tooltip_timing.Dwell) beforebefore.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) int anchored_overlays_tooltip_timing.DwellGroup.warmMsLeft0 ⇒ the next tooltip in the group opens instantly
warmMsLeft >= 0);
(struct) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false)A @nogc container with Small Buffer Optimization and copy-on-write.
Elements are stored inline up to N elements, then automatically
allocated on the heap (via AffixAllocator!(Mallocator, ControlBlock), which
keeps the reference count in an allocation prefix; the element capacity is the
heap slice length) when capacity is exceeded. Heap blocks are managed with the
std.experimental.allocator makeArray/expandArray/dispose helpers.
The buffer is copyable. Copying an inline buffer duplicates its elements
(independent copies). Copying a heap buffer shares the allocation and bumps a
reference count; the shared block is cloned copy-on-write the first time a
mutable copy is written. This suits the common pattern of one producer
building a buffer mutably, then handing out many const reader copies — read
via const (e.g. through borrow) never clones. Mutating accessors on a
shared mutable copy clone first, so a mutable slice/reference taken from a
shared buffer and held across a later mutation may be invalidated (the usual
copy-on-write caveat) — read through const to share without that risk.
Note
storage location is tied to length (data is inline whenever
length <= N), so reserve pre-grows only once on the heap,
and clear/popBack that drop the length back to <= N revert
to inline storage.
SmallBuffer!(char, 32) (local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) evev, (local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) grpgrp;
void anchored_overlays_tooltip_timing.writeEvent!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false))(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) w, in anchored_overlays_tooltip_timing.Step s) pure nothrow @nogc @safeenter A, tick, tap outside … — built through an output range, so the
label costs no allocation even though main only prints it.
writeEvent((local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) evev, (local variable) immutable(anchored_overlays_tooltip_timing.Step) stst);
void anchored_overlays_tooltip_timing.writeGroup!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false))(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) w, in anchored_overlays_tooltip_timing.DwellGroup g) pure nothrow @nogc @safecold / warm 180.
writeGroup((local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) grpgrp, (local variable) anchored_overlays_tooltip_timing.Dwell ss.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup);
void std.stdio.writefln!("%5d %-9s %-8s %-9s %-5s %s", int, char[], string, char[], string, string)(int __param_0, char[] __param_1, string __param_2, char[] __param_3, string __param_4, string __param_5) @safeEquivalent to writef(fmt, args, '\n').
writefln!"%5d %-9s %-8s %-9s %-5s %s"(
(local variable) int tt, (local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) evev[], string anchored_overlays_tooltip_timing.dwellPhase(in anchored_overlays_tooltip_timing.TooltipDwell d) pure nothrow @nogc @safedwellPhase((local variable) anchored_overlays_tooltip_timing.Dwell ss.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive), (local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) grpgrp[],
(local variable) anchored_overlays_tooltip_timing.Dwell ss.bool anchored_overlays_tooltip_timing.Dwell.visible() const pure nothrow @nogc @safevisible ? string anchored_overlays_tooltip_timing.anchorLabel(ulong id) pure nothrow @nogc @safeanchorLabel((local variable) anchored_overlays_tooltip_timing.Dwell ss.ulong anchored_overlays_tooltip_timing.Dwell.visibleId() const pure nothrow @nogc @safevisibleId) : "-", (local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) string anchored_overlays_tooltip_timing.Step.whywhat the step is meant to prove
why);
}
void std.stdio.writefln!("\nopens: %d paid the warm-up, %d opened instantly (one arbiter, zero per-widget timers)", ulong, ulong)(ulong __param_0, ulong __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln!("\nopens: %d paid the warm-up, %d opened instantly "
~ "(one arbiter, zero per-widget timers)")((local variable) ulong delayedOpensdelayedOpens, (local variable) ulong instantOpensinstantOpens);
assert((local variable) ulong instantOpensinstantOpens == 1 && (local variable) ulong delayedOpensdelayedOpens == 4);
// -- 2. the three STM6 seams --------------------------------------------
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\n=== 2. Three Timeline seams the composition must not step on ===");
{
// (a) visible() is already true while warming.
const (local variable) const(sparkles.ui.state.Timeline.Config) warmingCfgwarmingCfg = sparkles.ui.state.Timeline.Config anchored_overlays_tooltip_timing.lifeCfg(int resolvedDelayMs) pure nothrow @nogc @safeThe Timeline config a dwell arms with. fadeInMs is the resolved delay
(0 when the group was warm), and holdUntilDismissed is pinned so a
hover-triggered surface can never self-close — the WCAG 1.4.13 defaults trap
that Timeline.Config.holdMs = 1200 sets for an unwary caller.
lifeCfg((local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg.(field) int anchored_overlays_tooltip_timing.DwellConfig.warmUpMsthe first tooltip in a cold group waits this long
warmUpMs);
auto (local variable) sparkles.ui.state.Timeline ww = (struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline.sparkles.ui.state.Timeline sparkles.ui.state.Timeline.triggered(in sparkles.ui.state.Timeline.Config cfg) pure nothrow @nogc @safeA freshly-triggered effect (restarts if already running).
triggered((local variable) const(sparkles.ui.state.Timeline.Config) warmingCfgwarmingCfg).sparkles.ui.state.Timeline sparkles.ui.state.Timeline.stepped(int dtMs, in sparkles.ui.state.Timeline.Config cfg) const pure nothrow @nogc @safeAdvanced by dtMs milliseconds.
stepped(250, (local variable) const(sparkles.ui.state.Timeline.Config) warmingCfgwarmingCfg);
void std.stdio.writefln!("a) warming at 250/%d ms: life.visible()=%s alpha=%d%% => paint on `phase == hold`, never on visible()", const(int), bool, int)(const(int) __param_0, bool __param_1, int __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln!("a) warming at 250/%d ms: life.visible()=%s alpha=%d%% "
~ "=> paint on `phase == hold`, never on visible()")(
(local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg.(field) int anchored_overlays_tooltip_timing.DwellConfig.warmUpMsthe first tooltip in a cold group waits this long
warmUpMs, (local variable) sparkles.ui.state.Timeline ww.bool sparkles.ui.state.Timeline.visible() const pure nothrow @nogc @safetrue while anything should be painted.
visible, (local variable) sparkles.ui.state.Timeline ww.int sparkles.ui.state.Timeline.alphaPercent(in sparkles.ui.state.Timeline.Config cfg) const pure nothrow @nogc @safeOpacity in percent (fades ramp linearly; hold is 100, idle 0).
alphaPercent((local variable) const(sparkles.ui.state.Timeline.Config) warmingCfgwarmingCfg));
assert((local variable) sparkles.ui.state.Timeline ww.bool sparkles.ui.state.Timeline.visible() const pure nothrow @nogc @safetrue while anything should be painted.
visible && (local variable) sparkles.ui.state.Timeline ww.(field) sparkles.ui.state.Timeline.Phase sparkles.ui.state.Timeline.phasephase == (struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline.(enum) sparkles.ui.state.Timeline.PhaseThe phase of the effect.
Phase.(enum value) sparkles.ui.state.Timeline.Phase.fadeIn = 1appearing
fadeIn);
assert(!(struct) anchored_overlays_tooltip_timing.TooltipDwellOne tooltip's timing state: a composed Timeline plus the anchor it belongs
to. armedDelayMs is stored because the deadline must be absolute —
measured from the arm instant, never recomputed as now + delay — which
Timeline.stepped gives for free as long as the config does not change
under it.
TooltipDwell((local variable) sparkles.ui.state.Timeline ww, 1, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg.(field) int anchored_overlays_tooltip_timing.DwellConfig.warmUpMsthe first tooltip in a cold group waits this long
warmUpMs).bool anchored_overlays_tooltip_timing.TooltipDwell.shown() const pure nothrow @nogc @safePainted. Note this is phase == hold, not life.visible() — see § 2.
shown);
// (b) dismissed() from fadeIn plays a full-opacity fade-out.
const (local variable) const(sparkles.ui.state.Timeline.Config) fadingfading = (struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline.(struct) sparkles.ui.state.Timeline.ConfigPhase durations. holdUntilDismissed is the event-scoped mode: hold
persists until dismissed — a mode, not a magic duration.
Config(fadeInMs: 500, fadeOutMs: 150,
holdUntilDismissed: true);
const (local variable) const(sparkles.ui.state.Timeline) cancelledcancelled = (struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline.sparkles.ui.state.Timeline sparkles.ui.state.Timeline.triggered(in sparkles.ui.state.Timeline.Config cfg) pure nothrow @nogc @safeA freshly-triggered effect (restarts if already running).
triggered((local variable) const(sparkles.ui.state.Timeline.Config) fadingfading).sparkles.ui.state.Timeline sparkles.ui.state.Timeline.stepped(int dtMs, in sparkles.ui.state.Timeline.Config cfg) const pure nothrow @nogc @safeAdvanced by dtMs milliseconds.
stepped(250, (local variable) const(sparkles.ui.state.Timeline.Config) fadingfading)
.sparkles.ui.state.Timeline sparkles.ui.state.Timeline.dismissed(in sparkles.ui.state.Timeline.Config cfg) const pure nothrow @nogc @safeDismissed by an event (the no-frame-clock collapse): holding ends now.
dismissed((local variable) const(sparkles.ui.state.Timeline.Config) fadingfading);
void std.stdio.writefln!("b) dismissed() mid-warm-up: phase=%s alpha=%d%% \xe2\x80\x94 a cancelled warm-up must reset to idle, not dismiss", const(sparkles.ui.state.Timeline.Phase), int)(const(sparkles.ui.state.Timeline.Phase) __param_0, int __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln!("b) dismissed() mid-warm-up: phase=%s alpha=%d%% — a cancelled "
~ "warm-up must reset to idle, not dismiss")(
(local variable) const(sparkles.ui.state.Timeline) cancelledcancelled.(field) sparkles.ui.state.Timeline.Phase sparkles.ui.state.Timeline.phasephase, (local variable) const(sparkles.ui.state.Timeline) cancelledcancelled.int sparkles.ui.state.Timeline.alphaPercent(in sparkles.ui.state.Timeline.Config cfg) const pure nothrow @nogc @safeOpacity in percent (fades ramp linearly; hold is 100, idle 0).
alphaPercent((local variable) const(sparkles.ui.state.Timeline.Config) fadingfading));
assert((local variable) const(sparkles.ui.state.Timeline) cancelledcancelled.(field) sparkles.ui.state.Timeline.Phase sparkles.ui.state.Timeline.phasephase == (struct) sparkles.ui.state.TimelineTransient-effect timing as a mode machine (STM6): idle → fadeIn → hold →
fadeOut → idle, advanced by stepped(dtMs, config) — replacing the four
hand-decremented float timers in the GUI. A backend with no frame clock (the
event-driven TUI) collapses it without changing the caller: configure
holdUntilDismissed and call ``Timeline.dismissed on the next event.
Timeline.(enum) sparkles.ui.state.Timeline.PhaseThe phase of the effect.
Phase.(enum value) sparkles.ui.state.Timeline.Phase.fadeOut = 3disappearing
fadeOut
&& (local variable) const(sparkles.ui.state.Timeline) cancelledcancelled.int sparkles.ui.state.Timeline.alphaPercent(in sparkles.ui.state.Timeline.Config cfg) const pure nothrow @nogc @safeOpacity in percent (fades ramp linearly; hold is 100, idle 0).
alphaPercent((local variable) const(sparkles.ui.state.Timeline.Config) fadingfading) == 100);
// (c) a backend with no frame clock never advances `stepped`.
auto (local variable) anchored_overlays_tooltip_timing.Dwell frozenfrozen = (struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell().anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.entered(in anchored_overlays_tooltip_timing.Dwell s, ulong id, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe pointer came to rest on an anchor.
entered(1, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg);
foreach ((local variable) int __; 0 .. 100)
(local variable) anchored_overlays_tooltip_timing.Dwell frozenfrozen = (local variable) anchored_overlays_tooltip_timing.Dwell frozenfrozen.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.stepped(in anchored_overlays_tooltip_timing.Dwell s, int dtMs, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeAdvance by dtMs. The warm-up elapses inside Timeline; the cool-down is
the one subtraction this machine adds.
stepped(0, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg); // TuiHost.frameSeconds() => 0
void std.stdio.writefln!("c) 100 frames at dt=0 (the TUI's frameSeconds): dwell=%s, shown=%s \xe2\x80\x94 a warm-up needs an injected clock", string, bool)(string __param_0, bool __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln!("c) 100 frames at dt=0 (the TUI's frameSeconds): dwell=%s, "
~ "shown=%s — a warm-up needs an injected clock")(
string anchored_overlays_tooltip_timing.dwellPhase(in anchored_overlays_tooltip_timing.TooltipDwell d) pure nothrow @nogc @safedwellPhase((local variable) anchored_overlays_tooltip_timing.Dwell frozenfrozen.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive), (local variable) anchored_overlays_tooltip_timing.Dwell frozenfrozen.bool anchored_overlays_tooltip_timing.Dwell.visible() const pure nothrow @nogc @safevisible);
assert((local variable) anchored_overlays_tooltip_timing.Dwell frozenfrozen.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.bool anchored_overlays_tooltip_timing.TooltipDwell.warming() const pure nothrow @nogc @safeCounting down to the deadline, and not in the display or hit list.
warming && !(local variable) anchored_overlays_tooltip_timing.Dwell frozenfrozen.bool anchored_overlays_tooltip_timing.Dwell.visible() const pure nothrow @nogc @safevisible);
}
// -- 3. zero disables the feature, structurally --------------------------
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\n=== 3. skipMs == 0 disables warmth; it never arms a 0 ms timer ===");
{
const (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) zerozero = (struct) anchored_overlays_tooltip_timing.DwellConfigThe two durations the whole dimension reduces to. Both belong in Palette
beside popupPadX, exactly as WinUI puts them in the OS and Qt in QStyle;
they are parameters here so the trace can name them.
skipMs == 0 must statically disable warmth rather than arm a zero-length
timer — Radix shipped that bug (#3873: a zero-length timer left every tooltip
instant forever) and fixed it with early returns in both provider callbacks.
``DwellConfig.warmthEnabled is that early return, expressed as a value.
DwellConfig(warmUpMs: 500, skipMs: 0);
auto (local variable) anchored_overlays_tooltip_timing.Dwell zz = (struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell().anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.entered(in anchored_overlays_tooltip_timing.Dwell s, ulong id, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe pointer came to rest on an anchor.
entered(1, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) zerozero).anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.stepped(in anchored_overlays_tooltip_timing.Dwell s, int dtMs, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeAdvance by dtMs. The warm-up elapses inside Timeline; the cool-down is
the one subtraction this machine adds.
stepped(500, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) zerozero); // A shown
assert((local variable) anchored_overlays_tooltip_timing.Dwell zz.bool anchored_overlays_tooltip_timing.Dwell.visible() const pure nothrow @nogc @safevisible);
(local variable) anchored_overlays_tooltip_timing.Dwell zz = (local variable) anchored_overlays_tooltip_timing.Dwell zz.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.left(in anchored_overlays_tooltip_timing.Dwell s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe pointer left the anchor (and the group's region).
left((local variable) const(anchored_overlays_tooltip_timing.DwellConfig) zerozero);
void std.stdio.writefln!("after closing A with skipMs=0: group=%s, warmMsLeft=%d", string, int)(string __param_0, int __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln!"after closing A with skipMs=0: group=%s, warmMsLeft=%d"(
(local variable) anchored_overlays_tooltip_timing.Dwell zz.bool anchored_overlays_tooltip_timing.Dwell.warm() const pure nothrow @nogc @safetrue while a subsequent tooltip would open with no delay.
warm ? "warm" : "cold", (local variable) anchored_overlays_tooltip_timing.Dwell zz.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) int anchored_overlays_tooltip_timing.DwellGroup.warmMsLeft0 ⇒ the next tooltip in the group opens instantly
warmMsLeft);
(local variable) anchored_overlays_tooltip_timing.Dwell zz = (local variable) anchored_overlays_tooltip_timing.Dwell zz.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.entered(in anchored_overlays_tooltip_timing.Dwell s, ulong id, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe pointer came to rest on an anchor.
entered(2, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) zerozero);
void std.stdio.writefln!("entering B: armed delay %d ms (not 0) \xe2\x80\x94 the WPF BetweenShowDelay==0 rule, and the fix for Radix #3873", int)(int __param_0) @safeEquivalent to writef(fmt, args, '\n').
writefln!("entering B: armed delay %d ms (not 0) — the WPF "
~ "BetweenShowDelay==0 rule, and the fix for Radix #3873")(
(local variable) anchored_overlays_tooltip_timing.Dwell zz.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.(field) int anchored_overlays_tooltip_timing.TooltipDwell.armedDelayMsthe delay resolved at arm time
armedDelayMs);
assert(!(local variable) anchored_overlays_tooltip_timing.Dwell zz.bool anchored_overlays_tooltip_timing.Dwell.warm() const pure nothrow @nogc @safetrue while a subsequent tooltip would open with no delay.
warm && (local variable) anchored_overlays_tooltip_timing.Dwell zz.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.(field) int anchored_overlays_tooltip_timing.TooltipDwell.armedDelayMsthe delay resolved at arm time
armedDelayMs == (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) zerozero.(field) int anchored_overlays_tooltip_timing.DwellConfig.warmUpMsthe first tooltip in a cold group waits this long
warmUpMs);
}
// -- 4. the touch collapse ----------------------------------------------
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\n=== 4. The touch collapse (caps.hover == false) ===");
const (local variable) const(sparkles.input.capability.InputCapabilities) touchtouch = (struct) sparkles.input.capability.InputCapabilitiesA target's declared input affordances.
The defaults describe a mouse: the historical assumption, so a producer
that has not thought about this yet keeps today's behavior rather than
silently claiming a capability it lacks in the other direction.
Declare with one of the profiles below where one fits — mousePointer,
touchPointer, cellPointer, staticPointer — so the
vocabulary stays small and two targets that mean the same thing say it the
same way.
InputCapabilities(hover: false, precisePointer: false,
maxPointers: 5);
const (local variable) const(anchored_overlays_tooltip_timing.TriggerPolicy) wantwant = (struct) anchored_overlays_tooltip_timing.TriggerPolicyA declaration, not a behaviour: which triggers the component wants.
TriggerPolicy(hover: true, activate: true, longPress: false);
const (local variable) const(anchored_overlays_tooltip_timing.TriggerPlan) planplan = anchored_overlays_tooltip_timing.TriggerPlan anchored_overlays_tooltip_timing.resolveTriggers(in anchored_overlays_tooltip_timing.TriggerPolicy want, in sparkles.input.capability.InputCapabilities caps) pure nothrow @nogc @safe``caps.hover == false moves hover to substituted and lets activate
(tap-to-pin) carry it — the only substitution expressible on all three live
targets. Long press is not the default substitute: it exceeds the cell
pointer's tier, and on Android it collides with text selection.
resolveTriggers((local variable) const(anchored_overlays_tooltip_timing.TriggerPolicy) wantwant, (local variable) const(sparkles.input.capability.InputCapabilities) touchtouch);
void std.stdio.writefln!("want: hover+activate caps.hover=%s", const(bool))(const(bool) __param_0) @safeEquivalent to writef(fmt, args, '\n').
writefln!"want: hover+activate caps.hover=%s"((local variable) const(sparkles.input.capability.InputCapabilities) touchtouch.(field) bool sparkles.input.capability.InputCapabilities.hoverThe pointer can rest on a target without pressing.
false on touch, and it is the one that changes component behavior most:
everything hover-driven needs a second route, because on a touchscreen the
"pointer position" between taps is simply the last place a finger left.
hover);
void std.stdio.writefln!(" served: hover=%s activate=%s", const(bool), const(bool))(const(bool) __param_0, const(bool) __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln!" served: hover=%s activate=%s"((local variable) const(anchored_overlays_tooltip_timing.TriggerPlan) planplan.(field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.servedserved.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.hoverhover,
(local variable) const(anchored_overlays_tooltip_timing.TriggerPlan) planplan.(field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.servedserved.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.activateactivate);
void std.stdio.writefln!(" substituted: hover=%s -> tap-to-pin over PressState", const(bool))(const(bool) __param_0) @safeEquivalent to writef(fmt, args, '\n').
writefln!" substituted: hover=%s -> tap-to-pin over PressState"(
(local variable) const(anchored_overlays_tooltip_timing.TriggerPlan) planplan.(field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.substitutedsubstituted.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.hoverhover);
void std.stdio.writefln!(" dropped: hover=%s", const(bool))(const(bool) __param_0) @safeEquivalent to writef(fmt, args, '\n').
writefln!" dropped: hover=%s"((local variable) const(anchored_overlays_tooltip_timing.TriggerPlan) planplan.(field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.droppeddropped.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.hoverhover);
assert(!(local variable) const(anchored_overlays_tooltip_timing.TriggerPlan) planplan.(field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.servedserved.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.hoverhover && (local variable) const(anchored_overlays_tooltip_timing.TriggerPlan) planplan.(field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.substitutedsubstituted.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.hoverhover && !(local variable) const(anchored_overlays_tooltip_timing.TriggerPlan) planplan.(field) anchored_overlays_tooltip_timing.TriggerPolicy anchored_overlays_tooltip_timing.TriggerPlan.droppeddropped.(field) bool anchored_overlays_tooltip_timing.TriggerPolicy.hoverhover);
// The same hover script, on a target that emits no hover events at all.
{
(struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell (local variable) anchored_overlays_tooltip_timing.Dwell hh;
int (local variable) int delivereddelivered, (local variable) int shownStepsshownSteps;
foreach ((parameter) immutable(anchored_overlays_tooltip_timing.Step) stst; (immutable global) immutable(anchored_overlays_tooltip_timing.Step[]) anchored_overlays_tooltip_timing.hoverScriptThe hover script: A warms up and shows, B rides the warm group instantly,
the cool-down expires before C, a press clears warmth, and an abandoned
warm-up leaves the group cold.
hoverScript)
{
(local variable) anchored_overlays_tooltip_timing.Dwell hh = (local variable) anchored_overlays_tooltip_timing.Dwell hh.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.stepped(in anchored_overlays_tooltip_timing.Dwell s, int dtMs, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeAdvance by dtMs. The warm-up elapses inside Timeline; the cool-down is
the one subtraction this machine adds.
stepped((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) int anchored_overlays_tooltip_timing.Step.dtMsdtMs, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg);
if ((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) anchored_overlays_tooltip_timing.Ev anchored_overlays_tooltip_timing.Step.evev == (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.enter = 1the pointer rests on an anchor
enter || (local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) anchored_overlays_tooltip_timing.Ev anchored_overlays_tooltip_timing.Step.evev == (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.leave = 2the pointer leaves the group
leave)
continue; // the target produces neither
++(local variable) int delivereddelivered;
if ((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) anchored_overlays_tooltip_timing.Ev anchored_overlays_tooltip_timing.Step.evev == (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.press = 4a press elsewhere; also stands in for key/scroll
press)
(local variable) anchored_overlays_tooltip_timing.Dwell hh = (local variable) anchored_overlays_tooltip_timing.Dwell hh.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.interrupted(in anchored_overlays_tooltip_timing.Dwell s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeA press, a key, or a scroll: closes the surface and clears warmth.
Nothing in React Aria or Radix clears warmth except its own cool-down, so a
click or a scroll leaves the next tooltip in instant mode; Ariakit patched
exactly this symptom on onBlur. Warmth is a property of an uninterrupted
hover context, so the interruption ends it.
interrupted((local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg);
if ((local variable) anchored_overlays_tooltip_timing.Dwell hh.bool anchored_overlays_tooltip_timing.Dwell.visible() const pure nothrow @nogc @safevisible)
++(local variable) int shownStepsshownSteps;
}
void std.stdio.writefln!("\nreplaying the hover script here: %d of %d steps deliver an event, tooltip shown on %d \xe2\x80\x94 hover is unreachable, not slow", int, ulong, int)(int __param_0, ulong __param_1, int __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln!("\nreplaying the hover script here: %d of %d steps deliver an "
~ "event, tooltip shown on %d — hover is unreachable, not slow")(
(local variable) int delivereddelivered, (immutable global) immutable(anchored_overlays_tooltip_timing.Step[]) anchored_overlays_tooltip_timing.hoverScriptThe hover script: A warms up and shows, B rides the warm group instantly,
the cool-down expires before C, a press clears warmth, and an abandoned
warm-up leaves the group cold.
hoverScript.(field) ulong immutable(anchored_overlays_tooltip_timing.Step[]).lengthlength, (local variable) int shownStepsshownSteps);
assert((local variable) int shownStepsshownSteps == 0);
}
// The substituted trigger, driven by the same machine with openMs == 0.
// `warming` and the cool-down are unreachable *by configuration*, not by a
// runtime branch — a zero warm-up means `Timeline.triggered` lands in `hold`.
const (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) touchCfgtouchCfg = (struct) anchored_overlays_tooltip_timing.DwellConfigThe two durations the whole dimension reduces to. Both belong in Palette
beside popupPadX, exactly as WinUI puts them in the OS and Qt in QStyle;
they are parameters here so the trace can name them.
skipMs == 0 must statically disable warmth rather than arm a zero-length
timer — Radix shipped that bug (#3873: a zero-length timer left every tooltip
instant forever) and fixed it with early returns in both provider callbacks.
``DwellConfig.warmthEnabled is that early return, expressed as a value.
DwellConfig(warmUpMs: 0, skipMs: 0);
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\ntap-to-pin, warm-up statically 0, holdUntilDismissed pinned:");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" t event dwell group shown why");
{
(struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell (local variable) anchored_overlays_tooltip_timing.Dwell pp;
int (local variable) int tttt;
foreach ((parameter) immutable(anchored_overlays_tooltip_timing.Step) stst; (immutable global) immutable(anchored_overlays_tooltip_timing.Step[]) anchored_overlays_tooltip_timing.tapScriptThe touch script: the substituted trigger, on the same three anchors.
tapScript)
{
(local variable) int tttt += (local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) int anchored_overlays_tooltip_timing.Step.dtMsdtMs;
(local variable) anchored_overlays_tooltip_timing.Dwell pp = (local variable) anchored_overlays_tooltip_timing.Dwell pp.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.stepped(in anchored_overlays_tooltip_timing.Dwell s, int dtMs, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeAdvance by dtMs. The warm-up elapses inside Timeline; the cool-down is
the one subtraction this machine adds.
stepped((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) int anchored_overlays_tooltip_timing.Step.dtMsdtMs, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) touchCfgtouchCfg);
if ((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) anchored_overlays_tooltip_timing.Ev anchored_overlays_tooltip_timing.Step.evev == (enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev.(enum value) anchored_overlays_tooltip_timing.Ev.tap = 3press+release on an anchor (or on 0 == outside)
tap)
(local variable) anchored_overlays_tooltip_timing.Dwell pp = (local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) ulong anchored_overlays_tooltip_timing.Step.anchoranchor == 0 || (local variable) anchored_overlays_tooltip_timing.Dwell pp.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) ulong anchored_overlays_tooltip_timing.DwellGroup.openIdthe anchor currently showing, 0 == none
openId == (local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) ulong anchored_overlays_tooltip_timing.Step.anchoranchor
? (local variable) anchored_overlays_tooltip_timing.Dwell pp.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.interrupted(in anchored_overlays_tooltip_timing.Dwell s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeA press, a key, or a scroll: closes the surface and clears warmth.
Nothing in React Aria or Radix clears warmth except its own cool-down, so a
click or a scroll leaves the next tooltip in instant mode; Ariakit patched
exactly this symptom on onBlur. Warmth is a property of an uninterrupted
hover context, so the interruption ends it.
interrupted((local variable) const(anchored_overlays_tooltip_timing.DwellConfig) touchCfgtouchCfg) // outside / reactivate
: (local variable) anchored_overlays_tooltip_timing.Dwell pp.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.entered(in anchored_overlays_tooltip_timing.Dwell s, ulong id, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe pointer came to rest on an anchor.
entered((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) ulong anchored_overlays_tooltip_timing.Step.anchoranchor, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) touchCfgtouchCfg); // pin
assert(!(local variable) anchored_overlays_tooltip_timing.Dwell pp.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive.bool anchored_overlays_tooltip_timing.TooltipDwell.warming() const pure nothrow @nogc @safeCounting down to the deadline, and not in the display or hit list.
warming, "warming is unreachable on touch");
assert((local variable) anchored_overlays_tooltip_timing.Dwell pp.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup.(field) int anchored_overlays_tooltip_timing.DwellGroup.warmMsLeft0 ⇒ the next tooltip in the group opens instantly
warmMsLeft == 0, "the cool-down is unreachable too");
(struct) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false)A @nogc container with Small Buffer Optimization and copy-on-write.
Elements are stored inline up to N elements, then automatically
allocated on the heap (via AffixAllocator!(Mallocator, ControlBlock), which
keeps the reference count in an allocation prefix; the element capacity is the
heap slice length) when capacity is exceeded. Heap blocks are managed with the
std.experimental.allocator makeArray/expandArray/dispose helpers.
The buffer is copyable. Copying an inline buffer duplicates its elements
(independent copies). Copying a heap buffer shares the allocation and bumps a
reference count; the shared block is cloned copy-on-write the first time a
mutable copy is written. This suits the common pattern of one producer
building a buffer mutably, then handing out many const reader copies — read
via const (e.g. through borrow) never clones. Mutating accessors on a
shared mutable copy clone first, so a mutable slice/reference taken from a
shared buffer and held across a later mutation may be invalidated (the usual
copy-on-write caveat) — read through const to share without that risk.
Note
storage location is tied to length (data is inline whenever
length <= N), so reserve pre-grows only once on the heap,
and clear/popBack that drop the length back to <= N revert
to inline storage.
SmallBuffer!(char, 32) (local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) evev, (local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) grpgrp;
void anchored_overlays_tooltip_timing.writeEvent!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false))(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) w, in anchored_overlays_tooltip_timing.Step s) pure nothrow @nogc @safeenter A, tick, tap outside … — built through an output range, so the
label costs no allocation even though main only prints it.
writeEvent((local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) evev, (local variable) immutable(anchored_overlays_tooltip_timing.Step) stst);
void anchored_overlays_tooltip_timing.writeGroup!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false))(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) w, in anchored_overlays_tooltip_timing.DwellGroup g) pure nothrow @nogc @safecold / warm 180.
writeGroup((local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) grpgrp, (local variable) anchored_overlays_tooltip_timing.Dwell pp.(field) anchored_overlays_tooltip_timing.DwellGroup anchored_overlays_tooltip_timing.Dwell.groupgroup);
void std.stdio.writefln!("%5d %-11s %-8s %-9s %-5s %s", int, char[], string, char[], string, string)(int __param_0, char[] __param_1, string __param_2, char[] __param_3, string __param_4, string __param_5) @safeEquivalent to writef(fmt, args, '\n').
writefln!"%5d %-11s %-8s %-9s %-5s %s"(
(local variable) int tttt, (local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) evev[], string anchored_overlays_tooltip_timing.dwellPhase(in anchored_overlays_tooltip_timing.TooltipDwell d) pure nothrow @nogc @safedwellPhase((local variable) anchored_overlays_tooltip_timing.Dwell pp.(field) anchored_overlays_tooltip_timing.TooltipDwell anchored_overlays_tooltip_timing.Dwell.activeactive), (local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) grpgrp[],
(local variable) anchored_overlays_tooltip_timing.Dwell pp.bool anchored_overlays_tooltip_timing.Dwell.visible() const pure nothrow @nogc @safevisible ? string anchored_overlays_tooltip_timing.anchorLabel(ulong id) pure nothrow @nogc @safeanchorLabel((local variable) anchored_overlays_tooltip_timing.Dwell pp.ulong anchored_overlays_tooltip_timing.Dwell.visibleId() const pure nothrow @nogc @safevisibleId) : "-", (local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) string anchored_overlays_tooltip_timing.Step.whywhat the step is meant to prove
why);
}
assert(!(local variable) anchored_overlays_tooltip_timing.Dwell pp.bool anchored_overlays_tooltip_timing.Dwell.visible() const pure nothrow @nogc @safevisible);
}
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("unavailable rather than degraded: dwell intent (there is no rest "
~ "without hover),");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("the warm group, the cool-down. The substitution must be PUBLISHED "
~ "— as a readable");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("resolution value and an accessible description — or it is a silent "
~ "product hole.");
// -- 5. the tier-0 collapse ---------------------------------------------
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\n=== 5. The tier-0 collapse (static HTML: no script, no timers) ===");
{
(struct) sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false)A @nogc container with Small Buffer Optimization and copy-on-write.
Elements are stored inline up to N elements, then automatically
allocated on the heap (via AffixAllocator!(Mallocator, ControlBlock), which
keeps the reference count in an allocation prefix; the element capacity is the
heap slice length) when capacity is exceeded. Heap blocks are managed with the
std.experimental.allocator makeArray/expandArray/dispose helpers.
The buffer is copyable. Copying an inline buffer duplicates its elements
(independent copies). Copying a heap buffer shares the allocation and bumps a
reference count; the shared block is cloned copy-on-write the first time a
mutable copy is written. This suits the common pattern of one producer
building a buffer mutably, then handing out many const reader copies — read
via const (e.g. through borrow) never clones. Mutating accessors on a
shared mutable copy clone first, so a mutable slice/reference taken from a
shared buffer and held across a later mutation may be invalidated (the usual
copy-on-write caveat) — read through const to share without that risk.
Note
storage location is tied to length (data is inline whenever
length <= N), so reserve pre-grows only once on the heap,
and clear/popBack that drop the length back to <= N revert
to inline storage.
SmallBuffer!(char, 512) (local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) csscss;
void anchored_overlays_tooltip_timing.writeTier0Css!(sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false))(ref sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) w, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe rule pair a DwellConfig compiles to. It needs one change to
interp/html_semantic.d:57-58: display is not transitionable, so the
tier-0 reveal must switch to visibility/opacity.
writeTier0Css((local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) csscss, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg);
void std.stdio.writeln!(char[])(char[] __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln((local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 512LU, false) csscss[]);
}
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("The same hover script, evaluated by those two rules alone:");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" t event machine tier-0 divergence");
{
(struct) anchored_overlays_tooltip_timing.DwellA group and its at-most-one live tooltip. One record suffices because
opening closes every peer (React Aria's closeOthers), so a registry of
per-instance timers — which is not expressible in @safe pure value code
anyway — has nothing left to hold.
Dwell (local variable) anchored_overlays_tooltip_timing.Dwell mm;
(struct) anchored_overlays_tooltip_timing.CssHoverStatic HTML has no cross-element state, so this is the entire machine: how
long one element has matched :hover. There is no group, no arbiter, and
nothing to interrupt.
CssHover (local variable) anchored_overlays_tooltip_timing.CssHover hh;
int (local variable) int ctct, (local variable) int divergeddiverged;
foreach ((parameter) immutable(anchored_overlays_tooltip_timing.Step) stst; (immutable global) immutable(anchored_overlays_tooltip_timing.Step[]) anchored_overlays_tooltip_timing.hoverScriptThe hover script: A warms up and shows, B rides the warm group instantly,
the cool-down expires before C, a press clears warmth, and an abandoned
warm-up leaves the group cold.
hoverScript)
{
(local variable) int ctct += (local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) int anchored_overlays_tooltip_timing.Step.dtMsdtMs;
(local variable) anchored_overlays_tooltip_timing.Dwell mm = (local variable) anchored_overlays_tooltip_timing.Dwell mm.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.stepped(in anchored_overlays_tooltip_timing.Dwell s, int dtMs, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeAdvance by dtMs. The warm-up elapses inside Timeline; the cool-down is
the one subtraction this machine adds.
stepped((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) int anchored_overlays_tooltip_timing.Step.dtMsdtMs, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg);
(local variable) anchored_overlays_tooltip_timing.CssHover hh = (local variable) anchored_overlays_tooltip_timing.CssHover hh.anchored_overlays_tooltip_timing.CssHover anchored_overlays_tooltip_timing.cssStepped(in anchored_overlays_tooltip_timing.CssHover s, int dtMs) pure nothrow @nogc @safeLeaving reverts the property, so mid-delay cancellation is free — the one
piece of the machine tier-0 gets right for nothing.
cssStepped((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) int anchored_overlays_tooltip_timing.Step.dtMsdtMs);
final switch ((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) anchored_overlays_tooltip_timing.Ev anchored_overlays_tooltip_timing.Step.evev) with ((enum) anchored_overlays_tooltip_timing.EvWhat a target can deliver. enter/leave exist only where caps.hover.
Ev)
{
case (enum value) anchored_overlays_tooltip_timing.Ev.tick = cast(ubyte)0utime passes, nothing happens
tick: break;
case (enum value) anchored_overlays_tooltip_timing.Ev.enter = 1the pointer rests on an anchor
enter:
(local variable) anchored_overlays_tooltip_timing.Dwell mm = (local variable) anchored_overlays_tooltip_timing.Dwell mm.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.entered(in anchored_overlays_tooltip_timing.Dwell s, ulong id, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe pointer came to rest on an anchor.
entered((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) ulong anchored_overlays_tooltip_timing.Step.anchoranchor, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg);
(local variable) anchored_overlays_tooltip_timing.CssHover hh = anchored_overlays_tooltip_timing.CssHover anchored_overlays_tooltip_timing.cssEntered(ulong id) pure nothrow @nogc @safecssEntered((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) ulong anchored_overlays_tooltip_timing.Step.anchoranchor);
break;
case (enum value) anchored_overlays_tooltip_timing.Ev.leave = 2the pointer leaves the group
leave:
(local variable) anchored_overlays_tooltip_timing.Dwell mm = (local variable) anchored_overlays_tooltip_timing.Dwell mm.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.left(in anchored_overlays_tooltip_timing.Dwell s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe pointer left the anchor (and the group's region).
left((local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg);
(local variable) anchored_overlays_tooltip_timing.CssHover hh = anchored_overlays_tooltip_timing.CssHover anchored_overlays_tooltip_timing.cssLeft() pure nothrow @nogc @safecssLeft();
break;
case (enum value) anchored_overlays_tooltip_timing.Ev.tap = 3press+release on an anchor (or on 0 == outside)
tap:
(local variable) anchored_overlays_tooltip_timing.Dwell mm = (local variable) anchored_overlays_tooltip_timing.Dwell mm.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.entered(in anchored_overlays_tooltip_timing.Dwell s, ulong id, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeThe pointer came to rest on an anchor.
entered((local variable) immutable(anchored_overlays_tooltip_timing.Step) stst.(field) ulong anchored_overlays_tooltip_timing.Step.anchoranchor, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg);
break;
case (enum value) anchored_overlays_tooltip_timing.Ev.press = 4a press elsewhere; also stands in for key/scroll
press:
(local variable) anchored_overlays_tooltip_timing.Dwell mm = (local variable) anchored_overlays_tooltip_timing.Dwell mm.anchored_overlays_tooltip_timing.Dwell anchored_overlays_tooltip_timing.interrupted(in anchored_overlays_tooltip_timing.Dwell s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safeA press, a key, or a scroll: closes the surface and clears warmth.
Nothing in React Aria or Radix clears warmth except its own cool-down, so a
click or a scroll leaves the next tooltip in instant mode; Ariakit patched
exactly this symptom on onBlur. Warmth is a property of an uninterrupted
hover context, so the interruption ends it.
interrupted((local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg);
break; // no press channel at tier 0
}
const (local variable) const(string) mvmv = (local variable) anchored_overlays_tooltip_timing.Dwell mm.bool anchored_overlays_tooltip_timing.Dwell.visible() const pure nothrow @nogc @safevisible ? string anchored_overlays_tooltip_timing.anchorLabel(ulong id) pure nothrow @nogc @safeanchorLabel((local variable) anchored_overlays_tooltip_timing.Dwell mm.ulong anchored_overlays_tooltip_timing.Dwell.visibleId() const pure nothrow @nogc @safevisibleId) : "-";
const (local variable) const(string) hvhv = bool anchored_overlays_tooltip_timing.cssVisible(in anchored_overlays_tooltip_timing.CssHover s, in anchored_overlays_tooltip_timing.DwellConfig c) pure nothrow @nogc @safecssVisible((local variable) anchored_overlays_tooltip_timing.CssHover hh, (local variable) const(anchored_overlays_tooltip_timing.DwellConfig) cfgcfg) ? string anchored_overlays_tooltip_timing.anchorLabel(ulong id) pure nothrow @nogc @safeanchorLabel((local variable) anchored_overlays_tooltip_timing.CssHover hh.(field) ulong anchored_overlays_tooltip_timing.CssHover.hoveredIdhoveredId) : "-";
if ((local variable) const(string) mvmv != (local variable) const(string) hvhv)
++(local variable) int divergeddiverged;
(struct) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false)A @nogc container with Small Buffer Optimization and copy-on-write.
Elements are stored inline up to N elements, then automatically
allocated on the heap (via AffixAllocator!(Mallocator, ControlBlock), which
keeps the reference count in an allocation prefix; the element capacity is the
heap slice length) when capacity is exceeded. Heap blocks are managed with the
std.experimental.allocator makeArray/expandArray/dispose helpers.
The buffer is copyable. Copying an inline buffer duplicates its elements
(independent copies). Copying a heap buffer shares the allocation and bumps a
reference count; the shared block is cloned copy-on-write the first time a
mutable copy is written. This suits the common pattern of one producer
building a buffer mutably, then handing out many const reader copies — read
via const (e.g. through borrow) never clones. Mutating accessors on a
shared mutable copy clone first, so a mutable slice/reference taken from a
shared buffer and held across a later mutation may be invalidated (the usual
copy-on-write caveat) — read through const to share without that risk.
Note
storage location is tied to length (data is inline whenever
length <= N), so reserve pre-grows only once on the heap,
and clear/popBack that drop the length back to <= N revert
to inline storage.
SmallBuffer!(char, 32) (local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) evev;
void anchored_overlays_tooltip_timing.writeEvent!(sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false))(ref sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) w, in anchored_overlays_tooltip_timing.Step s) pure nothrow @nogc @safeenter A, tick, tap outside … — built through an output range, so the
label costs no allocation even though main only prints it.
writeEvent((local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) evev, (local variable) immutable(anchored_overlays_tooltip_timing.Step) stst);
void std.stdio.writefln!("%5d %-9s %-9s %s", int, char[], string, string)(int __param_0, char[] __param_1, string __param_2, string __param_3) @safeEquivalent to writef(fmt, args, '\n').
writefln!"%5d %-9s %-9s %s"((local variable) int ctct, (local variable) sparkles.base.smallbuffer.SmallBuffer!(char, 32LU, false) evev[], (local variable) const(string) mvmv,
(local variable) const(string) mvmv == (local variable) const(string) hvhv ? (local variable) const(string) hvhv : (local variable) const(string) hvhv ~ " <-- differs");
}
void std.stdio.writefln!("\n%d of %d steps differ.", int, ulong)(int __param_0, ulong __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln!"\n%d of %d steps differ."((local variable) int divergeddiverged, (immutable global) immutable(anchored_overlays_tooltip_timing.Step[]) anchored_overlays_tooltip_timing.hoverScriptThe hover script: A warms up and shows, B rides the warm group instantly,
the cool-down expires before C, a press clears warmth, and an abandoned
warm-up leaves the group cold.
hoverScript.(field) ulong immutable(anchored_overlays_tooltip_timing.Step[]).lengthlength);
assert((local variable) int divergeddiverged > 0);
}
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\nWhat tier 0 keeps, and what is simply not there:");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" warm-up delay -> transition-delay on the :hover rule");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" mid-delay cancellation -> free; the property reverts on unhover");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" hold until dismissed -> :hover holds as long as the pointer "
~ "stays");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" keyboard trigger -> the :focus-visible half of the same "
~ "rule");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" ABSENT: warm group / instant swap (no cross-element state)");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" ABSENT: cool-down, and the whole arbiter with it");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" ABSENT: rest (the id-stability gate needs a machine)");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" ABSENT: press / key / scroll clearing warmth (no channel)");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" ABSENT: the singleton — two nested .spk-hit elements both match "
~ ":hover");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("These are honestly absent, not approximated, and are reported "
~ "under TGT5.");
}