more keyboard shortcuts
This commit is contained in:
parent
9ce55e0264
commit
59d5e9913a
3 changed files with 61 additions and 19 deletions
|
|
@ -32,6 +32,7 @@ export function App() {
|
||||||
setRT([{inputEvent: null, simtime: 0, ...config}]);
|
setRT([{inputEvent: null, simtime: 0, ...config}]);
|
||||||
setRTIdx(0);
|
setRTIdx(0);
|
||||||
setTime({kind: "paused", simtime: 0});
|
setTime({kind: "paused", simtime: 0});
|
||||||
|
scrollDownSidebar();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClear() {
|
function onClear() {
|
||||||
|
|
@ -51,12 +52,17 @@ export function App() {
|
||||||
function appendNewConfig(inputEvent: string, simtime: number, config: BigStepOutput) {
|
function appendNewConfig(inputEvent: string, simtime: number, config: BigStepOutput) {
|
||||||
setRT([...rt.slice(0, rtIdx!+1), {inputEvent, simtime, ...config}]);
|
setRT([...rt.slice(0, rtIdx!+1), {inputEvent, simtime, ...config}]);
|
||||||
setRTIdx(rtIdx!+1);
|
setRTIdx(rtIdx!+1);
|
||||||
console.log('new config:', config);
|
// console.log('new config:', config);
|
||||||
|
scrollDownSidebar();
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollDownSidebar() {
|
||||||
if (refRightSideBar.current) {
|
if (refRightSideBar.current) {
|
||||||
const el = refRightSideBar.current;
|
const el = refRightSideBar.current;
|
||||||
|
// hack: we want to scroll to the new element, but we have to wait until it is rendered...
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
el.scrollIntoView({block: "end", behavior: "smooth"});
|
el.scrollIntoView({block: "end", behavior: "smooth"});
|
||||||
}, 100);
|
}, 50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,18 +72,39 @@ export function TopPanel({rt, time, setTime, onInit, onClear, onRaise, ast, mode
|
||||||
const onKeyDown = (e: KeyboardEvent) => {
|
const onKeyDown = (e: KeyboardEvent) => {
|
||||||
if (e.key === " ") {
|
if (e.key === " ") {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
if (rt)
|
||||||
onChangePaused(time.kind !== "paused", performance.now());
|
onChangePaused(time.kind !== "paused", performance.now());
|
||||||
};
|
};
|
||||||
if (e.key === "i") {
|
if (e.key === "i") {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onInit();
|
onInit();
|
||||||
}
|
}
|
||||||
|
if (e.key === "c") {
|
||||||
|
e.preventDefault();
|
||||||
|
onClear();
|
||||||
|
}
|
||||||
|
if (e.key === "Tab") {
|
||||||
|
e.preventDefault();
|
||||||
|
onSkip();
|
||||||
|
}
|
||||||
|
if (e.key === "s") {
|
||||||
|
e.preventDefault();
|
||||||
|
onSlower();
|
||||||
|
}
|
||||||
|
if (e.key === "f") {
|
||||||
|
e.preventDefault();
|
||||||
|
onFaster();
|
||||||
|
}
|
||||||
|
if (e.key === "`") {
|
||||||
|
e.preventDefault();
|
||||||
|
setShowKeys(show => !show);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
window.addEventListener("keydown", onKeyDown);
|
window.addEventListener("keydown", onKeyDown);
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("keydown", onKeyDown);
|
window.removeEventListener("keydown", onKeyDown);
|
||||||
};
|
};
|
||||||
}, [time, onInit]);
|
}, [time, onInit, timescale]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => localStorage.setItem("showKeys", showKeys?"1":"0"), 100);
|
setTimeout(() => localStorage.setItem("showKeys", showKeys?"1":"0"), 100);
|
||||||
|
|
@ -143,6 +164,25 @@ export function TopPanel({rt, time, setTime, onInit, onClear, onRaise, ast, mode
|
||||||
const timers: Timers = (rt?.environment.get("_timers") || []);
|
const timers: Timers = (rt?.environment.get("_timers") || []);
|
||||||
const nextTimedTransition: [number, TimerElapseEvent] | undefined = timers[0];
|
const nextTimedTransition: [number, TimerElapseEvent] | undefined = timers[0];
|
||||||
|
|
||||||
|
function onSkip() {
|
||||||
|
const now = performance.now();
|
||||||
|
setTime(time => {
|
||||||
|
if (time.kind === "paused") {
|
||||||
|
return {kind: "paused", simtime: nextTimedTransition[0]};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return {kind: "realtime", scale: time.scale, since: {simtime: nextTimedTransition[0], wallclktime: now}};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSlower() {
|
||||||
|
onTimeScaleChange((timescale/2).toString(), performance.now());
|
||||||
|
}
|
||||||
|
function onFaster() {
|
||||||
|
onTimeScaleChange((timescale*2).toString(), performance.now());
|
||||||
|
}
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className="toolbar">
|
<div className="toolbar">
|
||||||
|
|
||||||
|
|
@ -175,7 +215,9 @@ export function TopPanel({rt, time, setTime, onInit, onClear, onRaise, ast, mode
|
||||||
<KeyInfo keyInfo={<kbd>I</kbd>}>
|
<KeyInfo keyInfo={<kbd>I</kbd>}>
|
||||||
<button title="(re)initialize simulation" onClick={onInit} ><PlayArrowIcon fontSize="small"/><CachedIcon fontSize="small"/></button>
|
<button title="(re)initialize simulation" onClick={onInit} ><PlayArrowIcon fontSize="small"/><CachedIcon fontSize="small"/></button>
|
||||||
</KeyInfo>
|
</KeyInfo>
|
||||||
|
<KeyInfo keyInfo={<kbd>C</kbd>}>
|
||||||
<button title="clear the simulation" onClick={onClear} disabled={!rt}><StopIcon fontSize="small"/></button>
|
<button title="clear the simulation" onClick={onClear} disabled={!rt}><StopIcon fontSize="small"/></button>
|
||||||
|
</KeyInfo>
|
||||||
|
|
||||||
 
|
 
|
||||||
|
|
||||||
|
|
@ -186,21 +228,19 @@ export function TopPanel({rt, time, setTime, onInit, onClear, onRaise, ast, mode
|
||||||
|
|
||||||
 
|
 
|
||||||
|
|
||||||
<label htmlFor="number-timescale">timescale</label>
|
<label htmlFor="number-timescale">speed</label>
|
||||||
<KeyInfo keyInfo={<kbd>S</kbd>}>
|
<KeyInfo keyInfo={<kbd>S</kbd>}>
|
||||||
<button title="slower" onClick={() => onTimeScaleChange((timescale/2).toString(), performance.now())}>÷2</button>
|
<button title="slower" onClick={onSlower}>÷2</button>
|
||||||
</KeyInfo>
|
</KeyInfo>
|
||||||
<input title="controls how fast the simulation should run in real time mode - larger than 1 means: faster than wall-clock time" id="number-timescale" value={timescale.toFixed(3)} style={{width:40}} readOnly onChange={e => onTimeScaleChange(e.target.value, performance.now())}/>
|
<input title="controls how fast the simulation should run in real time mode - larger than 1 means: faster than wall-clock time" id="number-timescale" value={timescale.toFixed(3)} style={{width:40}} readOnly onChange={e => onTimeScaleChange(e.target.value, performance.now())}/>
|
||||||
<KeyInfo keyInfo={<kbd>F</kbd>}>
|
<KeyInfo keyInfo={<kbd>F</kbd>}>
|
||||||
<button title="faster" onClick={() => onTimeScaleChange((timescale*2).toString(), performance.now())}>×2</button>
|
<button title="faster" onClick={onFaster}>×2</button>
|
||||||
</KeyInfo>
|
</KeyInfo>
|
||||||
|
|
||||||
 
|
 
|
||||||
|
|
||||||
<KeyInfo>
|
|
||||||
<label htmlFor="time">time (s)</label>
|
<label htmlFor="time">time (s)</label>
|
||||||
<input title="the current simulated time" id="time" disabled={!rt} value={displayTime} readOnly={true} className="readonlyTextBox" />
|
<input title="the current simulated time" id="time" disabled={!rt} value={displayTime} readOnly={true} className="readonlyTextBox" />
|
||||||
</KeyInfo>
|
|
||||||
|
|
||||||
|
|
||||||
 
|
 
|
||||||
|
|
@ -210,17 +250,7 @@ export function TopPanel({rt, time, setTime, onInit, onClear, onRaise, ast, mode
|
||||||
<input title="next point in simulated time where a timed transition may fire" id="next-timeout" disabled={!rt} value={nextTimedTransition ? formatTime(nextTimedTransition[0]) : '+inf'} readOnly={true} className="readonlyTextBox"/>
|
<input title="next point in simulated time where a timed transition may fire" id="next-timeout" disabled={!rt} value={nextTimedTransition ? formatTime(nextTimedTransition[0]) : '+inf'} readOnly={true} className="readonlyTextBox"/>
|
||||||
</KeyInfo>
|
</KeyInfo>
|
||||||
<KeyInfo keyInfo={<kbd>Tab</kbd>}>
|
<KeyInfo keyInfo={<kbd>Tab</kbd>}>
|
||||||
<button title="advance time just enough for the next timer to elapse" disabled={nextTimedTransition===undefined} onClick={() => {
|
<button title="advance time just enough for the next timer to elapse" disabled={nextTimedTransition===undefined} onClick={onSkip}><SkipNextIcon fontSize="small"/><AccessAlarmIcon fontSize="small"/></button>
|
||||||
const now = performance.now();
|
|
||||||
setTime(time => {
|
|
||||||
if (time.kind === "paused") {
|
|
||||||
return {kind: "paused", simtime: nextTimedTransition[0]};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return {kind: "realtime", scale: time.scale, since: {simtime: nextTimedTransition[0], wallclktime: now}};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}><SkipNextIcon fontSize="small"/><AccessAlarmIcon fontSize="small"/></button>
|
|
||||||
</KeyInfo>
|
</KeyInfo>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -259,8 +289,10 @@ export function TopPanel({rt, time, setTime, onInit, onClear, onRaise, ast, mode
|
||||||
 
|
 
|
||||||
|
|
||||||
<div style={{display:"inline-block"}}>
|
<div style={{display:"inline-block"}}>
|
||||||
|
<KeyInfo keyInfo={<kbd>~</kbd>}>
|
||||||
<input id="checkbox-keys" type="checkbox" checked={showKeys} onChange={e => setShowKeys(e.target.checked)}></input>
|
<input id="checkbox-keys" type="checkbox" checked={showKeys} onChange={e => setShowKeys(e.target.checked)}></input>
|
||||||
<label for="checkbox-keys">shortcuts</label>
|
<label for="checkbox-keys">shortcuts</label>
|
||||||
|
</KeyInfo>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -35,3 +35,7 @@ input {
|
||||||
::selection {
|
::selection {
|
||||||
background-color: rgba(0,0,255,0.2);
|
background-color: rgba(0,0,255,0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue