fix: Fixed issues on windows
feat: Implemented mouse and gamepad automatic switching fix: Made touch screen work better on the steam deck
This commit is contained in:
parent
e4df8fb9fb
commit
b4a89385d0
24 changed files with 334 additions and 137 deletions
|
|
@ -9,6 +9,7 @@ import
|
|||
Search,
|
||||
Power,
|
||||
OctagonAlert,
|
||||
Maximize,
|
||||
} from "lucide-react";
|
||||
import
|
||||
{
|
||||
|
|
@ -19,6 +20,7 @@ import { useMutation } from "@tanstack/react-query";
|
|||
import
|
||||
{
|
||||
FocusContext,
|
||||
FocusDetails,
|
||||
useFocusable,
|
||||
} from "@noriginmedia/norigin-spatial-navigation";
|
||||
import classNames from "classnames";
|
||||
|
|
@ -79,9 +81,14 @@ function HomeList (data: {
|
|||
preferredChildFocusKey: `${data.selectedFilter}-list`
|
||||
});
|
||||
|
||||
const handleNodeFocus = (node: HTMLElement) =>
|
||||
const handleNodeFocus = (id: string, node: HTMLElement, details: FocusDetails) =>
|
||||
{
|
||||
node.scrollIntoView({ inline: 'center', behavior: initFocus ? 'smooth' : 'instant' });
|
||||
const isMounseEvent = details.nativeEvent instanceof MouseEvent;
|
||||
if (!isMounseEvent)
|
||||
{
|
||||
node?.scrollIntoView({ inline: 'center', behavior: initFocus ? 'smooth' : 'instant' });
|
||||
}
|
||||
|
||||
setInitFocus(true);
|
||||
};
|
||||
|
||||
|
|
@ -101,7 +108,7 @@ function HomeList (data: {
|
|||
(ref.current as HTMLElement)?.scrollBy({
|
||||
top: 0,
|
||||
left: deltaY,
|
||||
behavior: 'auto'
|
||||
behavior: 'instant'
|
||||
});
|
||||
|
||||
} else
|
||||
|
|
@ -109,7 +116,7 @@ function HomeList (data: {
|
|||
(ref.current as HTMLElement)?.scrollBy({
|
||||
top: 0,
|
||||
left: deltaY,
|
||||
behavior: 'auto'
|
||||
behavior: 'instant'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -145,7 +152,9 @@ function MainMenu (data: {})
|
|||
<ul
|
||||
ref={ref}
|
||||
save-child-focus="session"
|
||||
className="flex items-center justify-center gap-3"
|
||||
className={twMerge("md:relative flex items-center justify-center md:gap-3",
|
||||
"sm:gap-1 sm:absolute sm:bottom-2 sm:left-0 sm:right-0"
|
||||
)}
|
||||
>
|
||||
<FocusContext.Provider value={focusKey}>
|
||||
<CircleIcon
|
||||
|
|
@ -199,7 +208,7 @@ function CircleIcon (data: {
|
|||
onClick={data.action}
|
||||
className={twMerge(
|
||||
`menu-icon text-base-300 md:w-20 md:h-20 rounded-full flex items-center justify-center drop-shadow-lg cursor-pointer transition-all`,
|
||||
'sm:w-14 sm:h-14',
|
||||
'sm:w-14 sm:h-10',
|
||||
typeClasses[data.type ?? "none"], classNames(
|
||||
{
|
||||
"focus ring-7 ring-primary drop-shadow-2xl animate-scale": focused,
|
||||
|
|
@ -263,18 +272,19 @@ export default function ConsoleHomeUI ()
|
|||
<FocusContext.Provider value={focusKey}>
|
||||
<div className="px-3 w-full pt-2">
|
||||
<HeaderUI buttons={[
|
||||
{ id: "fullscreen", icon: <Maximize />, action: () => document.documentElement.requestFullscreen() },
|
||||
{ id: "search", icon: <Search /> },
|
||||
{ id: "power-button", icon: <Power />, external: true, action: () => closeMutation.mutate() }
|
||||
]} />
|
||||
</div>
|
||||
<div className="flex w-full flex-col grow justify-evenly">
|
||||
<div className="flex w-full flex-col grow justify-evenly md:pt-0">
|
||||
<FilterUI
|
||||
id="home"
|
||||
options={filters}
|
||||
selected={filter ? filter : 'games'}
|
||||
setSelected={setFilter}
|
||||
/>
|
||||
<div className="-mb-1">
|
||||
<div className="md:-mb-1">
|
||||
<HomeList
|
||||
selectedFilter={filter}
|
||||
/>
|
||||
|
|
@ -283,7 +293,9 @@ export default function ConsoleHomeUI ()
|
|||
<MainMenu />
|
||||
</div>
|
||||
</div>
|
||||
<footer className="px-2 pb-2 flex items-center justify-between h-12">
|
||||
<footer className={twMerge("md:relative px-2 md:pb-2 flex items-center justify-between h-12",
|
||||
"sm:absolute bottom-0 left-0 right-0"
|
||||
)}>
|
||||
<div className="flex gap-2 text-sm">
|
||||
</div>
|
||||
<Shortcuts shortcuts={shortcuts} />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { FocusContext, useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
||||
import { Block, createFileRoute, useBlocker } from '@tanstack/react-router';
|
||||
import { Block, createFileRoute } from '@tanstack/react-router';
|
||||
import DownloadDirectoryOption from '@/mainview/components/options/DownloadDirectoryOption';
|
||||
import { useIsMutating, useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { changeDownloadsMutation, downloadDrivesQuery } from '@/mainview/scripts/queries';
|
||||
|
|
@ -7,12 +7,12 @@ import { DownloadsDrive } from '@/shared/constants';
|
|||
import prettyBytes from 'pretty-bytes';
|
||||
import classNames from 'classnames';
|
||||
import { GamePadButtonCode, Shortcut, useShortcuts } from '@/mainview/scripts/shortcuts';
|
||||
import { Download, FolderOpen, HardDrive, Usb } from 'lucide-react';
|
||||
import { Download, FolderOpen, HardDrive, Save, Usb } from 'lucide-react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import { OptionSpace } from '@/mainview/components/options/OptionSpace';
|
||||
import data from '@emulators';
|
||||
import { Button } from '@/mainview/components/options/Button';
|
||||
import { systemApi } from '@/mainview/scripts/clientApi';
|
||||
import useActiveControl from '@/mainview/scripts/gamepads';
|
||||
|
||||
export const Route = createFileRoute('/settings/directories')({
|
||||
component: RouteComponent,
|
||||
|
|
@ -20,46 +20,54 @@ export const Route = createFileRoute('/settings/directories')({
|
|||
|
||||
function DriveComponent (data: { drive: DownloadsDrive; downloadsSize: number; refetchDrives: () => void; })
|
||||
{
|
||||
const { ref, focused, focusKey } = useFocusable({ focusKey: data.drive.device });
|
||||
const { ref, focused, focusKey } = useFocusable({
|
||||
focusKey: data.drive.device,
|
||||
onFocus: () => (ref.current as HTMLElement)?.scrollIntoView({ block: 'nearest', behavior: 'smooth' })
|
||||
});
|
||||
const isMoving = useIsMutating(changeDownloadsMutation);
|
||||
const usedWithoutDownlods = data.drive.used - (data.drive.isCurrentlyUsed ? data.downloadsSize : 0);
|
||||
const usedPercent = usedWithoutDownlods / data.drive.size;
|
||||
const usedPercentRaw = data.drive.used / data.drive.size;
|
||||
const changeDownloads = useMutation({ ...changeDownloadsMutation, onSuccess: data.refetchDrives }); data.drive.unusableReason;
|
||||
const shortcuts: Shortcut[] = [];
|
||||
if (!data.drive.unusableReason && isMoving <= 0)
|
||||
const valid = !data.drive.unusableReason && isMoving <= 0;
|
||||
const handleAction = () => changeDownloads.mutate(data.drive.mountPoint);
|
||||
if (valid)
|
||||
{
|
||||
shortcuts.push({ label: "Move Downloads", button: GamePadButtonCode.A, action: () => changeDownloads.mutate(data.drive.mountPoint) });
|
||||
shortcuts.push({ label: "Move Downloads", button: GamePadButtonCode.A, action: handleAction });
|
||||
}
|
||||
useShortcuts(focusKey, () => shortcuts, [shortcuts]);
|
||||
const { isMouse } = useActiveControl();
|
||||
|
||||
|
||||
return <li ref={ref} className={twMerge('flex flex-col p-4 bg-base-300 rounded-2xl gap-1',
|
||||
return <li ref={ref} className={twMerge('flex flex-row p-4 bg-base-300 rounded-2xl gap-1 items-end',
|
||||
classNames({
|
||||
"ring-7": focused,
|
||||
"border-dashed border-primary border-7": data.drive.isCurrentlyUsed,
|
||||
"border-dashed border-primary border-4": data.drive.isCurrentlyUsed,
|
||||
"border-solid": data.drive.unusableReason === 'already_used',
|
||||
"ring-error": data.drive.unusableReason === 'not_enough_space',
|
||||
}))}>
|
||||
<div className='flex gap-2 font-semibold'>{data.drive.isRemovable ? <Usb /> : <HardDrive />}{data.drive.label}</div>
|
||||
<small className='opacity-60'>{data.drive.mountPoint}</small>
|
||||
<div className='flex gap-2'>
|
||||
{prettyBytes(data.drive.size - data.drive.used)} Free
|
||||
{data.drive.unusableReason === 'not_enough_space' && <p className='text-error'>(Not Enough Space)</p>}
|
||||
{data.drive.unusableReason === 'already_used' && <p>(Currently Used)</p>}
|
||||
{data.drive.unusableReason !== 'already_used' && data.drive.isCurrentlyUsed && <p className='opacity-60'>(Custom Path)</p>}
|
||||
</div>
|
||||
<div className='flex flex-col grow gap-1'>
|
||||
<div className='flex gap-2 font-semibold'>{data.drive.isRemovable ? <Usb /> : <HardDrive />}{data.drive.label}</div>
|
||||
<small className='opacity-60'>{data.drive.mountPoint}</small>
|
||||
<div className='flex gap-2'>
|
||||
{prettyBytes(data.drive.size - data.drive.used)} Free
|
||||
{data.drive.unusableReason === 'not_enough_space' && <p className='text-error'>(Not Enough Space)</p>}
|
||||
{data.drive.unusableReason === 'already_used' && <p>(Currently Used)</p>}
|
||||
{data.drive.unusableReason !== 'already_used' && data.drive.isCurrentlyUsed && <p className='opacity-60'>(Custom Path)</p>}
|
||||
</div>
|
||||
|
||||
<div className={twMerge("progress", classNames({
|
||||
"progress-warning": usedPercent > 0.8,
|
||||
"progress-error": data.drive.unusableReason === 'not_enough_space',
|
||||
}))}>
|
||||
<div className={twMerge('h-full bg-primary', classNames({
|
||||
"bg-warning": usedPercent > 0.8,
|
||||
"bg-error": data.drive.unusableReason === 'not_enough_space',
|
||||
}))} style={{ width: usedPercent.toLocaleString('en-US', { style: 'percent' }) }}></div>
|
||||
{!!data.drive.isCurrentlyUsed && <div className="h-full bg-base-content" style={{ width: usedPercentRaw.toLocaleString('en-US', { style: 'percent' }) }}></div>}
|
||||
<div className={twMerge("progress", classNames({
|
||||
"progress-warning": usedPercent > 0.8,
|
||||
"progress-error": data.drive.unusableReason === 'not_enough_space',
|
||||
}))}>
|
||||
<div className={twMerge('h-full bg-primary', classNames({
|
||||
"bg-warning": usedPercent > 0.8,
|
||||
"bg-error": data.drive.unusableReason === 'not_enough_space',
|
||||
}))} style={{ width: usedPercent.toLocaleString('en-US', { style: 'percent' }) }}></div>
|
||||
{!!data.drive.isCurrentlyUsed && <div className="h-full bg-base-content" style={{ width: usedPercentRaw.toLocaleString('en-US', { style: 'percent' }) }}></div>}
|
||||
</div>
|
||||
</div>
|
||||
{valid && isMouse && <Button type="button" className='btn-circle' onAction={handleAction} id={`${data.drive.mountPoint}-select`}><Save /></Button>}
|
||||
</li>;
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +85,7 @@ function RouteComponent ()
|
|||
<Block shouldBlockFn={() => isMoving} withResolver={false} />
|
||||
<ul ref={ref} className="list rounded-box gap-2">
|
||||
<div className="divider text-2xl mt-0 md:mt-4">
|
||||
<Download className='size-16' /> Downloads ({drives?.downloadsSize ? prettyBytes(drives?.downloadsSize) : '?'})
|
||||
<Download className='size-16' /> Downloads ({drives?.downloadsSize ? prettyBytes(drives?.downloadsSize) : <span className="loading loading-spinner loading-lg size-6"></span>})
|
||||
</div>
|
||||
<ul className='p-2 grid grid-cols-2 gap-3'>
|
||||
{drives?.drives.filter(d => d.mountPoint).map(d => <DriveComponent refetchDrives={refetch} downloadsSize={drives.downloadsSize} drive={d} />)}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import { PopSource } from "../../scripts/spatialNavigation";
|
|||
import { Router } from "../..";
|
||||
import { GamePadButtonCode, useShortcutContext, useShortcuts } from "@/mainview/scripts/shortcuts";
|
||||
import Shortcuts from "@/mainview/components/Shortcuts";
|
||||
import useActiveControl from "@/mainview/scripts/gamepads";
|
||||
|
||||
export const Route = createFileRoute("/settings")({
|
||||
component: SettingsUI,
|
||||
|
|
@ -68,6 +69,7 @@ function MenuItem (data: {
|
|||
? handleNonFocusSelect
|
||||
: undefined,
|
||||
});
|
||||
const { isMouse } = useActiveControl();
|
||||
return (
|
||||
<li
|
||||
ref={ref}
|
||||
|
|
@ -81,13 +83,13 @@ function MenuItem (data: {
|
|||
"group rounded-full p-3 pl-5 text-base-content/80",
|
||||
classNames({
|
||||
"bg-primary text-primary-content": acitve,
|
||||
"font-semibold ring-7 ring-primary-content": focused,
|
||||
"font-semibold ring-7 ring-primary-content": focused && !isMouse,
|
||||
"bg-secondary text-secondary-content ring-primary": data.return && focused,
|
||||
}),
|
||||
data.linkClassName,
|
||||
)}
|
||||
>
|
||||
<div className={twMerge("flex gap-2 items-center transition-all group-hover:scale-110", classNames({
|
||||
<div className={twMerge("flex gap-2 items-center transition-all", classNames({
|
||||
"scale-110": focused || acitve
|
||||
}))}>
|
||||
{data.icon}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue