Skip to content

Commit 775588b

Browse files
committed
feat: add "open" action to plugins settings
This allows opening plugins even if they are hidden in the quick access list. This solves the use case where plugins are hidden from the quick access list because access to their panels is not used very often. For example, plugin "ProtonDB Badges" is fire-configure-once-and-forget, so it makes sense to hide it so it doesn't waste space and attention. But this doesn't mean the user will never want to access hidden plugin panels to e.g. briefly change configuration. Currently the only way to access the plugin content panel is to unhide the plugin, access it in the quick access list, and then hide it again. Resolves #734
1 parent 79bb62a commit 775588b

File tree

4 files changed

+28
-13
lines changed

4 files changed

+28
-13
lines changed

backend/decky_loader/locales/en-US.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
"freeze": "Freeze updates",
103103
"hide": "Quick access: Hide",
104104
"no_plugin": "No plugins installed!",
105+
"open": "Open",
105106
"plugin_actions": "Plugin Actions",
106107
"reinstall": "Reinstall",
107108
"reload": "Reload",

frontend/src/components/PluginView.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ const PluginView: FC = () => {
2626

2727
if (activePlugin) {
2828
return (
29-
<Focusable onCancelButton={closeActivePlugin}>
29+
<Focusable
30+
onCancelButton={closeActivePlugin}
31+
// Needed to focus inside the panel when opening plugin from settings menu
32+
// Doesn't seem to do any harm (since this seems to need focus in normal cases too) so I made this unconditional
33+
autoFocus
34+
>
3035
<TitleView />
3136
<div style={{ height: '100%', paddingTop: '16px' }}>
3237
<ErrorBoundary>{(visible || activePlugin.alwaysRender) && activePlugin.content}</ErrorBoundary>

frontend/src/components/TitleView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const TitleView: FC = () => {
5858
>
5959
<FaArrowLeft style={{ marginTop: '-4px', display: 'block' }} />
6060
</DialogButton>
61-
{activePlugin?.titleView || <div style={{ flex: 0.9 }}>{activePlugin.name}</div>}
61+
{activePlugin.titleView || <div style={{ flex: 0.9 }}>{activePlugin.name}</div>}
6262
</Focusable>
6363
);
6464
};

frontend/src/components/settings/pages/plugin_list/index.tsx

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import {
55
GamepadEvent,
66
Menu,
77
MenuItem,
8+
Navigation,
9+
QuickAccessTab,
810
ReorderableEntry,
911
ReorderableList,
1012
showContextMenu,
1113
} from '@decky/ui';
12-
import { useEffect, useState } from 'react';
14+
import { useEffect, useMemo } from 'react';
1315
import { useTranslation } from 'react-i18next';
1416
import { FaDownload, FaEllipsisH, FaRecycle } from 'react-icons/fa';
1517

@@ -41,6 +43,7 @@ type PluginTableData = PluginData & {
4143
hidden: boolean;
4244
onHide(): void;
4345
onShow(): void;
46+
onOpen(): void;
4447
isDeveloper: boolean;
4548
};
4649

@@ -54,11 +57,13 @@ function PluginInteractables(props: { entry: ReorderableEntry<PluginTableData> }
5457
return null;
5558
}
5659

57-
const { name, update, version, onHide, onShow, hidden, onFreeze, onUnfreeze, frozen, isDeveloper } = props.entry.data;
60+
const { name, update, version, onHide, onShow, hidden, onFreeze, onUnfreeze, frozen, isDeveloper, onOpen } =
61+
props.entry.data;
5862

5963
const showCtxMenu = (e: MouseEvent | GamepadEvent) => {
6064
showContextMenu(
6165
<Menu label={t('PluginListIndex.plugin_actions')}>
66+
<MenuItem onSelected={onOpen}>{t('PluginListIndex.open')}</MenuItem>
6267
<MenuItem
6368
onSelected={async () => {
6469
try {
@@ -147,7 +152,8 @@ type PluginData = {
147152
};
148153

149154
export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
150-
const { plugins, updates, pluginOrder, setPluginOrder, frozenPlugins, hiddenPlugins } = useDeckyState();
155+
const { plugins, updates, pluginOrder, setPluginOrder, frozenPlugins, hiddenPlugins, setActivePlugin } =
156+
useDeckyState();
151157
const [_, setPluginOrderSetting] = useSetting<string[]>(
152158
'pluginOrder',
153159
plugins.map((plugin) => plugin.name),
@@ -158,13 +164,12 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
158164
DeckyPluginLoader.checkPluginUpdates();
159165
}, []);
160166

161-
const [pluginEntries, setPluginEntries] = useState<ReorderableEntry<PluginTableData>[]>([]);
162-
const hiddenPluginsService = DeckyPluginLoader.hiddenPluginsService;
163-
const frozenPluginsService = DeckyPluginLoader.frozenPluginsService;
164-
165-
useEffect(() => {
166-
setPluginEntries(
167+
const pluginEntries = useMemo(
168+
() =>
167169
plugins.map(({ name, version }) => {
170+
const hiddenPluginsService = DeckyPluginLoader.hiddenPluginsService;
171+
const frozenPluginsService = DeckyPluginLoader.frozenPluginsService;
172+
168173
const frozen = frozenPlugins.includes(name);
169174
const hidden = hiddenPlugins.includes(name);
170175

@@ -182,11 +187,15 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
182187
onUnfreeze: () => frozenPluginsService.update(frozenPlugins.filter((pluginName) => name !== pluginName)),
183188
onHide: () => hiddenPluginsService.update([...hiddenPlugins, name]),
184189
onShow: () => hiddenPluginsService.update(hiddenPlugins.filter((pluginName) => name !== pluginName)),
190+
onOpen: () => {
191+
setActivePlugin(name);
192+
Navigation.OpenQuickAccessMenu(QuickAccessTab.Decky);
193+
},
185194
},
186195
};
187196
}),
188-
);
189-
}, [plugins, updates, hiddenPlugins]);
197+
[plugins, updates, frozenPlugins, hiddenPlugins, setActivePlugin],
198+
);
190199

191200
if (plugins.length === 0) {
192201
return (

0 commit comments

Comments
 (0)