Compare commits

..

2 commits

4 changed files with 60 additions and 36 deletions

View file

@ -44,8 +44,10 @@ export function App() {
const [rtIdx, setRTIdx] = useState<number|undefined>(); const [rtIdx, setRTIdx] = useState<number|undefined>();
const [time, setTime] = useState<TimeMode>({kind: "paused", simtime: 0}); const [time, setTime] = useState<TimeMode>({kind: "paused", simtime: 0});
const [modal, setModal] = useState<ReactElement|null>(null); const [modal, setModal] = useState<ReactElement|null>(null);
const [plantName, setPlantName] = usePersistentState("plant", "dummy"); const [plantName, setPlantName] = usePersistentState("plant", "dummy");
const [zoom, setZoom] = usePersistentState("zoom", 1); const [zoom, setZoom] = usePersistentState("zoom", 1);
const [showKeys, setShowKeys] = usePersistentState("shortcuts", true);
const plant = plants.find(([pn, p]) => pn === plantName)![1]; const plant = plants.find(([pn, p]) => pn === plantName)![1];
@ -249,7 +251,7 @@ export function App() {
> >
<TopPanel <TopPanel
rt={rtIdx === undefined ? undefined : rt[rtIdx]} rt={rtIdx === undefined ? undefined : rt[rtIdx]}
{...{rtIdx, ast, time, setTime, onUndo, onRedo, onInit, onClear, onRaise, onBack, mode, setMode, setModal, zoom, setZoom}} {...{rtIdx, ast, time, setTime, onUndo, onRedo, onInit, onClear, onRaise, onBack, mode, setMode, setModal, zoom, setZoom, showKeys, setShowKeys}}
/> />
</Box> </Box>
{/* Below the top bar: Editor */} {/* Below the top bar: Editor */}
@ -281,7 +283,7 @@ export function App() {
</PersistentDetails> </PersistentDetails>
<PersistentDetails localStorageKey="showInputEvents" initiallyOpen={true}> <PersistentDetails localStorageKey="showInputEvents" initiallyOpen={true}>
<summary>input events</summary> <summary>input events</summary>
<ShowInputEvents inputEvents={ast.inputEvents} onRaise={onRaise} disabled={rtIdx===undefined}/> <ShowInputEvents inputEvents={ast.inputEvents} onRaise={onRaise} disabled={rtIdx===undefined} showKeys={showKeys}/>
</PersistentDetails> </PersistentDetails>
<PersistentDetails localStorageKey="showInternalEvents" initiallyOpen={true}> <PersistentDetails localStorageKey="showInternalEvents" initiallyOpen={true}>
<summary>internal events</summary> <summary>internal events</summary>

View file

@ -72,38 +72,60 @@ export function ShowAST(props: {root: ConcreteState | PseudoState, transitions:
} }
import BoltIcon from '@mui/icons-material/Bolt'; import BoltIcon from '@mui/icons-material/Bolt';
import { KeyInfoHidden, KeyInfoVisible } from "./KeyInfo";
import { useEffect } from "react";
export function ShowInputEvents({inputEvents, onRaise, disabled}: {inputEvents: EventTrigger[], onRaise: (e: string, p: any) => void, disabled: boolean}) { export function ShowInputEvents({inputEvents, onRaise, disabled, showKeys}: {inputEvents: EventTrigger[], onRaise: (e: string, p: any) => void, disabled: boolean, showKeys: boolean}) {
return inputEvents.map(({event, paramName}) => const raiseHandlers = inputEvents.map(({event}) => {
<div key={event+'/'+paramName} className="toolbarGroup"> return () => {
<button // @ts-ignore
className="inputEvent" const param = document.getElementById(`input-${event}-param`)?.value;
title={`raise this input event`} let paramParsed;
disabled={disabled} try {
onClick={() => { if (param) {
// @ts-ignore paramParsed = JSON.parse(param); // may throw
const param = document.getElementById(`input-${event}-param`)?.value; }
let paramParsed; }
try { catch (e) {
if (param) { alert("invalid json");
paramParsed = JSON.parse(param); // may throw return;
} }
} onRaise(event, paramParsed);
catch (e) { };
alert("invalid json"); });
return; const onKeyDown = (e: KeyboardEvent) => {
} const n = (parseInt(e.key)+9) % 10;
onRaise(event, paramParsed); if (raiseHandlers[n] !== undefined) {
}}> raiseHandlers[n]();
<BoltIcon fontSize="small"/> e.stopPropagation();
{event} e.preventDefault();
</button> }
}
useEffect(() => {
window.addEventListener("keydown", onKeyDown);
return () => window.removeEventListener("keydown", onKeyDown);
}, [raiseHandlers]);
const KeyInfo = showKeys ? KeyInfoVisible : KeyInfoHidden;
return inputEvents.map(({event, paramName}, i) => {
const shortcut = (i+1)%10;
const KI = (i <= 10) ? KeyInfo : KeyInfoHidden;
return <div key={event+'/'+paramName} className="toolbarGroup">
<KI keyInfo={<kbd>{shortcut}</kbd>}>
<button
className="inputEvent"
title={`raise this input event`}
disabled={disabled}
onClick={raiseHandlers[i]}>
<BoltIcon fontSize="small"/>
{event}
</button>
</KI>
{paramName && {paramName &&
<><input id={`input-${event}-param`} style={{width: 20}} placeholder={paramName}/></> <><input id={`input-${event}-param`} style={{width: 20}} placeholder={paramName}/></>
} }
&nbsp; &nbsp;
</div> </div>;
) })
} }
export function ShowInternalEvents(props: {internalEvents: EventTrigger[]}) { export function ShowInternalEvents(props: {internalEvents: EventTrigger[]}) {

View file

@ -43,12 +43,13 @@ export type TopPanelProps = {
setModal: Dispatch<SetStateAction<ReactElement|null>>, setModal: Dispatch<SetStateAction<ReactElement|null>>,
zoom: number, zoom: number,
setZoom: Dispatch<SetStateAction<number>>, setZoom: Dispatch<SetStateAction<number>>,
showKeys: boolean,
setShowKeys: Dispatch<SetStateAction<boolean>>,
} }
export function TopPanel({rt, rtIdx, time, setTime, onUndo, onRedo, onInit, onClear, onRaise, onBack, ast, mode, setMode, setModal, zoom, setZoom}: TopPanelProps) { export function TopPanel({rt, rtIdx, time, setTime, onUndo, onRedo, onInit, onClear, onRaise, onBack, ast, mode, setMode, setModal, zoom, setZoom, showKeys, setShowKeys}: TopPanelProps) {
const [displayTime, setDisplayTime] = useState("0.000"); const [displayTime, setDisplayTime] = useState("0.000");
const [timescale, setTimescale] = useState(1); const [timescale, setTimescale] = useState(1);
const [showKeys, setShowKeys] = usePersistentState("shortcuts", true);
const KeyInfo = showKeys ? KeyInfoVisible : KeyInfoHidden; const KeyInfo = showKeys ? KeyInfoVisible : KeyInfoHidden;

View file

@ -1,6 +1,5 @@
import imgNote from "./noteSmall.png"; import imgNote from "./noteSmall.png";
import imgWatch from "./watch.png"; import imgWatch from "./watch.png";
import imgWatchLight from "./watch-light.png";
import digitalFont from "./digital-font.ttf"; import digitalFont from "./digital-font.ttf";
import { Plant } from "../Plant"; import { Plant } from "../Plant";
import { RaisedEvent } from "@/statecharts/runtime_types"; import { RaisedEvent } from "@/statecharts/runtime_types";
@ -38,10 +37,10 @@ export function DigitalWatch({light, h, m, s, alarm, callbacks}: DigitalWatchPro
} }
`}</style> `}</style>
<svg version="1.1" width="222" height="236" style={{userSelect: 'none'}}> <svg version="1.1" width="222" height="236" style={{userSelect: 'none'}}>
{light ? <image width="222" height="236" xlinkHref={imgWatch}/>
<image width="222" height="236" xlinkHref={imgWatchLight}/>
: <image width="222" height="236" xlinkHref={imgWatch}/> {light &&
} <rect x={52} y={98} width={120} height={52} fill="#deeaffff" rx={5} ry={5} />}
<text x="111" y="126" dominantBaseline="middle" textAnchor="middle" fontFamily="digital-font" fontSize={28} style={{whiteSpace:'preserve'}}>{hhmmss}</text> <text x="111" y="126" dominantBaseline="middle" textAnchor="middle" fontFamily="digital-font" fontSize={28} style={{whiteSpace:'preserve'}}>{hhmmss}</text>