diff --git a/src/App/AST.css b/src/App/AST.css index 1dd14ed..b95fe4f 100644 --- a/src/App/AST.css +++ b/src/App/AST.css @@ -1,8 +1,9 @@ details.active { /* background-color: rgba(128, 72, 0, 0.855); color: white; */ - - box-shadow: rgba(128, 72, 0, 0.856) 0 0 8; + border: rgb(192, 125, 0); + background-color:rgb(255, 251, 244); + filter: drop-shadow( 0px 0px 3px rgba(192, 125, 0, 0.856)); } details { @@ -11,6 +12,8 @@ details { background-color: white; margin-bottom: 4px; padding-right: 2px; + padding-top: 2px; + padding-bottom: 2px; color: black; width: fit-content; border-radius: 10px; diff --git a/src/App/App.tsx b/src/App/App.tsx index 2e2b79e..e116143 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -105,10 +105,12 @@ export function App() { }; }, []); - const highlightActive = (rtIdx !== undefined) && new Set([...rt[rtIdx].mode].filter(uid => { - const state = ast.uid2State.get(uid); - return state && state.parent?.kind !== "and"; - })) || new Set(); + // const highlightActive = (rtIdx !== undefined) && new Set([...rt[rtIdx].mode].filter(uid => { + // const state = ast.uid2State.get(uid); + // return state && state.parent?.kind !== "and"; + // })) || new Set(); + + const highlightActive = (rtIdx === undefined) ? new Set() : rt[rtIdx].mode; const highlightTransitions = (rtIdx === undefined) ? [] : rt[rtIdx].firedTransitions; diff --git a/src/App/TopPanel.tsx b/src/App/TopPanel.tsx index df25360..4b336e9 100644 --- a/src/App/TopPanel.tsx +++ b/src/App/TopPanel.tsx @@ -4,7 +4,6 @@ import { getSimTime, setPaused, setRealtime, TimeMode } from "../statecharts/tim import { Statechart } from "../statecharts/abstract_syntax"; import CachedIcon from '@mui/icons-material/Cached'; -import ClearIcon from '@mui/icons-material/Clear'; import PauseIcon from '@mui/icons-material/Pause'; import PlayArrowIcon from '@mui/icons-material/PlayArrow'; import BoltIcon from '@mui/icons-material/Bolt'; @@ -63,6 +62,19 @@ export function TopPanel({rt, time, setTime, onInit, onClear, onRaise, ast, mode const [displayTime, setDisplayTime] = useState("0.000"); const [timescale, setTimescale] = useState(1); + useEffect(() => { + const onKeyDown = (e: KeyboardEvent) => { + if (e.key === " ") { + e.preventDefault(); + onChangePaused(time.kind !== "paused", performance.now()); + }; + }; + window.addEventListener("keydown", onKeyDown); + return () => { + window.removeEventListener("keydown", onKeyDown); + }; + }, [time]); + function updateDisplayedTime() { const now = performance.now(); const timeMs = getSimTime(time, now); @@ -81,7 +93,7 @@ export function TopPanel({rt, time, setTime, onInit, onClear, onRaise, ast, mode function onChangePaused(paused: boolean, wallclktime: number) { setTime(time => { if (paused) { - return setPaused(time, performance.now()); + return setPaused(time, wallclktime); } else { return setRealtime(time, timescale, wallclktime); diff --git a/src/VisualEditor/VisualEditor.tsx b/src/VisualEditor/VisualEditor.tsx index d1a27b8..d5d6c80 100644 --- a/src/VisualEditor/VisualEditor.tsx +++ b/src/VisualEditor/VisualEditor.tsx @@ -786,7 +786,7 @@ export function VisualEditor({ast, setAST, rt, errors, setErrors, mode, highligh highlight={arrowsToHighlight.hasOwnProperty(arrow.uid)} fired={highlightTransitions.includes(arrow.uid)} arc={arc} - initialMarker={initialMarker} + initialMarker={Boolean(initialMarker)} />; } )} diff --git a/src/statecharts/interpreter.ts b/src/statecharts/interpreter.ts index 7e0e3af..b425bcc 100644 --- a/src/statecharts/interpreter.ts +++ b/src/statecharts/interpreter.ts @@ -5,13 +5,13 @@ import { BigStepOutput, Environment, initialRaised, Mode, RaisedEvents, RT_Event export function initialize(ast: Statechart): BigStepOutput { let history = new Map(); - let enteredStates, environment, raised; - ({enteredStates, environment, history, ...raised} = enterDefault(0, ast.root, { + let enteredStates, environment, rest; + ({enteredStates, environment, history, ...rest} = enterDefault(0, ast.root, { environment: new Environment([new Map([["_timers", []]])]), history, ...initialRaised, })); - return handleInternalEvents(0, ast, {mode: enteredStates, environment, history, ...raised}); + return handleInternalEvents(0, ast, {mode: enteredStates, environment, history, ...rest}); } type ActionScope = { @@ -121,8 +121,10 @@ export function enterDefault(simtime: number, state: ConcreteState, rt: ActionSc ({enteredStates: enteredChildren, firedTransitions, ...actionScope} = enterDefault(simtime, toEnter, {firedTransitions, ...actionScope})); enteredStates = enteredStates.union(enteredChildren); } - // console.warn(state.uid + ': no initial state'); - } + else { + console.warn(state.uid + ': no initial state'); + } + } return {enteredStates, firedTransitions, ...actionScope}; } @@ -155,12 +157,7 @@ export function enterStates(simtime: number, state: ConcreteState, toEnter: Set< } else if (childToEnter.length === 0) { // also good, enter default child - for (const [_, defaultChild] of state.initial) { - let enteredChildren; - ({enteredStates: enteredChildren, ...actionScope} = enterDefault(simtime, defaultChild, actionScope)); - enteredStates = enteredStates.union(enteredChildren); - break; // one is enough - } + return enterDefault(simtime, state, {...actionScope}); } else { throw new Error("can only enter one child of an OR-state, stupid!"); @@ -184,6 +181,7 @@ export function exitCurrent(simtime: number, state: ConcreteState, rt: EnteredSc ({history, ...actionScope} = exitActions(simtime, state, {enteredStates, history, ...actionScope})); // record history + history = new Map(history); // defensive copy for (const h of state.history) { if (h.kind === "shallow") { history.set(h.uid, new Set(state.children @@ -204,7 +202,7 @@ export function exitCurrent(simtime: number, state: ConcreteState, rt: EnteredSc return {history, ...actionScope}; } -export function handleEvent(simtime: number, event: RT_Event, statechart: Statechart, activeParent: StableState, {environment, mode, history, ...raised}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents { +export function handleEvent(simtime: number, event: RT_Event, statechart: Statechart, activeParent: StableState, {environment, mode, ...rest}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents { const arenasFired = new Set(); for (const state of activeParent.children) { if (mode.has(state.uid)) { @@ -256,7 +254,7 @@ export function handleEvent(simtime: number, event: RT_Event, statechart: Statec event.param, ); } - ({mode, environment, history, ...raised} = fireTransition(simtime, t, statechart.transitions, l, arena, {mode, environment, history, ...raised})); + ({mode, environment, ...rest} = fireTransition(simtime, t, statechart.transitions, l, arena, {mode, environment, ...rest})); if (event.kind === "input" && event.param !== undefined) { environment = environment.popScope(); } @@ -268,11 +266,12 @@ export function handleEvent(simtime: number, event: RT_Event, statechart: Statec } else { // no enabled outgoing transitions, try the children: - ({environment, mode, history, ...raised} = handleEvent(simtime, event, statechart, state, {environment, mode, history, ...raised})); + ({environment, mode, ...rest} = handleEvent(simtime, event, statechart, state, + {environment, mode, ...rest})); } } } - return {environment, mode, history, ...raised}; + return {environment, mode, ...rest}; } export function handleInputEvent(simtime: number, event: RT_Event, statechart: Statechart, {mode, environment, history}: {mode: Mode, environment: Environment, history: RT_History}): BigStepOutput { @@ -293,20 +292,20 @@ export function handleInternalEvents(simtime: number, statechart: Statechart, {i return rest; } -export function fireTransition(simtime: number, t: Transition, ts: Map, label: TransitionLabel, arena: OrState, {mode, environment, history, ...raised}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents { +export function fireTransition(simtime: number, t: Transition, ts: Map, label: TransitionLabel, arena: OrState, {mode, environment, history, ...rest}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents { console.log('fire', transitionDescription(t)); const srcPath = computePath({ancestor: arena, descendant: t.src as ConcreteState}).reverse() as ConcreteState[]; // exit src and other states up to arena - ({environment, history, ...raised} = exitCurrent(simtime, srcPath[0], {environment, enteredStates: mode, history, ...raised})) + ({environment, history, ...rest} = exitCurrent(simtime, srcPath[0], {environment, enteredStates: mode, history, ...rest})) const toExit = getDescendants(arena); toExit.delete(arena.uid); // do not exit the arena itself const exitedMode = mode.difference(toExit); // active states after exiting the states we need to exit // console.log({exitedMode}); - return fireSecondHalfOfTransition(simtime, t, ts, label, arena, {mode: exitedMode, history, environment, ...raised}); + return fireSecondHalfOfTransition(simtime, t, ts, label, arena, {mode: exitedMode, history, environment, ...rest}); } // assuming we've already exited the source state of the transition, now enter the target state @@ -350,7 +349,7 @@ export function fireSecondHalfOfTransition(simtime: number, t: Transition, ts: M // enter tgt let enteredStates; - ({enteredStates, environment, history, ...raised} = enterStates(simtime, state, toEnter, {environment, history, firedTransitions, ...raised})); + ({enteredStates, environment, history, firedTransitions, ...raised} = enterStates(simtime, state, toEnter, {environment, history, firedTransitions, ...raised})); const enteredMode = mode.union(enteredStates); // console.log({enteredMode});