summaryrefslogtreecommitdiff
path: root/src/components/GameConfigurationSelector.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/GameConfigurationSelector.tsx')
-rw-r--r--src/components/GameConfigurationSelector.tsx102
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"