diff options
Diffstat (limited to 'src/components/GameConfigurationSelector.tsx')
| -rw-r--r-- | src/components/GameConfigurationSelector.tsx | 102 |
1 files changed, 55 insertions, 47 deletions
diff --git a/src/components/GameConfigurationSelector.tsx b/src/components/GameConfigurationSelector.tsx index 5f23b91..92eacba 100644 --- a/src/components/GameConfigurationSelector.tsx +++ b/src/components/GameConfigurationSelector.tsx @@ -1,5 +1,5 @@ import { ButtonItem, ConfirmModal, Field, PanelSectionRow, showModal } from "@decky/ui"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState, type RefObject } from "react"; import { RiArrowDownSFill, RiArrowUpSFill } from "react-icons/ri"; import { GameTarget } from "../hooks/useGameConfiguration"; @@ -9,10 +9,12 @@ interface Props { onSelect: (appid: string) => void; onEnableAll: () => Promise<void>; onResetAll: () => Promise<void>; + focusConfiguredToggle?: boolean; + onConfiguredToggleFocused?: () => void; } -const CONFIGURED_COLLAPSED_KEY = "lsfg-configured-games-collapsed-v3"; -const AVAILABLE_COLLAPSED_KEY = "lsfg-available-games-collapsed-v2"; +const ENABLED_COLLAPSED_KEY = "lsfg-enabled-games-collapsed-v4"; +const AVAILABLE_COLLAPSED_KEY = "lsfg-available-games-collapsed-v3"; function usePersistentCollapsed(key: string) { const [collapsed, setCollapsed] = useState(() => { @@ -34,28 +36,36 @@ function usePersistentCollapsed(key: string) { return [collapsed, () => setCollapsed((value) => !value)] as const; } +function targetDescription(game: GameTarget): string { + if (game.transport.kind === "flatpak") return "Non-Steam ยท Flatpak"; + return game.nonSteam ? "Non-Steam" : "Steam"; +} + function GameGroup({ title, games, collapsed, onToggle, onSelect, + toggleRef, }: { title: string; games: GameTarget[]; collapsed: boolean; onToggle: () => void; onSelect: (appid: string) => void; + toggleRef?: RefObject<HTMLDivElement>; }) { if (games.length === 0) return null; return ( <> <PanelSectionRow> - <Field label={`${title} (${games.length})`} bottomSeparator="none" /> + <Field label={title + " (" + games.length + ")"} bottomSeparator="none" /> </PanelSectionRow> <PanelSectionRow> <div + ref={toggleRef} className="LSFG_GameGroupCollapseButton_Container" style={{ marginTop: "-2px", marginBottom: "4px" }} > @@ -64,11 +74,7 @@ function GameGroup({ bottomSeparator={collapsed ? "standard" : "none"} onClick={onToggle} > - {collapsed ? ( - <RiArrowDownSFill /> - ) : ( - <RiArrowUpSFill /> - )} + {collapsed ? <RiArrowDownSFill /> : <RiArrowUpSFill />} </ButtonItem> </div> </PanelSectionRow> @@ -76,7 +82,7 @@ function GameGroup({ <PanelSectionRow key={game.appid}> <Field label={game.name} - description={game.nonSteam ? "Non-Steam" : "Steam"} + description={targetDescription(game)} onActivate={() => onSelect(game.appid)} highlightOnFocus /> @@ -86,22 +92,35 @@ function GameGroup({ ); } -export function GameConfigurationSelector({ targets, runningGame, onSelect, onEnableAll, onResetAll }: Props) { +export function GameConfigurationSelector({ + targets, + runningGame, + onSelect, + onEnableAll, + onResetAll, + focusConfiguredToggle = false, + onConfiguredToggleFocused, +}: Props) { const sortGames = (games: GameTarget[]) => [...games].sort((a, b) => { if (a.appid === runningGame?.appid) return -1; if (b.appid === runningGame?.appid) return 1; return a.name.localeCompare(b.name); }); - const configuredGames = sortGames(targets.filter((game) => game.configured)); + const enabledGames = sortGames(targets.filter((game) => game.configured)); const availableGames = sortGames(targets.filter((game) => !game.configured)); - const configuredSteamGames = configuredGames.filter((game) => !game.nonSteam); - const configuredNonSteamGames = configuredGames.filter((game) => game.nonSteam); - const availableSteamGames = availableGames.filter((game) => !game.nonSteam); - const availableNonSteamGames = availableGames.filter((game) => game.nonSteam); - const [configuredCollapsed, toggleConfigured] = usePersistentCollapsed(CONFIGURED_COLLAPSED_KEY); - const [configuredNonSteamCollapsed, toggleConfiguredNonSteam] = usePersistentCollapsed(`${CONFIGURED_COLLAPSED_KEY}-non-steam`); + const [enabledCollapsed, toggleEnabled] = usePersistentCollapsed(ENABLED_COLLAPSED_KEY); const [availableCollapsed, toggleAvailable] = usePersistentCollapsed(AVAILABLE_COLLAPSED_KEY); - const [availableNonSteamCollapsed, toggleAvailableNonSteam] = usePersistentCollapsed(`${AVAILABLE_COLLAPSED_KEY}-non-steam`); + const enabledToggleRef = useRef<HTMLDivElement>(null); + + useEffect(() => { + if (!focusConfiguredToggle) return; + const frame = requestAnimationFrame(() => { + enabledToggleRef.current?.querySelector<HTMLElement>('[role="button"], button')?.focus(); + onConfiguredToggleFocused?.(); + }); + return () => cancelAnimationFrame(frame); + }, [enabledGames.length, focusConfiguredToggle, onConfiguredToggleFocused]); + const confirmResetAll = () => { showModal( <ConfirmModal @@ -113,11 +132,12 @@ export function GameConfigurationSelector({ targets, runningGame, onSelect, onEn />, ); }; + const confirmEnableAll = () => { showModal( <ConfirmModal strTitle="Enable all available games?" - strDescription="Create individual LSFG-VK profiles for every available game using the plugin defaults." + strDescription="Create individual LSFG-VK profiles for every available game using the plugin defaults. Flatpak targets will be provisioned as needed." strOKButtonText="Enable all" strCancelButtonText="Cancel" onOK={() => void onEnableAll()} @@ -133,6 +153,7 @@ export function GameConfigurationSelector({ targets, runningGame, onSelect, onEn .LSFG_GameGroupCollapseButton_Container > div > div > div > button, .LSFG_GameGroupCollapseButton_Container > div > div > div > div > button { height: 24px !important; + min-height: 24px !important; padding: 0 !important; display: flex !important; align-items: center !important; @@ -150,41 +171,28 @@ export function GameConfigurationSelector({ targets, runningGame, onSelect, onEn <Field label="No installed games" description="Steam has not reported any eligible games" /> </PanelSectionRow> )} - {availableGames.length > 0 && ( - <PanelSectionRow> - <ButtonItem layout="below" onClick={confirmEnableAll}> - Enable all available games - </ButtonItem> - </PanelSectionRow> - )} <GameGroup - title="LSFG-VK Enabled" - games={configuredSteamGames} - collapsed={configuredCollapsed} - onToggle={toggleConfigured} + title="Enabled" + games={enabledGames} + collapsed={enabledCollapsed} + onToggle={toggleEnabled} onSelect={onSelect} + toggleRef={enabledToggleRef} /> <GameGroup - title="LSFG-VK Enabled (Non-Steam)" - games={configuredNonSteamGames} - collapsed={configuredNonSteamCollapsed} - onToggle={toggleConfiguredNonSteam} - onSelect={onSelect} - /> - <GameGroup - title="Available games" - games={availableSteamGames} + title="Available" + games={availableGames} collapsed={availableCollapsed} onToggle={toggleAvailable} onSelect={onSelect} /> - <GameGroup - title="Available games (Non-Steam)" - games={availableNonSteamGames} - collapsed={availableNonSteamCollapsed} - onToggle={toggleAvailableNonSteam} - onSelect={onSelect} - /> + {availableGames.length > 0 && ( + <PanelSectionRow> + <ButtonItem layout="below" onClick={confirmEnableAll}> + Enable all available games + </ButtonItem> + </PanelSectionRow> + )} <PanelSectionRow> <ButtonItem layout="below" |
