Files

93 lines
4.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Window curvature
`dashboard.windowCurvature.*`, module `window-curvature` ([options](../README.md#options)).
A [SteamVR dashboard patch](ui-patches.md#steamvr-dashboard-patches).
## Problem
SteamVR dashboard windows are either curved (fixed radius) or flat, and
world windows start flat.
## What you get
The "Toggle Curvature" row of a window's three-dot menu becomes a control
showing the window's value; the same control in the bottom bar (see
[window control bar](window-control-bar.md)) works without the value, with
haptic steps.
- **click:** curved → flat, flat → stock (1);
- **drag up/down** with the laser: curvature from 0 (flat) to `max`,
relative to stock (2 = half the radius), rounded to `step`, with a detent
of `detentPixels` of drag at each of `detentPoints` (no values skipped),
and haptics (`haptics`) for detents, edges and steps. Drag distance:
`dragPixelsPerUnit` in the menu, `barDragPixelsPerUnit` on the bar button,
after `dragThresholdPixels`.
A window without its own value is shown at `initial` once curved in the
world or on a hand, at 1 in the dashboard or theater. Values are kept per
window until SteamVR restarts.
## Configuration
```nix
steamFrame.dashboard.windowCurvature = {
enable = true;
# initial = 1.0; max = 3.0; step = 0.05;
# detentPoints = [ 0 1.0 ]; detentPixels = 24;
# dragPixelsPerUnit = 60; # full range in one drag (see below)
};
```
## Limitations
- Laser only; with gamepad navigation the row is the stock toggle.
- No thumbstick scrolling (SteamVR sends no wheel events to the menu).
- The laser stops at the menu's edge (~190 px above the row): with the
default 120 px per 1.0, 0 → 1 fits into one drag, 0 → 3 takes two. Lower
`dragPixelsPerUnit` (≤ 60) for the full range in one drag.
## How it works
Stock curvature: the frame renders a transform `frame:<id>:curvature-origin`
at `z = DashboardStore.curvatureDistance` when curved, else 1000; its panels
reference it as `curvature-origin-id` and vrcompositor bends them onto a
cylinder around it (curvature = 1/radius). Those MobX properties are
non-configurable, so the patch hooks the mailbox `SendMessage`
([shared hooks](ui-patches.md#shared-method-hooks), shared with
[dashboard windows](dashboard-windows.md)) and rewrites the origin's z in
outgoing scene graphs to stock / value. On/off stays the stock toggle state,
so stock toggle and wheel always agree.
Values are kept per window (key: first overlay key) in
`window.__sfuiWindowCurvatureState`, across re-patch and unpatch, not across
a dashboard reload. The bar button uses the coordinates SteamVR keeps
sending past the pressed panel's edge while the trigger is held; the bar
panel is never resized.
Debugging: `window.__sfuiWindowCurvature.dump()` (`.log` recent events).
### Contract for other patches
For patches handling presses on the curvature controls (e.g. the window
control bar's long press); the curvature patch owns press-and-drag on them:
- Every element the patch drives has class `sfui-curv-ctl`.
- When a press becomes a drag (`dragThresholdPixels` vertical from where the
press started), a bubbling `CustomEvent` `sfui-curv-dragstart` (detail
`{ frameID, where: 'menu' | 'bar' }`) is dispatched on the element, and
`sfui-curv-dragend` when that drag ends (release or `cancelPress()`).
- `window.__sfuiWindowCurvature.scalePressDragThreshold(factor)` sets the
current press's drag threshold to `factor` × `dragThresholdPixels`, still
measured from the press start; returns whether it applied (a press that
is not yet a drag). The next press starts at 1×.
- `window.__sfuiWindowCurvature.cancelPress()` ends the current press
without its click (a drag keeps its value); returns whether a press was
active.
A patch with its own gesture lets `mousemove` through while undecided,
drops its gesture on `sfui-curv-dragstart`, may raise the threshold while
its gesture is under way, and calls `cancelPress()` when it takes the press
over. Neither side reads the other's thresholds or restores the other's
state.