mobile swappy controls

This commit is contained in:
legop3
2026-03-23 02:04:21 -04:00
parent f112f74db0
commit 626b48f777
8 changed files with 70 additions and 32 deletions
+20 -10
View File
@@ -3,8 +3,8 @@ import TelemetryPanel from './components/TelemetryPanel.jsx';
import ReplaySourcesPanel from './components/ReplaySourcesPanel.jsx';
import AlertFeed from './components/AlertFeed.jsx';
import MobileControls, {
MobileLeftColumn,
MobileRightColumn,
MobileActionsColumn,
MobileDriveColumn,
} from './components/MobileControls.jsx';
import {
ControlSystemProvider,
@@ -160,11 +160,11 @@ function MobileFeatureTabs({
);
}
function MobilePortraitLayout({ onOpenHelpOverlay }) {
function MobilePortraitLayout({ onOpenHelpOverlay, swapMobileControlColumns = false }) {
return (
<div className="flex flex-col gap-0.5">
<DriverVideoPanel layoutFormat="mobile-portrait" />
<MobileControls />
<MobileControls swapColumns={swapMobileControlColumns} />
<div className="grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)]">
<ReplaySourcesPanel panelId="replay-sources-mobile-portrait" />
<RoverQueuesPanel />
@@ -179,12 +179,18 @@ function MobilePortraitLayout({ onOpenHelpOverlay }) {
);
}
function MobileLandscapeLayout({ onOpenHelpOverlay }) {
function MobileLandscapeLayout({ onOpenHelpOverlay, swapMobileControlColumns = false }) {
const columnClass = 'self-start h-[min(100svh,32rem)]';
const firstColumn = swapMobileControlColumns
? <MobileDriveColumn layout="landscape" className={columnClass} />
: <MobileActionsColumn layout="landscape" className={columnClass} />;
const secondColumn = swapMobileControlColumns
? <MobileActionsColumn layout="landscape" className={columnClass} />
: <MobileDriveColumn layout="landscape" className={columnClass} />;
return (
<div className="flex flex-col gap-0.5">
<section className="grid min-h-screen grid-cols-[minmax(0,0.7fr)_minmax(0,2.1fr)_minmax(0,0.7fr)] gap-0.5">
<MobileLeftColumn layout="landscape" className={columnClass} />
{firstColumn}
<div>
<DriverVideoPanel layoutFormat="mobile-landscape" />
<div className="grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)]">
@@ -193,7 +199,7 @@ function MobileLandscapeLayout({ onOpenHelpOverlay }) {
</div>
{/* <TelemetryPanel /> */}
</div>
<MobileRightColumn layout="landscape" className={columnClass} />
{secondColumn}
</section>
<div className="flex flex-col gap-0.5 pb-0">
<MobileFeatureTabs
@@ -234,6 +240,10 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
status: helpStatus,
save: saveHelpSettings,
} = useSettingsNamespace('help', { showOnLoad: true });
const { value: pageSettings } = useSettingsNamespace('page', {
swapMobileControlColumns: false,
});
const swapMobileControlColumns = Boolean(pageSettings?.swapMobileControlColumns);
const [helpVisible, setHelpVisible] = useState(false);
useEffect(() => {
@@ -260,9 +270,9 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
isDesktop
? <DesktopLayout layout={layout} onOpenHelpOverlay={openHelp} />
: layout === 'mobile-landscape'
? <MobileLandscapeLayout onOpenHelpOverlay={openHelp} />
: <MobilePortraitLayout onOpenHelpOverlay={openHelp} />,
[isDesktop, layout, openHelp],
? <MobileLandscapeLayout onOpenHelpOverlay={openHelp} swapMobileControlColumns={swapMobileControlColumns} />
: <MobilePortraitLayout onOpenHelpOverlay={openHelp} swapMobileControlColumns={swapMobileControlColumns} />,
[isDesktop, layout, openHelp, swapMobileControlColumns],
);
return (
+13 -7
View File
@@ -224,7 +224,7 @@ function MobileAuxButton({ id, label, values, color, disabled, onPress, onReleas
);
}
function MobileLeftColumnContent({ layout }) {
function MobileActionsColumnContent({ layout }) {
const {
state: { roverId, camera, horn },
pipeline,
@@ -347,15 +347,15 @@ function MobileLeftColumnContent({ layout }) {
);
}
export function MobileLeftColumn({ layout, className = '' }) {
export function MobileActionsColumn({ layout, className = '' }) {
return (
<div className={`flex flex-col gap-0.5 ${className}`.trim()}>
<MobileLeftColumnContent layout={layout} />
<MobileActionsColumnContent layout={layout} />
</div>
);
}
export function MobileRightColumn({ layout, className = '' }) {
export function MobileDriveColumn({ layout, className = '' }) {
return (
<div className={`flex flex-col gap-0.5 ${className}`.trim()}>
<MobileJoystickPanel layout={layout === 'landscape' ? 'landscape' : 'portrait'} />
@@ -363,13 +363,19 @@ export function MobileRightColumn({ layout, className = '' }) {
);
}
export default function MobilePortraitControls() {
export default function MobilePortraitControls({ swapColumns = false }) {
const columnHeight = 'h-[min(60svh,24rem)]';
const firstColumn = swapColumns
? <MobileDriveColumn layout="portrait" className={columnHeight} />
: <MobileActionsColumn layout="portrait" className={columnHeight} />;
const secondColumn = swapColumns
? <MobileActionsColumn layout="portrait" className={columnHeight} />
: <MobileDriveColumn layout="portrait" className={columnHeight} />;
return (
<section className="panel">
<div className="grid grid-cols-2 gap-0.5 items-stretch">
<MobileLeftColumn layout="portrait" className={columnHeight} />
<MobileRightColumn layout="portrait" className={columnHeight} />
{firstColumn}
{secondColumn}
</div>
</section>
);
+19
View File
@@ -32,9 +32,11 @@ export default function SettingsPanel() {
const { value: pageSettings, save: savePageSettings } = useSettingsNamespace('page', {
hudMapDesktop: false,
connectionTransport: 'websocket',
swapMobileControlColumns: false,
});
const { value: audioSettings, save: saveAudioSettings } = useSettingsNamespace('audio', AUDIO_SETTINGS_DEFAULTS);
const connectionTransport = pageSettings?.connectionTransport || 'websocket';
const swapMobileControlColumns = Boolean(pageSettings?.swapMobileControlColumns);
const masterVolume = Number.isFinite(audioSettings?.masterVolume) ? audioSettings.masterVolume : AUDIO_SETTINGS_DEFAULTS.masterVolume;
const alertVolume = Number.isFinite(audioSettings?.alertVolume) ? audioSettings.alertVolume : AUDIO_SETTINGS_DEFAULTS.alertVolume;
const roverVolume = Number.isFinite(audioSettings?.roverVolume) ? audioSettings.roverVolume : AUDIO_SETTINGS_DEFAULTS.roverVolume;
@@ -80,6 +82,11 @@ export default function SettingsPanel() {
const checked = Boolean(event.target.checked);
saveAudioSettings((current) => ({ ...(current ?? {}), mainBrushDuckEnabled: checked }));
};
const handleSwapMobileControlColumns = (event) => {
const checked = Boolean(event.target.checked);
savePageSettings((current) => ({ ...(current ?? {}), swapMobileControlColumns: checked }));
};
return (
<Tabs defaultTab="keybindings">
<TabList>
@@ -114,6 +121,18 @@ export default function SettingsPanel() {
</label>
<p className="text-xs text-slate-500">Mobile HUD keeps the map on by default.</p>
</section>
<section className="panel-section space-y-0.5 text-sm">
<p className="text-slate-400">Mobile controls</p>
<label className="flex items-center gap-0.5 text-slate-200">
<input
type="checkbox"
className="accent-emerald-500"
checked={swapMobileControlColumns}
onChange={handleSwapMobileControlColumns}
/>
<span>Swap control columns (put joystick on the left)</span>
</label>
</section>
<section className="panel-section space-y-0.5 text-sm">
<p className="text-slate-400">Audio</p>
<label className="grid gap-0.5 text-slate-200">
+2 -2
View File
@@ -124,7 +124,7 @@ export const HELP_CONTENT = {
title: 'Driving the rover',
items: [
{ segments: ['Press the "Start Driving" button onscreen to put the rover into driving mode.'] },
'Look below the rover video. Use the joystick on the right to move the rover, and hold the buttons on the left to run the aux motors.'
'Look below the rover video. Use the joystick column to move the rover, and hold the aux buttons in the other control column.'
],
},
{
@@ -154,7 +154,7 @@ export const HELP_CONTENT = {
title: 'Driving the rover',
items: [
{ segments: ['Press the "Start Driving" button, or the "Drive" button to put the rover into driving mode.'] },
'Use the joystick to the right of the video feed to move the rover, and hold the buttons on the left to run the aux motors.'
'Use the joystick column beside the video feed to move the rover, and hold the aux buttons in the other control column.'
],
},
{