mirror of
https://github.com/lhns/steam-frame-nix.git
synced 2026-10-06 06:00:22 +02:00
Dashboard options: windows.*, detent names, positive number types
- dashboard.windowMaxScale / windowDistance -> dashboard.windows.maxScale / distance - windowCurvature: default -> initial, snapPixels/snapPoints -> detentPixels/detentPoints, dragThreshold -> dragThresholdPixels (also the patch's option keys; window-curvature VERSION 19); removed `snap` now points to detentPixels - types.numbers.positive/nonnegative instead of number + range assertions (step <= max, initial/detentPoints <= max and distance min <= max stay) - frame-controls patch accepts longPressMs from 300, like the option (VERSION 2) - "SteamVR dashboard patch" in descriptions, which fit into 80 columns Old names keep working with a rename warning.
This commit is contained in:
1 parent
a0c3e0846f
commit
71c78bd733
8 files changed
+121
-100
No files matched your search
@@ -1,10 +1,10 @@
|
||||
# Resize and grab-distance limits of SteamVR dashboard windows. Dashboard patch
|
||||
# (dashboard-windows/patch.js; "systemui" on 127.0.0.1:8087), registered only
|
||||
# when an option is set; enables steamvr-debugger.nix (one SteamVR restart the
|
||||
# first time). Unsetting all options reverts to stock.
|
||||
# Resize and grab-distance limits of SteamVR dashboard windows. SteamVR
|
||||
# dashboard patch (dashboard-windows/patch.js; "systemui" on 127.0.0.1:8087),
|
||||
# registered only when an option is set; enables steamvr-debugger.nix (one
|
||||
# SteamVR restart the first time). Unsetting all options reverts to stock.
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.steamFrame.dashboard;
|
||||
cfg = config.steamFrame.dashboard.windows;
|
||||
inherit (lib) mkOption types;
|
||||
inherit (import ./lib { inherit pkgs; }) mkPatch;
|
||||
|
||||
@@ -17,7 +17,7 @@ let
|
||||
|
||||
rangeOption = kind: what: {
|
||||
min = mkOption {
|
||||
type = types.nullOr types.number;
|
||||
type = types.nullOr types.numbers.positive;
|
||||
default = null;
|
||||
description = ''
|
||||
Closest distance (m) ${what} can be pulled in to while grabbed; null =
|
||||
@@ -25,7 +25,7 @@ let
|
||||
'';
|
||||
};
|
||||
max = mkOption {
|
||||
type = types.nullOr types.number;
|
||||
type = types.nullOr types.numbers.positive;
|
||||
default = null;
|
||||
example = stockDistance.${kind}.max * 2;
|
||||
description = ''
|
||||
@@ -36,43 +36,49 @@ let
|
||||
};
|
||||
|
||||
effective = kind: {
|
||||
min = if cfg.windowDistance.${kind}.min != null then cfg.windowDistance.${kind}.min else stockDistance.${kind}.min;
|
||||
max = if cfg.windowDistance.${kind}.max != null then cfg.windowDistance.${kind}.max else stockDistance.${kind}.max;
|
||||
min = if cfg.distance.${kind}.min != null then cfg.distance.${kind}.min else stockDistance.${kind}.min;
|
||||
max = if cfg.distance.${kind}.max != null then cfg.distance.${kind}.max else stockDistance.${kind}.max;
|
||||
};
|
||||
|
||||
distanceSet = lib.any (r: r.min != null || r.max != null) (lib.attrValues cfg.windowDistance);
|
||||
enabled = cfg.windowMaxScale != null || distanceSet;
|
||||
in {
|
||||
imports = [ ./steam-ui-patches.nix ./steamvr-debugger.nix ];
|
||||
distanceSet = lib.any (r: r.min != null || r.max != null) (lib.attrValues cfg.distance);
|
||||
enabled = cfg.maxScale != null || distanceSet;
|
||||
|
||||
options.steamFrame.dashboard = {
|
||||
windowMaxScale = mkOption {
|
||||
type = types.nullOr types.number;
|
||||
old = [ "steamFrame" "dashboard" ];
|
||||
new = [ "steamFrame" "dashboard" "windows" ];
|
||||
in {
|
||||
imports = [
|
||||
./steam-ui-patches.nix ./steamvr-debugger.nix
|
||||
(lib.mkRenamedOptionModule (old ++ [ "windowMaxScale" ]) (new ++ [ "maxScale" ]))
|
||||
] ++ lib.concatMap (kind: map (bound:
|
||||
lib.mkRenamedOptionModule
|
||||
(old ++ [ "windowDistance" kind bound ]) (new ++ [ "distance" kind bound ]))
|
||||
[ "min" "max" ]) (lib.attrNames stockDistance);
|
||||
|
||||
options.steamFrame.dashboard.windows = {
|
||||
maxScale = mkOption {
|
||||
type = types.nullOr types.numbers.positive;
|
||||
default = null;
|
||||
example = 4.0;
|
||||
description = ''
|
||||
Largest resize factor of SteamVR dashboard windows, relative to their
|
||||
default size; null = stock (2). The theater screen starts 2.8x larger,
|
||||
so its limit is 2.8x this. Dashboard patch, applied immediately.
|
||||
so its limit is 2.8x this. SteamVR dashboard patch, applied
|
||||
immediately.
|
||||
'';
|
||||
};
|
||||
windowDistance = {
|
||||
world = rangeOption "world" "windows placed in the world (not attached to the dashboard)";
|
||||
distance = {
|
||||
world = rangeOption "world"
|
||||
"windows placed in the world (not attached to the dashboard)";
|
||||
theater = rangeOption "theater" "the theater-mode screen";
|
||||
dashboard = rangeOption "dashboard" "the dashboard itself";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf enabled {
|
||||
assertions =
|
||||
[ {
|
||||
assertion = cfg.windowMaxScale == null || cfg.windowMaxScale > 0;
|
||||
message = "steamFrame.dashboard.windowMaxScale must be positive.";
|
||||
} ]
|
||||
++ map (kind: let r = effective kind; in {
|
||||
assertion = r.min > 0 && r.max >= r.min;
|
||||
message = "steamFrame.dashboard.windowDistance.${kind}: need 0 < min <= max (stock values fill in unset ones), got ${toString r.min}-${toString r.max}.";
|
||||
}) (lib.attrNames stockDistance);
|
||||
assertions = map (kind: let r = effective kind; in {
|
||||
assertion = r.max >= r.min;
|
||||
message = "steamFrame.dashboard.windows.distance.${kind}: need min <= max (stock values fill in unset ones), got ${toString r.min}-${toString r.max}.";
|
||||
}) (lib.attrNames stockDistance);
|
||||
|
||||
steamFrame.uiPatches.patches = [ {
|
||||
name = "dashboard-windows";
|
||||
@@ -81,10 +87,7 @@ in {
|
||||
patch = mkPatch {
|
||||
name = "dashboard-windows";
|
||||
src = ./dashboard-windows/patch.js;
|
||||
opts = {
|
||||
maxScale = cfg.windowMaxScale;
|
||||
distance = cfg.windowDistance;
|
||||
};
|
||||
opts = { inherit (cfg) maxScale distance; };
|
||||
};
|
||||
unpatch = ./dashboard-windows/unpatch.js;
|
||||
} ];
|
||||
|
||||
+2
-1
@@ -34,7 +34,8 @@ let
|
||||
in {
|
||||
options.steamFrame.firefox = {
|
||||
enable = lib.mkEnableOption ''
|
||||
the Firefox Flatpak (org.mozilla.firefox) launcher with Steam Frame fixes'';
|
||||
the Firefox Flatpak (org.mozilla.firefox) launcher with Steam Frame
|
||||
fixes'';
|
||||
vrFullscreenFix = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Move window controls between a dashboard window's bottom bar and its
|
||||
# three-dot menu (long press). Dashboard patch (frame-controls/patch.js;
|
||||
# "systemui" on 127.0.0.1:8087), registered only when enabled; enables
|
||||
# steamvr-debugger.nix (one SteamVR restart the first time).
|
||||
# three-dot menu (long press). SteamVR dashboard patch
|
||||
# (frame-controls/patch.js; "systemui" on 127.0.0.1:8087), registered only
|
||||
# when enabled; enables steamvr-debugger.nix (one SteamVR restart the first
|
||||
# time).
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.steamFrame.dashboard.frameControls;
|
||||
@@ -25,7 +26,7 @@ in {
|
||||
moving SteamVR dashboard window controls between the bottom bar and the
|
||||
three-dot menu: long press an icon or menu row for a "Show in bar"
|
||||
popup. Applies to that control in all windows, kept across SteamVR
|
||||
restarts. Dashboard patch; off restores stock (next switch)'';
|
||||
restarts. SteamVR dashboard patch; off restores stock (next switch)'';
|
||||
longPressMs = mkOption {
|
||||
type = types.ints.between 300 10000;
|
||||
default = 1500;
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
// setPlacement(name or "icon:N", 'bar' | 'menu' | null), reset().
|
||||
((find, sigs, opts) => {
|
||||
const NAME = 'frame-controls';
|
||||
const VERSION = 1;
|
||||
const VERSION = 2;
|
||||
const T_SPACER = 1, T_ACTION = 2;
|
||||
const LS_KEY = 'sfui.frameControls.v1';
|
||||
// Action icon enums (sigs.controls anchors them): names for the options.
|
||||
@@ -70,7 +70,7 @@
|
||||
const TOOLTIP_OFFSET = '0 0.15 0.06'; // stock tooltip / menu offset (y 0.15, z 0.05), in front
|
||||
|
||||
const o = { inBar: [], inMenu: [], longPressMs: 1500, floatInTheater: false, ...opts };
|
||||
if (!(o.longPressMs >= 200)) return `invalid options ${JSON.stringify(opts)}`;
|
||||
if (!(o.longPressMs >= 300)) return `invalid options ${JSON.stringify(opts)}`;
|
||||
const toKey = (x) => (typeof x === 'number' ? `icon:${x}` : NAMES[x] != null ? `icon:${NAMES[x]}` : /^icon:\d+$/.test(x) ? x : null);
|
||||
const optMap = {}; // key -> 'bar' | 'menu' from the options
|
||||
for (const [list, where] of [[o.inBar, 'bar'], [o.inMenu, 'menu']]) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Close (X) button on the dashboard's Steam window. Dashboard patch
|
||||
# Close (X) button on the dashboard's Steam window. SteamVR dashboard patch
|
||||
# (steam-close-button/patch.js; "systemui" on 127.0.0.1:8087), registered only
|
||||
# when enabled; enables steamvr-debugger.nix (one SteamVR restart the first time).
|
||||
{ config, lib, pkgs, ... }:
|
||||
@@ -11,7 +11,7 @@ in {
|
||||
options.steamFrame.dashboard.steamCloseButton.enable = lib.mkEnableOption ''
|
||||
a Close (X) button on the dashboard's Steam window. It hides Steam
|
||||
(previous window or just the bar) until shown explicitly, e.g. via the
|
||||
Steam tab. Dashboard patch; off removes it (next switch)'';
|
||||
Steam tab. SteamVR dashboard patch; off removes it (next switch)'';
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
steamFrame.uiPatches.patches = [ {
|
||||
|
||||
@@ -58,11 +58,14 @@ in {
|
||||
options.steamFrame.steamvrDebugger.enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
defaultText = lib.literalMD "on automatically when a dashboard patch (a `steamFrame.uiPatches.patches` entry on port 8087) is enabled";
|
||||
defaultText = lib.literalMD ''
|
||||
on automatically when a SteamVR dashboard patch (a
|
||||
`steamFrame.uiPatches.patches` entry on port 8087) is enabled
|
||||
'';
|
||||
description = ''
|
||||
SteamVR's web helper debugger (dashboard DevTools on 127.0.0.1:8087,
|
||||
VRWebHelper/DebuggerEnabled in steamvr.vrsettings), needed by dashboard
|
||||
patches, which turn it on automatically. Takes effect after one SteamVR
|
||||
VRWebHelper/DebuggerEnabled in steamvr.vrsettings), needed by SteamVR
|
||||
dashboard patches, which turn it on automatically. Takes effect after one SteamVR
|
||||
restart; off resets the key at the next start. Developer Mode also
|
||||
forwards the port to the LAN (0.0.0.0:8088).
|
||||
'';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Adjustable curvature per dashboard window. Dashboard patch
|
||||
# Adjustable curvature per dashboard window. SteamVR dashboard patch
|
||||
# (window-curvature/patch.js; "systemui" on 127.0.0.1:8087), registered only
|
||||
# when enabled; enables steamvr-debugger.nix (one SteamVR restart the first time).
|
||||
{ config, lib, pkgs, ... }:
|
||||
@@ -7,17 +7,23 @@ let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (import ./lib { inherit pkgs; }) mkPatch;
|
||||
|
||||
value = default: description: mkOption {
|
||||
type = types.number;
|
||||
inherit default description;
|
||||
path = [ "steamFrame" "dashboard" "windowCurvature" ];
|
||||
rename = from: to: lib.mkRenamedOptionModule (path ++ [ from ]) (path ++ [ to ]);
|
||||
|
||||
number = type: default: description: mkOption {
|
||||
inherit type default description;
|
||||
};
|
||||
in {
|
||||
imports = [
|
||||
./steam-ui-patches.nix ./steamvr-debugger.nix
|
||||
(lib.mkRemovedOptionModule [ "steamFrame" "dashboard" "windowCurvature" "barDragRoom" ]
|
||||
(lib.mkRemovedOptionModule (path ++ [ "barDragRoom" ])
|
||||
"Not needed: SteamVR keeps sending the bar's coordinates past its edge during a drag.")
|
||||
(lib.mkRemovedOptionModule [ "steamFrame" "dashboard" "windowCurvature" "snap" ]
|
||||
"Snapping is a detent in drag pixels now: use steamFrame.dashboard.windowCurvature.snapPixels.")
|
||||
(lib.mkRemovedOptionModule (path ++ [ "snap" ])
|
||||
"Snapping is a detent in drag pixels now: use steamFrame.dashboard.windowCurvature.detentPixels.")
|
||||
(rename "default" "initial")
|
||||
(rename "snapPixels" "detentPixels")
|
||||
(rename "snapPoints" "detentPoints")
|
||||
(rename "dragThreshold" "dragThresholdPixels")
|
||||
];
|
||||
|
||||
options.steamFrame.dashboard.windowCurvature = {
|
||||
@@ -26,48 +32,56 @@ in {
|
||||
Curvature" menu row (shows the value) or bar button: click toggles
|
||||
curved/flat, drag up/down sets it live. Values are relative to the stock
|
||||
curve (1 = stock, 2 = half the radius, 0 = flat), kept per window until
|
||||
SteamVR restarts. Dashboard patch; off restores stock (next switch)'';
|
||||
default = value 1.0 ''
|
||||
SteamVR restarts. SteamVR dashboard patch; off restores stock (next
|
||||
switch)'';
|
||||
initial = number types.numbers.nonnegative 1.0 ''
|
||||
Curvature a world/hand window (stock: flat) gets when first curved.
|
||||
Dashboard and theater windows start at 1, keeping the docked Steam
|
||||
window concentric with the bar.
|
||||
'';
|
||||
max = value 3.0 "Largest curvature the control goes to (relative to the stock curve).";
|
||||
step = value 0.05 "Step the value is rounded to while dragging.";
|
||||
snapPixels = mkOption {
|
||||
max = number types.numbers.positive 3.0 ''
|
||||
Largest curvature the control goes to (relative to the stock curve).
|
||||
'';
|
||||
step = number types.numbers.positive 0.05 ''
|
||||
Step the value is rounded to while dragging.
|
||||
'';
|
||||
detentPixels = mkOption {
|
||||
type = types.ints.unsigned;
|
||||
default = 24;
|
||||
description = ''
|
||||
Detent at each snap point, in pixels of drag: the value holds at the
|
||||
point for this much travel, then continues from it (no values are
|
||||
skipped). 0 = no detent.
|
||||
Width of the detent at each of `detentPoints`, in pixels of drag:
|
||||
the value holds at the point for this much travel, then continues
|
||||
from it (no values are skipped). 0 = no detent.
|
||||
'';
|
||||
};
|
||||
snapPoints = mkOption {
|
||||
type = types.listOf types.number;
|
||||
detentPoints = mkOption {
|
||||
type = types.listOf types.numbers.nonnegative;
|
||||
default = [ 0 1.0 ];
|
||||
description = "Values with a detent while dragging (0 = flat, 1 = stock curve).";
|
||||
description = ''
|
||||
Values with a detent while dragging (0 = flat, 1 = stock curve).
|
||||
'';
|
||||
};
|
||||
dragThreshold = mkOption {
|
||||
dragThresholdPixels = mkOption {
|
||||
type = types.ints.unsigned;
|
||||
default = 8;
|
||||
description = ''
|
||||
Vertical laser travel (menu px) that turns a press into a drag.
|
||||
'';
|
||||
};
|
||||
dragPixelsPerUnit = value 120 ''
|
||||
dragPixelsPerUnit = number types.numbers.positive 120 ''
|
||||
Menu pixels per 1.0 of curvature (6 px per 0.05 step). The laser stops
|
||||
at the menu's edge (~190 px above the row): with the default, 0 to 1
|
||||
fits into one drag, the full range to `max` = 3 takes two.
|
||||
'';
|
||||
barDragPixelsPerUnit = value 60 ''
|
||||
barDragPixelsPerUnit = number types.numbers.positive 60 ''
|
||||
Bar pixels per 1.0 of curvature on the bottom-bar button.
|
||||
'';
|
||||
haptics = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Controller haptics while dragging (steps, detents, 0/`max` edges); the dashboard's own hover clicks are muted during a drag.
|
||||
Controller haptics while dragging (steps, detents, 0/`max` edges);
|
||||
the dashboard's own hover clicks are muted during a drag.
|
||||
'';
|
||||
};
|
||||
};
|
||||
@@ -75,20 +89,16 @@ in {
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.max > 0 && cfg.step > 0 && cfg.step <= cfg.max;
|
||||
message = "steamFrame.dashboard.windowCurvature: need max > 0 and 0 < step <= max, got max ${builtins.toJSON cfg.max}, step ${builtins.toJSON cfg.step}.";
|
||||
assertion = cfg.step <= cfg.max;
|
||||
message = "steamFrame.dashboard.windowCurvature: need step <= max, got max ${builtins.toJSON cfg.max}, step ${builtins.toJSON cfg.step}.";
|
||||
}
|
||||
{
|
||||
assertion = cfg.default >= 0 && cfg.default <= cfg.max;
|
||||
message = "steamFrame.dashboard.windowCurvature.default must be within 0 .. max (${builtins.toJSON cfg.max}), got ${builtins.toJSON cfg.default}.";
|
||||
assertion = cfg.initial <= cfg.max;
|
||||
message = "steamFrame.dashboard.windowCurvature.initial must be within 0 .. max (${builtins.toJSON cfg.max}), got ${builtins.toJSON cfg.initial}.";
|
||||
}
|
||||
{
|
||||
assertion = lib.all (p: p >= 0 && p <= cfg.max) cfg.snapPoints;
|
||||
message = "steamFrame.dashboard.windowCurvature: snapPoints must be within 0 .. max (${builtins.toJSON cfg.max}).";
|
||||
}
|
||||
{
|
||||
assertion = cfg.dragPixelsPerUnit > 0 && cfg.barDragPixelsPerUnit > 0;
|
||||
message = "steamFrame.dashboard.windowCurvature: dragPixelsPerUnit and barDragPixelsPerUnit must be positive.";
|
||||
assertion = lib.all (p: p <= cfg.max) cfg.detentPoints;
|
||||
message = "steamFrame.dashboard.windowCurvature: detentPoints must be within 0 .. max (${builtins.toJSON cfg.max}).";
|
||||
}
|
||||
];
|
||||
|
||||
@@ -99,7 +109,10 @@ in {
|
||||
patch = mkPatch {
|
||||
name = "window-curvature";
|
||||
src = ./window-curvature/patch.js;
|
||||
opts = removeAttrs cfg [ "enable" "snap" "barDragRoom" ];
|
||||
opts = {
|
||||
inherit (cfg) initial max step detentPixels detentPoints
|
||||
dragThresholdPixels dragPixelsPerUnit barDragPixelsPerUnit haptics;
|
||||
};
|
||||
};
|
||||
unpatch = ./window-curvature/unpatch.js;
|
||||
} ];
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
// curve); dragging up/down with the laser sets the curvature live.
|
||||
//
|
||||
// Target: SteamVR dashboard (vrwebhelper, DevTools 127.0.0.1:8087, title
|
||||
// "systemui"). mkPatch patch (see lib/default.nix); opts: { default, max,
|
||||
// step, snapPixels, snapPoints, dragThreshold, dragPixelsPerUnit,
|
||||
// "systemui"). mkPatch patch (see lib/default.nix); opts: { initial, max,
|
||||
// step, detentPixels, detentPoints, dragThresholdPixels, dragPixelsPerUnit,
|
||||
// barDragPixelsPerUnit, haptics }.
|
||||
//
|
||||
// Stock curvature (frame.curvature, systemui's `curvature` component):
|
||||
@@ -26,7 +26,7 @@
|
||||
// Values per window (key: first overlay key) in
|
||||
// window.__sfuiWindowCurvatureState (schema 1: { values, log }); kept across
|
||||
// re-patch and unpatch, not across a dashboard reload. Without a stored value:
|
||||
// opts.default in the world or on a hand, 1 when docked in the dashboard or
|
||||
// opts.initial in the world or on a hand, 1 when docked in the dashboard or
|
||||
// theater (so the Steam window stays concentric with the dashboard bar).
|
||||
//
|
||||
// Bar button: the bar panel (#legacy-frame-controls-<frameID>) is one button
|
||||
@@ -36,18 +36,18 @@
|
||||
// origin reach the compositor at different times).
|
||||
//
|
||||
// Dragging: value = start value + drag distance / px per unit, rounded to
|
||||
// step. Snap points are detents in drag distance: the value holds there for
|
||||
// opts.snapPixels of travel, then continues, so no value is skipped.
|
||||
// step. Detent points hold in drag distance: the value stays there for
|
||||
// opts.detentPixels of travel, then continues, so no value is skipped.
|
||||
//
|
||||
// Haptics (opts.haptics): VROverlay.TriggerOverlayHapticEffect with stock
|
||||
// EOverlayHapticEffect values: SlidingEdge at 0/max, Snap at snap points,
|
||||
// EOverlayHapticEffect values: SlidingEdge at 0/max, Snap at detent points,
|
||||
// Sliding per step (at most every 30 ms). During a drag the dashboard's own
|
||||
// haptics (hover clicks on buttons the laser passes) are muted.
|
||||
//
|
||||
// Contract with other patches (e.g. frame-controls' long press); this patch
|
||||
// owns press-and-drag on its controls:
|
||||
// - Every element it drives has the class `sfui-curv-ctl`.
|
||||
// - When a press becomes a drag (opts.dragThreshold px vertical) it
|
||||
// - When a press becomes a drag (opts.dragThresholdPixels px vertical) it
|
||||
// dispatches a bubbling CustomEvent `sfui-curv-dragstart` on the element
|
||||
// (detail { frameID, where: 'menu' | 'bar' }); when that drag ends
|
||||
// (release or cancelPress), `sfui-curv-dragend`.
|
||||
@@ -64,18 +64,18 @@
|
||||
// dragValue(v0, dy, ppu, cur).
|
||||
((find, sigs, opts, hooks) => {
|
||||
const NAME = 'window-curvature';
|
||||
const VERSION = 18;
|
||||
const VERSION = 19;
|
||||
const FLAT = 999; // origin distances >= this are "flat"
|
||||
const ICON_OFF = 40, ICON_ON = 39; // Toggle Curvature action icons (sigs.curvatureAction)
|
||||
const HAPTIC = { Snap: 3, Sliding: 4, SlidingEdge: 5 }; // EOverlayHapticEffect (sigs.hapticEffects)
|
||||
|
||||
const o = {
|
||||
default: 1, max: 3, step: 0.05, snapPixels: 24, snapPoints: [0, 1], dragThreshold: 8, dragPixelsPerUnit: 120,
|
||||
initial: 1, max: 3, step: 0.05, detentPixels: 24, detentPoints: [0, 1], dragThresholdPixels: 8, dragPixelsPerUnit: 120,
|
||||
barDragPixelsPerUnit: 60, haptics: true, ...opts,
|
||||
};
|
||||
if (!(o.max > 0 && o.step > 0 && o.step <= o.max && o.default >= 0 && o.default <= o.max && o.snapPixels >= 0 &&
|
||||
Array.isArray(o.snapPoints) && o.snapPoints.every((p) => p >= 0 && p <= o.max) &&
|
||||
o.dragThreshold >= 0 && o.dragPixelsPerUnit > 0 && o.barDragPixelsPerUnit > 0 &&
|
||||
if (!(o.max > 0 && o.step > 0 && o.step <= o.max && o.initial >= 0 && o.initial <= o.max && o.detentPixels >= 0 &&
|
||||
Array.isArray(o.detentPoints) && o.detentPoints.every((p) => p >= 0 && p <= o.max) &&
|
||||
o.dragThresholdPixels >= 0 && o.dragPixelsPerUnit > 0 && o.barDragPixelsPerUnit > 0 &&
|
||||
typeof o.haptics === 'boolean'))
|
||||
return `invalid options ${JSON.stringify(opts)}`;
|
||||
const id = JSON.stringify([VERSION, o]);
|
||||
@@ -120,26 +120,26 @@
|
||||
const keyOf = (f) => f.associatedSummonOverlayKeys?.[0] ?? `frame:${f.frameID}`;
|
||||
const dockOf = (f) => f.docking?.visualDockLocation ?? f.docking?.dockLocation;
|
||||
const strength = (f) => S.values[keyOf(f)] ??
|
||||
([DOCK.LeftHand, DOCK.RightHand, DOCK.World].includes(dockOf(f)) ? o.default : 1);
|
||||
([DOCK.LeftHand, DOCK.RightHand, DOCK.World].includes(dockOf(f)) ? o.initial : 1);
|
||||
const curved = (f) => !!curvatureOf(f)?.shouldCurve;
|
||||
const shown = (f) => (curved(f) ? strength(f) : 0);
|
||||
|
||||
// Value for a drag of dy px (up = positive) from v0 at ppu px per unit, or
|
||||
// null to keep cur. Snap points are detents in drag distance: reaching one
|
||||
// (or starting on it) holds the value there for snapPixels of travel, then
|
||||
// it continues from the point, so no values are skipped. Off a snap point
|
||||
// null to keep cur. Detent points hold in drag distance: reaching one
|
||||
// (or starting on it) holds the value there for detentPixels of travel,
|
||||
// then it continues from the point, so no values are skipped. Off a point
|
||||
// the value is rounded to step; a change of less than 0.6 steps away from
|
||||
// cur is ignored (laser jitter at a step boundary).
|
||||
const dragValue = (v0, dy, ppu, cur) => {
|
||||
const dir = Math.sign(dy);
|
||||
let v = v0, rest = Math.abs(dy), held = null;
|
||||
const points = o.snapPoints.filter((p) => (dir > 0 ? p >= v0 : p <= v0)).sort((a, b) => dir * (a - b));
|
||||
const points = o.detentPoints.filter((p) => (dir > 0 ? p >= v0 : p <= v0)).sort((a, b) => dir * (a - b));
|
||||
for (const p of points) {
|
||||
const dist = Math.abs(p - v) * ppu;
|
||||
if (rest <= dist) break;
|
||||
rest -= dist; v = p;
|
||||
if (rest <= o.snapPixels) { held = p; rest = 0; break; }
|
||||
rest -= o.snapPixels;
|
||||
if (rest <= o.detentPixels) { held = p; rest = 0; break; }
|
||||
rest -= o.detentPixels;
|
||||
}
|
||||
const target = held ?? v + dir * rest / ppu;
|
||||
const nv = clamp(held ?? round(target));
|
||||
@@ -169,7 +169,7 @@
|
||||
try { stockHaptic.call(ov, ov.ThisOverlayHandle(), effect); hits.haptics++; } catch (e) { log('haptic failed', String(e)); }
|
||||
};
|
||||
const hapticFor = (v) => haptic(v <= 0 || v >= o.max ? HAPTIC.SlidingEdge
|
||||
: o.snapPoints.includes(v) ? HAPTIC.Snap : HAPTIC.Sliding);
|
||||
: o.detentPoints.includes(v) ? HAPTIC.Snap : HAPTIC.Sliding);
|
||||
|
||||
// ---- scene graph ---------------------------------------------------------------------
|
||||
// Origin node ids may carry a "<prefix>::" (sub-scene); match the suffix.
|
||||
@@ -338,7 +338,7 @@ ${ROW}:last-child > .sfui-curv-ind { margin-bottom: -14px; }
|
||||
// Presses/clicks are stopped at the element so React (root listener) never
|
||||
// runs the stock onClick; a press that never became a drag is a click on
|
||||
// mouseup. Drag is vertical and relative (the cylinder bends horizontally,
|
||||
// so y barely moves as curvature changes); after dragThreshold px it
|
||||
// so y barely moves as curvature changes); after dragThresholdPixels px it
|
||||
// re-bases (no jump). One press at a time, window capture listeners.
|
||||
const stop = (e) => e.stopPropagation();
|
||||
const STOPPED = ['click', 'dblclick', 'mouseup', 'pointerdown', 'pointerup', 'contextmenu'];
|
||||
@@ -359,7 +359,7 @@ ${ROW}:last-child > .sfui-curv-ind { margin-bottom: -14px; }
|
||||
const y = e.clientY;
|
||||
p.last = y;
|
||||
if (!p.moved) {
|
||||
if (Math.abs(y - p.y0) < o.dragThreshold) return;
|
||||
if (Math.abs(y - p.y0) < o.dragThresholdPixels) return;
|
||||
p.moved = true;
|
||||
muteStock = true;
|
||||
p.c.el?.classList.add('sfui-dragging');
|
||||
@@ -543,6 +543,6 @@ ${ROW}:last-child > .sfui-curv-ind { margin-bottom: -14px; }
|
||||
log('patched', { version: VERSION, opts: o });
|
||||
resend();
|
||||
queueSync();
|
||||
return `patched (default ${o.default}, max ${o.max}, step ${o.step}, snap ${o.snapPixels} px at ${o.snapPoints.join('/')}, ` +
|
||||
return `patched (initial ${o.initial}, max ${o.max}, step ${o.step}, detent ${o.detentPixels} px at ${o.detentPoints.join('/')}, ` +
|
||||
`haptics ${o.haptics ? 'on' : 'off'})`;
|
||||
})
|
||||
Reference in new issue
Block a user