mirror of
https://github.com/lhns/steam-frame-nix.git
synced 2026-10-06 01:00:13 +02:00
steam-close-button: keep Steam hidden after X until shown explicitly
Closing Steam with X now marks it hidden (state steamHidden). Before, bar-only ended as soon as any window became active, so closing a docked window afterwards let stock SteamVR fall back to Steam. While hidden, the stock fallbacks go to the most recently active other docked window with a tab, else bar-only: - autoSwitchOverlayIfNeeded with no active frame; - the dashboard mailbox's dashboard_overlay_destroyed handler (stock: switchToHomeOverlay -> Steam); - show/switch requests for Steam with reason "theater frame destroyed" (key removed) or "SetDockLocation" (echo of X docking Steam: dropped). Steam becoming the active frame (tab click, Steam menu pick, SwitchToDashboardOverlay) ends hidden. The hidden state replaces the rule that swallowed the first ShowOverlay(main) from Steam after each dashboard open in bar-only (Steam doesn't send one there; its only effect was eating a real Steam menu pick). Version 7; state of versions 5/6 is taken over (bar-only implies hidden), their fields are left for a rollback. Signatures: mailbox and dashboard handler anchors, the ShowOverlay ones dropped.
This commit is contained in:
1 parent
817190facd
commit
36fcb207bd
5 files changed
+170
-159
No files matched your search
@@ -557,29 +557,21 @@ own, and with no other window open the dashboard always shows it.
|
||||
|
||||
**Fix:** a UI patch of SteamVR's dashboard (turns on the
|
||||
[SteamVR debugger](#steamvr-debugger-steamvrdebuggerenable)) gives the Steam
|
||||
window an X. Clicking it:
|
||||
window an X that hides Steam: it docks the window back if it was in the
|
||||
world, theater or on a hand, then shows the most recently active other
|
||||
dashboard window, or **just the dashboard bar** if there is none.
|
||||
|
||||
- docks the window back into the dashboard if it was in the world, theater
|
||||
or on a hand;
|
||||
- if it was active, switches to the most recently active other window with a
|
||||
dashboard tab;
|
||||
- with none left, shows **just the dashboard bar** ("bar only").
|
||||
Steam stays hidden until you bring it back (Steam tab, a Steam menu pick,
|
||||
SteamVR asking for it): closing the active window, or a theater window,
|
||||
then goes to the previous window or the bar instead of Steam. This
|
||||
survives dashboard reopens and patch-service restarts; a SteamVR restart
|
||||
starts with Steam. Debugging: `window.__sfuiSteamClose.plan()` /
|
||||
`homePlan()` and `window.__sfuiSteamCloseState` in the `systemui` page.
|
||||
|
||||
Bar-only survives closing/reopening the dashboard and patch-service restarts,
|
||||
and ends as soon as any window becomes active; after a SteamVR restart the
|
||||
dashboard starts with Steam again.
|
||||
|
||||
Debugging: `window.__sfuiSteamClose.plan()` in the `systemui` page tells
|
||||
what a click would do; state is `window.__sfuiSteamCloseState`.
|
||||
|
||||
**Limitations:**
|
||||
|
||||
- If Steam doesn't send its automatic show request after a dashboard open,
|
||||
the first Steam menu pick in bar-only is swallowed once.
|
||||
- Steam shown via SteamVR's `SwitchToDashboardOverlay` path ends bar-only.
|
||||
- No effect with a VRLink remote dashboard.
|
||||
- Turning the option off while bar-only leaves no active window until the
|
||||
next tab click or dashboard open.
|
||||
**Limitations:** SteamVR's rarer "go home" paths (Now Playing after a game
|
||||
quits, message overlays) still show Steam; no effect with a VRLink remote
|
||||
dashboard; turning the option off while bar-only leaves no active window
|
||||
until the next tab click or dashboard open.
|
||||
|
||||
**Caveats:** found by signature (`steam-close-button` in `signatures.json`);
|
||||
on mismatch the dashboard stays stock. Tested with SteamVR build 11008059.
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
"checkOnly": true,
|
||||
"module": { "includes": ["e[e.Alive=2]=\"Alive\""] },
|
||||
"expects": ["this.m_eState=", "this.closing=", "this.docking=", "this.tab=", "get isActiveDashboardFrame()",
|
||||
"this.props="]
|
||||
"this.props=", "get associatedSummonOverlayKeys()"]
|
||||
},
|
||||
"frameStore": {
|
||||
"checkOnly": true,
|
||||
@@ -223,19 +223,29 @@
|
||||
"checkOnly": true,
|
||||
"module": { "includes": ["window.DashboardStore="] },
|
||||
"expects": ["_setActiveFrame(e,t){", "\"_setActiveFrame\"", "get mainSteamFrame()", "m_unMainSteamFrameID",
|
||||
"get activeFrameID()", "get activeFrame()", "get visibilityState()", "get isFullyVisible()"]
|
||||
"get activeFrameID()", "get activeFrame()", "get isFullyVisible()"]
|
||||
},
|
||||
"dashboard": {
|
||||
"checkOnly": true,
|
||||
"module": { "includes": ["window.Dashboard=this", "autoSwitchOverlayIfNeeded(){"] },
|
||||
"expects": ["onShowOverlayRequestFromSteam(e){",
|
||||
"SetImplementation(\"ShowOverlay\",(e=>{this.onShowOverlayRequestFromSteam(e)}))",
|
||||
"this.autoSwitchOverlayIfNeeded()", "switchToFrameInternal(e,t,r){", "\"hideDashboard\"",
|
||||
"visibleInDashboardBar"]
|
||||
"expects": ["this.autoSwitchOverlayIfNeeded()", "switchToFrameInternal(e,t,r){", "\"hideDashboard\"",
|
||||
"visibleInDashboardBar", "this.m_mailbox=",
|
||||
"k_sDashboardOverlayDestroyedMessage=\"dashboard_overlay_destroyed\"",
|
||||
"k_sDashboardOverlayDestroyedMessage,this.onDashboardOverlayDestroyed)",
|
||||
"onDashboardOverlayDestroyed(e){", "&&this.switchToHomeOverlay()}",
|
||||
"RegisterHandler(\"show_dashboard_requested\",(e=>this.showDashboardOverlay(",
|
||||
"RegisterHandler(\"switch_dashboard_overlay_requested\",(e=>this.showDashboardOverlay(",
|
||||
"overlayKey:null==e?void 0:e.overlay_key,sReason:null==e?void 0:e.reason"]
|
||||
},
|
||||
"mailbox": {
|
||||
"checkOnly": true,
|
||||
"module": { "includes": ["RegisterHandler(e,t){this.m_oHandlers[e]=t}"] },
|
||||
"expects": ["this.m_oHandlers[t.type](t)"]
|
||||
},
|
||||
"docking": {
|
||||
"checkOnly": true,
|
||||
"module": { "includes": ["get dockLocation()", "SetDockLocation(e"], "excludes": ["get associatedSummonOverlayKeys()"] }
|
||||
"module": { "includes": ["get dockLocation()", "SetDockLocation(e"], "excludes": ["get associatedSummonOverlayKeys()"] },
|
||||
"expects": ["sReason:\"SetDockLocation\""]
|
||||
},
|
||||
"overlayKeys": {
|
||||
"checkOnly": true,
|
||||
|
||||
@@ -9,10 +9,9 @@ in {
|
||||
imports = [ ./steam-ui-patches.nix ./steamvr-debugger.nix ];
|
||||
|
||||
options.steamFrame.dashboard.steamCloseButton.enable = lib.mkEnableOption ''
|
||||
a Close (X) button on the dashboard's Steam window: docks it back if it
|
||||
was in the world, then switches to the last active other window, or
|
||||
leaves only the dashboard bar (until a window is activated, e.g. via the
|
||||
Steam tab). Dashboard patch; off removes it (next switch)'';
|
||||
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)'';
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
steamFrame.uiPatches.patches = [ {
|
||||
|
||||
+133
-124
@@ -1,51 +1,40 @@
|
||||
// steam-close-button: a Close (X) button on the SteamVR dashboard's main
|
||||
// "Steam" window (overlay valve.steam.gamepadui.main).
|
||||
// Target: SteamVR dashboard (vrwebhelper, DevTools 127.0.0.1:8087, title
|
||||
// "systemui"). mkPatch patch (see lib/default.nix); no options.
|
||||
// steam-close-button: a Close (X) button on the SteamVR dashboard's main Steam
|
||||
// window (overlay valve.steam.gamepadui.main). Target: "systemui" on
|
||||
// 127.0.0.1:8087; mkPatch patch, no options.
|
||||
//
|
||||
// The button: a frame's `closing` component shows X when a close method is
|
||||
// possible; method 0 is "componentProps.onCloseRequested exists", with
|
||||
// componentProps = {...defaultComponentProps, ...frame.props.componentProps.closing}.
|
||||
// The main frame passes no closing props, so this instance's
|
||||
// defaultComponentProps is replaced by a copy adding onCloseRequested
|
||||
// (original as non-enumerable __sfuiOrig) and frame.props is re-assigned (in a
|
||||
// MobX action) so the computed re-evaluates. Later React props updates keep it.
|
||||
// Button: the frame's `closing` component shows X when
|
||||
// componentProps.onCloseRequested exists; this instance's defaultComponentProps
|
||||
// gets one (original kept as __sfuiOrig) and frame.props is re-assigned so the
|
||||
// MobX computed re-evaluates.
|
||||
//
|
||||
// Clicking X: the frame can't be destroyed, so it is put out of view:
|
||||
// * undocked (world/theater/hand) -> docked back into the dashboard;
|
||||
// * if it was active -> switch to the most recently active other frame that
|
||||
// is alive, docked and has a dashboard-bar tab (as a tab click does);
|
||||
// * with none -> "bar only": dashboard open with no active frame.
|
||||
// Stock SteamVR has that state but never keeps it, so while bar-only is on
|
||||
// and no frame is active:
|
||||
// - an instance override of Dashboard.autoSwitchOverlayIfNeeded returns
|
||||
// early (its autorun, and reopening the dashboard, would switch back);
|
||||
// - an instance override of Dashboard.onShowOverlayRequestFromSteam drops
|
||||
// the FIRST ShowOverlay(main) from Steam after each dashboard open, within
|
||||
// STEAM_SHOW_CAP_MS (Steam sends it by itself 50 ms .. 1.7 s after open).
|
||||
// Trade-off: if Steam doesn't send it, the user's first explicit Steam-menu
|
||||
// pick in that session is swallowed once.
|
||||
// Steam tab clicks and SteamVR's SwitchToDashboardOverlay aren't filtered.
|
||||
// Bar-only ends as soon as any frame becomes active.
|
||||
// X hides Steam: docks the window back if undocked, then switches to the most
|
||||
// recently active other alive, docked frame with a bar tab, else "bar only"
|
||||
// (no active frame). Without the Dashboard component it hides the dashboard.
|
||||
// Steam stays hidden until it is the active frame again, which only explicit
|
||||
// requests do (tab click, Steam menu pick, SwitchToDashboardOverlay). While
|
||||
// hidden, stock fallbacks to Steam go to that same target instead:
|
||||
// - Dashboard.autoSwitchOverlayIfNeeded (instance override): no active frame
|
||||
// (active window closed, dashboard opened) -> target; bar-only stays;
|
||||
// - mailbox handler "dashboard_overlay_destroyed" (stock: active overlay gone
|
||||
// -> switchToHomeOverlay -> Steam) -> target;
|
||||
// - mailbox show/switch requests for Steam with reason "SetDockLocation" (echo
|
||||
// of X docking Steam) are dropped, "theater frame destroyed" (vrserver) are
|
||||
// passed on without the key.
|
||||
// Rarer stock "go home" paths (Now Playing, message overlay) still show Steam.
|
||||
//
|
||||
// State (bar-only flag, open time, pending request, history, log) lives in
|
||||
// window.__sfuiSteamCloseState (schema 1), taken over by upgrades. Teardown
|
||||
// never switches frames and keeps the state, so a re-injection continues
|
||||
// bar-only; a dashboard reload starts fresh.
|
||||
//
|
||||
// Without the Dashboard component, X hides the dashboard. RequestClose's
|
||||
// spinner (it expects the frame to go away) is reset right after. A MobX
|
||||
// reaction tracks active-frame history and re-applies when the main frame
|
||||
// changes. window.__sfuiSteamClose: plan() (what a click would do),
|
||||
// wouldIgnoreSteamShow(), barOnly, state, teardown(). Result: "patched" /
|
||||
// "unchanged", plus " (bar-only, N ignored Steam requests)" when relevant.
|
||||
// Contract: state in window.__sfuiSteamCloseState (schema 1: steamHidden,
|
||||
// barOnly, history, redirected, redirects; older versions' fields are left
|
||||
// for a rollback) survives re-injection; teardown restores all overrides and
|
||||
// never switches frames.
|
||||
// window.__sfuiSteamClose: plan(), homePlan(), steamHidden, barOnly, state.
|
||||
((find, sigs, opts) => {
|
||||
const NAME = 'steam-close-button';
|
||||
const VERSION = 5;
|
||||
const MAIN_KEY = 'valve.steam.gamepadui.main'; // anchored by sigs.overlayKeys
|
||||
const FRAME_ALIVE = 2; // frame m_eState Alive (sigs.frame)
|
||||
const STEAM_SHOW_CAP_MS = 10000;
|
||||
const VERSION = 7;
|
||||
const MAIN_KEY = 'valve.steam.gamepadui.main'; // sigs.overlayKeys
|
||||
const FRAME_ALIVE = 2; // sigs.frame
|
||||
const REASON = 'sfui steam-close-button';
|
||||
const DOCK_ECHO = 'SetDockLocation'; // sigs.docking
|
||||
const THEATER_GONE = 'theater frame destroyed'; // sent by vrserver
|
||||
|
||||
const prev = window.__sfuiSteamClose;
|
||||
if (prev?.version === VERSION) return prev.apply();
|
||||
@@ -66,21 +55,19 @@
|
||||
|
||||
prev?.teardown?.(); // other VERSION; never switches frames
|
||||
|
||||
// Page state. To keep bar-only across dashboard reloads, load/save it here
|
||||
// (e.g. localStorage; shownAt is performance.now() of this page).
|
||||
const loadState = () => {
|
||||
const S = (() => {
|
||||
const s = window.__sfuiSteamCloseState;
|
||||
if (s?.schema === 1) return s;
|
||||
return (window.__sfuiSteamCloseState = {
|
||||
schema: 1, barOnly: false, shownAt: -Infinity, steamShowPending: false,
|
||||
history: [], ignored: 0, suppressed: [],
|
||||
});
|
||||
};
|
||||
const S = loadState();
|
||||
if (S.barOnly && DS.activeFrame != null) S.barOnly = false; // a frame became active meanwhile
|
||||
if (s?.schema !== 1) return (window.__sfuiSteamCloseState =
|
||||
{ schema: 1, steamHidden: false, barOnly: false, history: [], redirected: 0, redirects: [] });
|
||||
s.steamHidden ??= !!s.barOnly; // version <= 5: bar-only implied hidden
|
||||
s.barOnly = !!s.barOnly && DS.activeFrame == null;
|
||||
s.history ??= []; s.redirected ??= 0; s.redirects ??= [];
|
||||
return s;
|
||||
})();
|
||||
const mainActive = () => DS.activeFrameID != null && DS.activeFrameID === DS.m_unMainSteamFrameID;
|
||||
if (mainActive()) S.steamHidden = false;
|
||||
|
||||
// Recently active frame IDs, most recent last.
|
||||
const note = (id) => {
|
||||
const note = (id) => { // recently active frames, most recent last
|
||||
if (id == null) return;
|
||||
const h = S.history, i = h.indexOf(id);
|
||||
if (i >= 0) h.splice(i, 1);
|
||||
@@ -89,16 +76,17 @@
|
||||
};
|
||||
note(DS.activeFrameID);
|
||||
|
||||
const fallbackFrame = (main) => {
|
||||
const fallbackFrame = (except) => {
|
||||
const main = DS.mainSteamFrame;
|
||||
for (let i = S.history.length - 1; i >= 0; i--) {
|
||||
const f = FS.GetFrame(S.history[i]);
|
||||
if (f && f.frameID !== main.frameID && f.m_eState === FRAME_ALIVE &&
|
||||
if (f && f !== main && f !== except && f.m_eState === FRAME_ALIVE &&
|
||||
f.tab?.visibleInDashboardBar && f.docking?.dockLocation === DOCK_DASHBOARD) return f;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// What a click on the main frame's X would do: { action, dock, to?, title? }.
|
||||
// What a click on X would do: { action, dock, to?, title? }.
|
||||
const plan = (frame = DS.mainSteamFrame) => {
|
||||
if (!frame) return { action: 'none', reason: 'no main frame' };
|
||||
const dock = !!frame.docking && frame.docking.dockLocation !== DOCK_DASHBOARD;
|
||||
@@ -108,84 +96,108 @@
|
||||
if (window.Dashboard && DS._setActiveFrame) return { action: 'bar-only', dock };
|
||||
return { action: DS.isFullyVisible ? 'hide' : 'none', dock };
|
||||
};
|
||||
// Where a fallback away from `except` goes while hidden.
|
||||
const homePlan = (except = DS.activeFrame) => {
|
||||
const f = fallbackFrame(except);
|
||||
return f ? { action: 'switch', to: f.frameID, title: f.title } : { action: 'bar-only' };
|
||||
};
|
||||
|
||||
// ---- bar-only: overrides on the Dashboard instance --------------------------
|
||||
const holding = () => S.barOnly && DS.activeFrame == null;
|
||||
const wouldIgnoreSteamShow = (key = MAIN_KEY) => holding() && key === MAIN_KEY && DS.visibilityState !== 0 &&
|
||||
S.steamShowPending && performance.now() - S.shownAt < STEAM_SHOW_CAP_MS;
|
||||
const wrappers = {
|
||||
const hiding = () => S.steamHidden && DS.mainSteamFrame != null && !mainActive();
|
||||
const barOnly = () => {
|
||||
S.barOnly = true;
|
||||
if (DS.activeFrameID != null) DS._setActiveFrame(undefined, undefined);
|
||||
};
|
||||
const goHome = (except, why) => {
|
||||
const p = homePlan(except);
|
||||
S.redirected++;
|
||||
S.redirects.push({ at: new Date().toISOString(), why, to: p.to ?? 'bar-only' });
|
||||
if (S.redirects.length > 10) S.redirects.shift();
|
||||
if (p.to != null && window.Dashboard?.switchToFrameInternal) window.Dashboard.switchToFrameInternal(FS.GetFrame(p.to), undefined, REASON);
|
||||
else barOnly();
|
||||
};
|
||||
|
||||
// ---- overrides: a Dashboard instance method and its mailbox handlers ------
|
||||
const methodWrappers = {
|
||||
autoSwitchOverlayIfNeeded: (orig) => function autoSwitchOverlayIfNeeded(...args) {
|
||||
if (holding()) return;
|
||||
if (S.barOnly && DS.activeFrame == null) return;
|
||||
if (hiding() && DS.activeFrame == null) return goHome(null, 'autoSwitchOverlayIfNeeded');
|
||||
return orig.apply(this, args);
|
||||
},
|
||||
onShowOverlayRequestFromSteam: (orig) => function onShowOverlayRequestFromSteam(e, ...rest) {
|
||||
if (e?.overlay_key === MAIN_KEY) {
|
||||
const ignore = wouldIgnoreSteamShow(MAIN_KEY);
|
||||
S.steamShowPending = false;
|
||||
if (ignore) {
|
||||
S.ignored++;
|
||||
S.suppressed.push({ at: new Date().toISOString(), afterOpenMs: Math.round(performance.now() - S.shownAt), key: MAIN_KEY });
|
||||
if (S.suppressed.length > 10) S.suppressed.shift();
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
const showRequest = (orig) => function showRequest(e, ...rest) {
|
||||
if (hiding() && e?.overlay_key === MAIN_KEY) {
|
||||
if (e.reason === DOCK_ECHO) return;
|
||||
if (e.reason === THEATER_GONE) return orig.call(this, { ...e, overlay_key: undefined }, ...rest);
|
||||
}
|
||||
return orig.call(this, e, ...rest);
|
||||
};
|
||||
const handlerWrappers = {
|
||||
dashboard_overlay_destroyed: (orig) => function overlayDestroyed(e, ...rest) {
|
||||
const a = DS.activeFrame;
|
||||
if (hiding() && a?.associatedSummonOverlayKeys?.includes(e?.overlay_key ?? '')) return goHome(a, 'overlay destroyed');
|
||||
return orig.call(this, e, ...rest);
|
||||
},
|
||||
show_dashboard_requested: showRequest,
|
||||
switch_dashboard_overlay_requested: showRequest,
|
||||
};
|
||||
let guarded = null; // Dashboard instance carrying the overrides
|
||||
// Installs the overrides (idempotent); true if autoSwitchOverlayIfNeeded,
|
||||
// the one bar-only needs, is overridden.
|
||||
const ours = (f) => f?.__sfuiPatch === NAME && f.__sfuiVersion === VERSION;
|
||||
const wrapAll = (obj, wrappers) => {
|
||||
for (const [name, make] of Object.entries(wrappers)) {
|
||||
const cur = obj[name];
|
||||
if (typeof cur !== 'function' || ours(cur)) continue;
|
||||
const own = Object.getOwnPropertyDescriptor(obj, name);
|
||||
if (own && !own.writable && !own.configurable) continue;
|
||||
const old = cur.__sfuiPatch === NAME; // left by another version
|
||||
const orig = old ? cur.__sfuiOrig : cur;
|
||||
obj[name] = Object.assign(make(orig), { __sfuiPatch: NAME, __sfuiVersion: VERSION, __sfuiOrig: orig,
|
||||
__sfuiHadOwn: old ? cur.__sfuiHadOwn : !!own });
|
||||
}
|
||||
};
|
||||
const DASH_NAMES = [...Object.keys(methodWrappers), 'onShowOverlayRequestFromSteam']; // + version <= 6
|
||||
const unwrapAll = (obj, names) => {
|
||||
for (const name of names) {
|
||||
const f = obj?.[name];
|
||||
if (f?.__sfuiPatch !== NAME) continue;
|
||||
if (f.__sfuiHadOwn) obj[name] = f.__sfuiOrig;
|
||||
else delete obj[name];
|
||||
}
|
||||
};
|
||||
let guarded = null, guardedHandlers = null;
|
||||
// Installs the overrides (idempotent); true if autoSwitchOverlayIfNeeded is ours.
|
||||
const guard = () => {
|
||||
const dash = window.Dashboard;
|
||||
if (!dash) return false;
|
||||
if (guarded && guarded !== dash) unguard(); // Dashboard remounted
|
||||
guarded = dash;
|
||||
for (const [name, make] of Object.entries(wrappers)) {
|
||||
const cur = dash[name];
|
||||
if (typeof cur !== 'function' || (cur.__sfuiPatch === NAME && cur.__sfuiVersion === VERSION)) continue;
|
||||
const own = Object.getOwnPropertyDescriptor(dash, name);
|
||||
if (own && !own.writable && !own.configurable) continue;
|
||||
const ours = cur.__sfuiPatch === NAME; // left by another version
|
||||
const orig = ours ? cur.__sfuiOrig : cur;
|
||||
dash[name] = Object.assign(make(orig), {
|
||||
__sfuiPatch: NAME, __sfuiVersion: VERSION, __sfuiOrig: orig, __sfuiHadOwn: ours ? cur.__sfuiHadOwn : !!own,
|
||||
});
|
||||
unwrapAll(dash, DASH_NAMES.filter((n) => !(n in methodWrappers))); // left by an older version
|
||||
wrapAll(dash, methodWrappers);
|
||||
const h = dash.m_mailbox?.m_oHandlers; // registered once the mailbox is up
|
||||
if (h && typeof h === 'object') {
|
||||
guardedHandlers = h;
|
||||
wrapAll(h, Object.fromEntries(Object.entries(handlerWrappers).filter(([t]) => typeof h[t] === 'function')));
|
||||
}
|
||||
return dash.autoSwitchOverlayIfNeeded?.__sfuiVersion === VERSION && dash.autoSwitchOverlayIfNeeded.__sfuiPatch === NAME;
|
||||
return ours(dash.autoSwitchOverlayIfNeeded);
|
||||
};
|
||||
const unguard = () => {
|
||||
const d = guarded;
|
||||
guarded = null;
|
||||
for (const name of Object.keys(wrappers)) {
|
||||
const f = d?.[name];
|
||||
if (f?.__sfuiPatch !== NAME) continue;
|
||||
if (f.__sfuiHadOwn) d[name] = f.__sfuiOrig;
|
||||
else delete d[name];
|
||||
}
|
||||
unwrapAll(guarded, DASH_NAMES);
|
||||
unwrapAll(guardedHandlers, Object.keys(handlerWrappers));
|
||||
guarded = guardedHandlers = null;
|
||||
};
|
||||
|
||||
const onClose = (frame) => {
|
||||
const p = plan(frame);
|
||||
if (p.dock) frame.docking.SetDockLocation(DOCK_DASHBOARD);
|
||||
const dash = window.Dashboard;
|
||||
switch (p.action) {
|
||||
case 'switch':
|
||||
dash.switchToFrameInternal(FS.GetFrame(p.to), undefined, REASON);
|
||||
break;
|
||||
case 'bar-only':
|
||||
if (!guard()) { dash.hideDashboard?.(REASON, false); break; }
|
||||
S.barOnly = true;
|
||||
S.steamShowPending = false;
|
||||
DS._setActiveFrame(undefined, undefined);
|
||||
break;
|
||||
case 'hide':
|
||||
if (dash?.hideDashboard) dash.hideDashboard(REASON, false);
|
||||
else window.VRHTML?.VRDashboardManager?.HideDashboard(REASON, false);
|
||||
break;
|
||||
if (p.action !== 'none' || p.dock) { guard(); S.steamHidden = true; }
|
||||
if (p.dock) frame.docking.SetDockLocation(DOCK_DASHBOARD);
|
||||
if (p.action === 'switch') dash.switchToFrameInternal(FS.GetFrame(p.to), undefined, REASON);
|
||||
else if (p.action === 'bar-only' && guard()) barOnly();
|
||||
else if (p.action === 'bar-only' || p.action === 'hide') {
|
||||
if (dash?.hideDashboard) dash.hideDashboard(REASON, false);
|
||||
else window.VRHTML?.VRDashboardManager?.HideDashboard(REASON, false);
|
||||
}
|
||||
};
|
||||
|
||||
// ---- the button: defaultComponentProps of the main frame's `closing` --------
|
||||
// ---- the button --------------------------------------------------------------
|
||||
let closing = null; // the component we changed
|
||||
const nudge = (frame) => mx.runInAction(() => { if (frame?.props) frame.props = { ...frame.props }; });
|
||||
const resetSpinner = (c) => mx.runInAction(() => { c.m_bShowSpinner = false; });
|
||||
@@ -199,8 +211,8 @@
|
||||
const orig = cur?.__sfuiPatch === NAME ? cur.__sfuiOrig : cur;
|
||||
const props = {
|
||||
...orig,
|
||||
onCloseRequested: () => {
|
||||
try { onClose(frame); } finally { setTimeout(() => resetSpinner(c), 0); } // set after we return
|
||||
onCloseRequested: () => { // RequestClose sets the spinner after we return
|
||||
try { onClose(frame); } finally { setTimeout(() => resetSpinner(c), 0); }
|
||||
},
|
||||
};
|
||||
for (const [k, value] of [['__sfuiPatch', NAME], ['__sfuiVersion', VERSION], ['__sfuiOrig', orig]])
|
||||
@@ -226,19 +238,15 @@
|
||||
guard();
|
||||
if (!f) return 'main Steam frame not found';
|
||||
const r = patchFrame(f);
|
||||
const notes = [S.barOnly && 'bar-only', S.ignored && `${S.ignored} ignored Steam request${S.ignored === 1 ? '' : 's'}`]
|
||||
.filter(Boolean);
|
||||
const notes = [S.steamHidden && 'Steam hidden', S.barOnly && 'bar-only',
|
||||
S.redirected && `${S.redirected} redirect${S.redirected === 1 ? '' : 's'}`].filter(Boolean);
|
||||
return notes.length ? `${r} (${notes.join(', ')})` : r;
|
||||
};
|
||||
|
||||
const disposers = [
|
||||
mx.reaction(() => DS.activeFrameID, note),
|
||||
mx.reaction(() => DS.activeFrameID != null, (active) => {
|
||||
if (active) { S.barOnly = false; S.steamShowPending = false; }
|
||||
}),
|
||||
mx.reaction(() => DS.visibilityState !== 0, (shown) => {
|
||||
if (shown) { S.shownAt = performance.now(); S.steamShowPending = true; }
|
||||
}),
|
||||
mx.reaction(() => DS.activeFrameID != null, (active) => { if (active) S.barOnly = false; }),
|
||||
mx.reaction(mainActive, (on) => { if (on) S.steamHidden = false; }),
|
||||
mx.reaction(() => DS.m_unMainSteamFrameID, () => { try { apply(); } catch (e) { console.error(NAME, e); } }),
|
||||
];
|
||||
const teardown = () => {
|
||||
@@ -248,7 +256,8 @@
|
||||
if (window.__sfuiSteamClose === self) delete window.__sfuiSteamClose;
|
||||
};
|
||||
const self = window.__sfuiSteamClose = {
|
||||
version: VERSION, apply, teardown, plan, wouldIgnoreSteamShow, state: S,
|
||||
version: VERSION, apply, teardown, plan, homePlan, state: S,
|
||||
get steamHidden() { return S.steamHidden; },
|
||||
get barOnly() { return S.barOnly; },
|
||||
};
|
||||
return apply().replace(/^unchanged/, 'patched');
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Reverts steam-close-button/patch.js (the X disappears). Never switches
|
||||
// frames and keeps window.__sfuiSteamCloseState, so a re-injection continues;
|
||||
// if removed while bar-only, the dashboard stays without an active frame
|
||||
// until the next tab click or open. Safe when not patched.
|
||||
// frames and keeps window.__sfuiSteamCloseState (Steam hidden, bar-only), so
|
||||
// a re-injection continues; restores the Dashboard methods and mailbox
|
||||
// handlers it wrapped. If removed while bar-only, the dashboard stays without
|
||||
// an active frame until the next tab click or open. Safe when not patched.
|
||||
(() => {
|
||||
const s = window.__sfuiSteamClose;
|
||||
if (!s) return 'not patched';
|
||||
|
||||
Reference in new issue
Block a user