diff options
| author | AAGaming <aagaming@riseup.net> | 2026-10-26 23:50:59 -0400 |
|---|---|---|
| committer | AAGaming <aagaming@riseup.net> | 2026-10-26 23:50:59 -0400 |
| commit | e82a55e9582fbd6d84f3f4510b04480c35e41fe4 (patch) | |
| tree | 1fe05c2296bbedbf301137a67a28ef542804fd69 /frontend/src/hooks/tabs.tsx | |
| parent | 6301508037b30489c42ee9ea8acee97cdea76297 (diff) | |
| download | decky-loader-e82a55e9582fbd6d84f3f4510b04480c35e41fe4.tar.gz decky-loader-e82a55e9582fbd6d84f3f4510b04480c35e41fe4.zip | |
feat: initial VERY VERY incomplete steam frame support
Diffstat (limited to 'frontend/src/hooks/tabs.tsx')
| -rw-r--r-- | frontend/src/hooks/tabs.tsx | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/frontend/src/hooks/tabs.tsx b/frontend/src/hooks/tabs.tsx new file mode 100644 index 00000000..4d917b6c --- /dev/null +++ b/frontend/src/hooks/tabs.tsx @@ -0,0 +1,131 @@ +// TabsHook for versions after the Desktop merge +import { + ErrorBoundary, + Patch, + QuickAccessTab, + afterPatch, + createReactTreePatcher, + findInReactTree, + findModuleByExport, + getReactRoot, +} from '@decky/ui'; + +import { QuickAccessVisibleStateProvider } from '../components/QuickAccessVisibleState'; +import Logger from '../logger'; + +declare global { + interface Window { + __TABS_HOOK_INSTANCE: any; + } +} + +interface Tab { + id: QuickAccessTab | number; + title: any; + content: any; + icon: any; +} + +class TabsHook extends Logger { + // private keys = 7; + tabs: Tab[] = []; + private qamBrowserViewPatch?: Patch; + private qamEmbeddedPatch?: Patch; + + constructor() { + super('TabsHook'); + + this.log('Initialized'); + window.__TABS_HOOK_INSTANCE?.deinit?.(); + window.__TABS_HOOK_INSTANCE = this; + } + + init() { + const qamModule = findModuleByExport((e) => e?.type?.toString?.()?.includes('QuickAccessMenuBrowserView')); + const qamBrowserViewRenderer = Object.values(qamModule).find((e: any) => + e?.type?.toString?.()?.includes('QuickAccessMenuBrowserView'), + ); + const qamEmbeddedRenderer = Object.values(qamModule).find((e: any) => + e?.type?.toString?.()?.includes('QuickAccessMenuEmbedded'), + ); + + const patchHandler = createReactTreePatcher( + [(tree) => findInReactTree(tree, (node) => node?.props?.onFocusNavDeactivated)], + (args, ret) => { + const tabs = findInReactTree(ret, (x) => x?.props?.tabs); + this.render(tabs.props.tabs, args[0].visible); + return ret; + }, + 'TabsHook', + ); + + this.qamBrowserViewPatch = afterPatch(qamBrowserViewRenderer, 'type', patchHandler); + if (qamEmbeddedRenderer) this.qamEmbeddedPatch = afterPatch(qamEmbeddedRenderer, 'type', patchHandler); + + // Patch already rendered qam + const root = getReactRoot(document.getElementById('root') as any); + const qamNode = + root && + findInReactTree( + root, + (n: any) => + n.elementType == qamBrowserViewRenderer || + (qamEmbeddedRenderer != null && n.elementType == qamEmbeddedRenderer), + ); // need elementType, because type is actually mobx wrapper + if (qamNode) { + // Only affects this fiber node so we don't need to unpatch here + qamNode.type = qamNode.elementType.type; + if (qamNode?.alternate) { + qamNode.alternate.type = qamNode.type; + } + } + } + + deinit() { + this.qamBrowserViewPatch?.unpatch(); + this.qamEmbeddedPatch?.unpatch(); + } + + add(tab: Tab) { + this.debug('Adding tab', tab.id, 'to render array'); + this.tabs.push(tab); + } + + removeById(id: number) { + this.debug('Removing tab', id); + this.tabs = this.tabs.filter((tab) => tab.id !== id); + } + + render(existingTabs: any[], visible: boolean) { + let deckyTabAmount = existingTabs.reduce((prev: any, cur: any) => (cur.decky ? prev + 1 : prev), 0); + if (deckyTabAmount == this.tabs.length) { + for (let tab of existingTabs) { + if (tab?.decky) { + if (tab?.qAMVisibilitySetter) { + tab?.qAMVisibilitySetter(visible); + } else { + tab.initialVisibility = visible; + } + } + } + return; + } + for (const { title, icon, content, id } of this.tabs) { + const tab: any = { + key: id, + title, + tab: icon, + decky: true, + initialVisibility: visible, + }; + tab.panel = ( + <ErrorBoundary> + <QuickAccessVisibleStateProvider tab={tab}>{content}</QuickAccessVisibleStateProvider> + </ErrorBoundary> + ); + existingTabs.push(tab); + } + } +} + +export default TabsHook; |
