Wire MENU page-menu + HRZN HDG — last dead display keys now functional
MENU (bezel hard key) now opens a G1000-style PAGE MENU (Invert / Store / Delete flight plan) instead of only mirroring the sim command. HRZN HDG draws heading reference marks (N/E/S/W + ticks) along the attitude horizon, toggled from the PFD submenu. With TRAFFIC/NEXRAD/PROFILE/PATHWAY/APTSIGNS already wired, every softkey now does something; only ENT remains a pure sim-command mirror. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+22
-4
@@ -67,7 +67,7 @@ export default function App() {
|
||||
const [win, setWin] = useState(null);
|
||||
const toggleWin = (id) => setWin((w) => (w === id ? null : id));
|
||||
const nrst = win === 'nrst', tmr = win === 'tmr', dme = win === 'dme', alerts = win === 'alerts';
|
||||
const fpl = win === 'fpl', dto = win === 'dto', proc = win === 'proc';
|
||||
const fpl = win === 'fpl', dto = win === 'dto', proc = win === 'proc', menu = win === 'menu';
|
||||
// MFD map mode (base layer + overlays), switched via the Map-Opt softkeys.
|
||||
const [mapMode, setMapMode] = useState({ base: 'topo' });
|
||||
// VNAV profile control (FPL VNAV keys + Direct-To descent): enabled gates the
|
||||
@@ -76,7 +76,7 @@ export default function App() {
|
||||
const [vnavCfg, setVnavCfg] = useState({ enabled: true, fpa: 3, offsetNm: 0 });
|
||||
// Synthetic-vision display options (PFD submenu): PATHWAY draws the flight-plan
|
||||
// route on the 3D terrain; APTSIGNS shows runway/airport labels.
|
||||
const [svtOpts, setSvtOpts] = useState({ pathway: true, aptSigns: true });
|
||||
const [svtOpts, setSvtOpts] = useState({ pathway: true, aptSigns: true, hrznHdg: false });
|
||||
// Altimeter barometric units (false = inHg, true = hectopascal) — PFD ALT UNIT softkey.
|
||||
const [baroHpa, setBaroHpa] = useState(false);
|
||||
// Barometric minimums (set in TMR/REF) — shown on the PFD altimeter as BARO MIN.
|
||||
@@ -102,6 +102,24 @@ export default function App() {
|
||||
<>
|
||||
{dto && <DirectTo xp={xp} onClose={() => setWin(null)} vnav={vnavCfg} onVnav={setVnavCfg} />}
|
||||
{proc && <Proc xp={xp} onClose={() => setWin(null)} />}
|
||||
{menu && (() => {
|
||||
const wps = xp.flightPlan?.waypoints || [];
|
||||
const act = (fn) => { fn(); setWin(null); };
|
||||
const item = (label, on, dis) => <button className="proc-menu-i" disabled={dis} onClick={() => act(on)}>{label}</button>;
|
||||
return (
|
||||
<div className="gwin-backdrop" onClick={() => setWin(null)}>
|
||||
<div className="dlg proc menu" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="dlg-head">PAGE MENU</div>
|
||||
<div className="proc-menu">
|
||||
{item('INVERT FLIGHT PLAN', () => xp.fp.set({ name: 'ACTIVE', waypoints: wps.slice().reverse(), activeLeg: 1 }), wps.length < 2)}
|
||||
{item('STORE FLIGHT PLAN', () => xp.fp.export('WEBFPL'), wps.length < 2)}
|
||||
{item('DELETE FLIGHT PLAN', () => xp.fp.clear(), wps.length < 1)}
|
||||
{item('CANCEL', () => {})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
{fpl && (
|
||||
<div className="gwin-backdrop" onClick={() => setWin(null)}>
|
||||
<div onClick={(e) => e.stopPropagation()}><FplPage xp={xp} onClose={() => setWin(null)} vnav={vnavCfg} onVnav={setVnavCfg} /></div>
|
||||
@@ -146,7 +164,7 @@ export default function App() {
|
||||
inset={inset} onSetInset={setInset} insetMode={insetMode} onInsetMode={setInsetMode}
|
||||
nrst={nrst} onToggleNrst={() => toggleWin('nrst')} onDirect={() => toggleWin('dto')}
|
||||
tmr={tmr} onToggleTmr={() => toggleWin('tmr')} dme={dme} onToggleDme={() => toggleWin('dme')}
|
||||
alerts={alerts} onToggleAlerts={() => toggleWin('alerts')} onProc={() => toggleWin('proc')} onFpl={() => toggleWin('fpl')} onClr={() => setWin(null)}
|
||||
alerts={alerts} onToggleAlerts={() => toggleWin('alerts')} onProc={() => toggleWin('proc')} onFpl={() => toggleWin('fpl')} onMenu={() => toggleWin('menu')} onClr={() => setWin(null)}
|
||||
altHpa={baroHpa} onAltUnit={setBaroHpa} obs={obs} onObs={() => setObs((v) => !v)}>
|
||||
<PFD xp={xp} values={xp.values} command={xp.command} connected={xp.xpConnected} svt={svt3d} inset={inset} insetMode={insetMode} nrst={nrst} onCloseNrst={() => setWin(null)}
|
||||
tmr={tmr} onCloseTmr={() => setWin(null)} dme={dme} onCloseDme={() => setWin(null)}
|
||||
@@ -156,7 +174,7 @@ export default function App() {
|
||||
</Bezel>
|
||||
)}
|
||||
{tab === 'mfd' && (
|
||||
<Bezel variant="mfd" xp={xp} knobMode={knobMode} mapMode={mapMode} onMapMode={setMapMode} onDirect={() => toggleWin('dto')} onProc={() => toggleWin('proc')} onFms={cycleMfd} onFpl={() => setMfdPage('fpl')} onClr={() => setWin(null)}>
|
||||
<Bezel variant="mfd" xp={xp} knobMode={knobMode} mapMode={mapMode} onMapMode={setMapMode} onDirect={() => toggleWin('dto')} onProc={() => toggleWin('proc')} onFms={cycleMfd} onFpl={() => setMfdPage('fpl')} onMenu={() => toggleWin('menu')} onClr={() => setWin(null)}>
|
||||
<MFD values={xp.values} flightPlan={xp.flightPlan} fp={xp.fp} mapMode={mapMode} page={mfdPage} onCycle={cycleMfd} xp={xp} vnav={vnavCfg} onVnav={setVnavCfg} />
|
||||
{dialogs}
|
||||
</Bezel>
|
||||
|
||||
@@ -34,7 +34,7 @@ const MFD_MENU = {
|
||||
};
|
||||
const KG_PER_GAL = 2.72; // fuel totalizer steps in US gallons (matches the EIS readout)
|
||||
|
||||
export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, svtOpts, onSvtOpt, inset, onSetInset, insetMode, onInsetMode, nrst, onToggleNrst, tmr, onToggleTmr, dme, onToggleDme, alerts, onToggleAlerts, onDirect, onProc, onFpl, onClr, onFms, mapMode, onMapMode, altHpa, onAltUnit, obs, onObs, knobMode = 'arrows', children }) {
|
||||
export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, svtOpts, onSvtOpt, inset, onSetInset, insetMode, onInsetMode, nrst, onToggleNrst, tmr, onToggleTmr, dme, onToggleDme, alerts, onToggleAlerts, onDirect, onProc, onFpl, onClr, onMenu, onFms, mapMode, onMapMode, altHpa, onAltUnit, obs, onObs, knobMode = 'arrows', children }) {
|
||||
const u = variant === 'mfd' ? 'mfd' : 'pfd'; // command prefix
|
||||
const fire = (suffix) => xp && xp.command(`${u}_${suffix}`);
|
||||
const [page, setPage] = useState('root'); // softkey menu page
|
||||
@@ -85,6 +85,7 @@ export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, svtOpts
|
||||
else if (label === 'SYN TERR') onToggleSvt && onToggleSvt();
|
||||
else if (label === 'PATHWAY') onSvtOpt && onSvtOpt((o) => ({ ...o, pathway: !o.pathway })); // route on the 3D terrain
|
||||
else if (label === 'APTSIGNS') onSvtOpt && onSvtOpt((o) => ({ ...o, aptSigns: !o.aptSigns })); // runway/airport labels in SVT
|
||||
else if (label === 'HRZN HDG') onSvtOpt && onSvtOpt((o) => ({ ...o, hrznHdg: !o.hrznHdg })); // heading marks on the horizon
|
||||
else if (label === 'ALT UNIT') setPage('altunit');
|
||||
else if (label === 'IN') { onAltUnit && onAltUnit(false); setPage('pfd'); }
|
||||
else if (label === 'HPA') { onAltUnit && onAltUnit(true); setPage('pfd'); }
|
||||
@@ -124,6 +125,7 @@ export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, svtOpts
|
||||
|| (label === 'AIRSPACE' && mapMode?.airspace) || (label === 'TRAFFIC' && mapMode?.traffic) || (label === 'NEXRAD' && mapMode?.nexrad)
|
||||
|| (label === 'PROFILE' && mapMode?.profile);
|
||||
return (label === 'SYN TERR' && svt3d) || (label === 'PATHWAY' && svtOpts?.pathway) || (label === 'APTSIGNS' && svtOpts?.aptSigns)
|
||||
|| (label === 'HRZN HDG' && svtOpts?.hrznHdg)
|
||||
|| (label === 'INSET' && inset) || (label === 'NRST' && nrst) || (label === 'TMR/REF' && tmr)
|
||||
|| (label === 'DME' && dme) || (label === 'OBS' && obs) || (label === 'CAUTION' && (alerts || hasAlerts))
|
||||
|| (label === 'STBY' && xpdrMode === 1) || (label === 'ON' && xpdrMode === 2) || (label === 'ALT' && xpdrMode === 3)
|
||||
@@ -182,7 +184,7 @@ export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, svtOpts
|
||||
<Knob label="RANGE" sub="PUSH PAN" joy fire={fire} mode={knobMode}
|
||||
outer={['range_up', 'range_down']} push="pan_push" pan />
|
||||
<div className="bezel-grid">
|
||||
<BtnG fire={fire} mode={knobMode} cmd="direct" onClick={onDirect}>D→</BtnG><BtnG fire={fire} mode={knobMode} cmd="menu">MENU</BtnG>
|
||||
<BtnG fire={fire} mode={knobMode} cmd="direct" onClick={onDirect}>D→</BtnG><BtnG fire={fire} mode={knobMode} cmd="menu" onClick={onMenu}>MENU</BtnG>
|
||||
<BtnG fire={fire} mode={knobMode} cmd="fpl" onClick={onFpl}>FPL</BtnG><BtnG fire={fire} mode={knobMode} cmd="proc" onClick={onProc}>PROC</BtnG>
|
||||
<BtnG fire={fire} mode={knobMode} cmd="clr" onClick={onClr}>CLR</BtnG><BtnG fire={fire} mode={knobMode} cmd="ent">ENT</BtnG>
|
||||
</div>
|
||||
|
||||
@@ -186,7 +186,7 @@ export default function PFD({ xp, values: V, command, connected = true, svt = tr
|
||||
<RadioBar V={V} onTune={command ? setTune : null} />
|
||||
{nav && <NavStatus nav={nav} />}
|
||||
{vnav && <VnavBox vnav={vnav} />}
|
||||
<Attitude V={V} svt={svt} />
|
||||
<Attitude V={V} svt={svt} hrznHdg={svtOpts?.hrznHdg} />
|
||||
<AFCS V={V} />
|
||||
<Marker V={V} />
|
||||
<AirspeedTape V={V} ias={iasS} />
|
||||
@@ -385,7 +385,21 @@ function AFCS({ V }) {
|
||||
}
|
||||
|
||||
/* ---------------- attitude + flight director ---------------- */
|
||||
function Attitude({ V, svt }) {
|
||||
// Heading reference marks along the horizon line (HRZN HDG softkey).
|
||||
function hdgMarks(cx, cy, hdg) {
|
||||
const PX = 3.4, out = []; // px per degree across the horizon
|
||||
for (let k = -40; k <= 40; k += 10) {
|
||||
const d = ((Math.round(hdg / 10) * 10 + k) % 360 + 360) % 360;
|
||||
const off = (((d - hdg + 540) % 360) - 180) * PX;
|
||||
const x = cx + off;
|
||||
const big = d % 30 === 0;
|
||||
out.push(<line key={'h' + k} x1={x} y1={cy - (big ? 13 : 8)} x2={x} y2={cy} stroke="#fff" strokeWidth="1.5" />);
|
||||
if (big) { const lbl = d === 0 ? 'N' : d === 90 ? 'E' : d === 180 ? 'S' : d === 270 ? 'W' : d / 10; out.push(<text key={'t' + k} x={x} y={cy - 16} fill="#fff" fontSize="13" textAnchor="middle" fontFamily="'Saira Condensed',monospace">{lbl}</text>); }
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function Attitude({ V, svt, hrznHdg }) {
|
||||
const pitch = num(V.pitch), roll = num(V.roll), slip = num(V.slip);
|
||||
const fdP = num(V.fdPitch), fdR = num(V.fdRoll);
|
||||
const cx = W / 2, cy = 270;
|
||||
@@ -434,6 +448,7 @@ function Attitude({ V, svt }) {
|
||||
{!svt && <rect x={cx - 800} y={cy} width={1600} height={1100} fill="url(#ground)" />}
|
||||
{!svt && <rect x={cx - 800} y={cy - 1.5} width={1600} height={3} fill="#fff" />}
|
||||
{pitchLadder(cx, cy)}
|
||||
{hrznHdg && hdgMarks(cx, cy, ((num(V.heading) % 360) + 360) % 360)}
|
||||
</g>
|
||||
</g>
|
||||
{/* flight director command bars — magenta filled chevron (single cue) */}
|
||||
|
||||
Reference in New Issue
Block a user