Debounce keyboard repeats for smoother movement

This commit is contained in:
Julian Stirling 2026-02-13 01:02:55 +00:00
parent 61666a116b
commit 586d45aed3
2 changed files with 55 additions and 47 deletions

View file

@ -29,6 +29,8 @@ import loadingContent from "./components/loadingContent.vue";
import Mousetrap from "mousetrap"; import Mousetrap from "mousetrap";
import { eventBus } from "./eventBus.js"; import { eventBus } from "./eventBus.js";
const move_keys = ["up", "down", "left", "right", "pageup", "pagedown"];
Mousetrap.prototype.stopCallback = function (e, element) { Mousetrap.prototype.stopCallback = function (e, element) {
// if the element has the class "mousetrap" then no need to stop // if the element has the class "mousetrap" then no need to stop
if ((" " + element.className + " ").indexOf(" Mousetrap ") > -1) { if ((" " + element.className + " ").indexOf(" Mousetrap ") > -1) {
@ -65,6 +67,10 @@ export default {
keyboardManual: [], keyboardManual: [],
systemDark: undefined, systemDark: undefined,
themeObserver: undefined, themeObserver: undefined,
keysDown: new Set(),
lastJogTime: 0,
jogDistance: 600,
jogTime: 300,
}; };
}, },
@ -130,42 +136,31 @@ export default {
this.toggleModalElement(this.$refs["keyboardManualModal"]); // Calls the mixin this.toggleModalElement(this.$refs["keyboardManualModal"]); // Calls the mixin
}); });
// Arrow keys
Mousetrap.bind( Mousetrap.bind(
["up", "down", "left", "right"], move_keys,
(event) => { (event, key) => {
this.arrowKeysDown[event.keyCode] = true; //Add key to array event.preventDefault();
this.navigateKeyHandler(); this.keysDown.add(key);
this.updateJogFromKeys();
}, },
"keydown", "keydown",
); );
Mousetrap.bind( Mousetrap.bind(
["up", "down", "left", "right"], move_keys,
(event) => { (event, key) => {
delete this.arrowKeysDown[event.keyCode]; //Remove key from array event.preventDefault();
this.invokeAction("stage", "jog", { stop: true }); this.keysDown.delete(key);
this.updateJogFromKeys();
}, },
"keyup", "keyup",
); );
this.keyboardManual.push({ this.keyboardManual.push({
shortcut: "←↑→↓", shortcut: "←↑→↓",
description: "Move the microscope stage", description: "Move the microscope stage",
}); });
// Focus keys
Mousetrap.bind("pageup", () => {
eventBus.emit("globalMoveStepEvent", { x: 0, y: 0, z: 1 });
});
Mousetrap.bind("pagedown", () => {
eventBus.emit("globalMoveStepEvent", { x: 0, y: 0, z: -1 });
});
Mousetrap.bind(
["pageup", "pagedown"],
() => {
this.invokeAction("stage", "jog", { stop: true });
},
"keyup",
);
this.keyboardManual.push({ this.keyboardManual.push({
shortcut: "pgup / pgdn", shortcut: "pgup / pgdn",
description: "Move the microscope focus", description: "Move the microscope focus",
@ -266,29 +261,42 @@ export default {
} }
}, },
navigateKeyHandler: function () { jog(x, y, z) {
// Calculate movement array // Manually debounce extra requests from keyboard repeat rate.
var x_rel = 0; // This is used rather than and interval in case of missing a repeat.
var y_rel = 0; const now = Date.now();
// 37 corresponds to the left key if (now - this.lastJogTime < this.jogTime) {
if (37 in this.arrowKeysDown) { return;
x_rel = x_rel - 1;
} }
// 39 corresponds to the right key this.lastJogTime = now;
if (39 in this.arrowKeysDown) {
x_rel = x_rel + 1; this.invokeAction("stage", "jog", {
x: x * this.jogDistance,
y: y * this.jogDistance,
z: z * this.jogDistance,
});
},
jogStop() {
this.invokeAction("stage", "jog", { stop: true });
},
updateJogFromKeys() {
let x = 0,
y = 0,
z = 0;
if (this.keysDown.has("left")) x -= 1;
if (this.keysDown.has("right")) x += 1;
if (this.keysDown.has("up")) y += 1;
if (this.keysDown.has("down")) y -= 1;
if (this.keysDown.has("pageup")) z += 1;
if (this.keysDown.has("pagedown")) z -= 1;
if (x || y || z) {
this.jog(x, y, z);
} else {
this.jogStop();
} }
// 38 corresponds to the up key
if (38 in this.arrowKeysDown) {
y_rel = y_rel + 1;
}
// 40 corresponds to the down key
if (40 in this.arrowKeysDown) {
y_rel = y_rel - 1;
}
// Make a position request
// Emit a signal to move, acted on by panelControl.vue
eventBus.emit("globalMoveStepEvent", { x: x_rel, y: y_rel, z: 0 });
}, },
}, },
}; };

View file

@ -120,9 +120,9 @@ export default {
onMoveStep(payload) { onMoveStep(payload) {
const navigationStepSize = this.$store.state.navigationStepSize; const navigationStepSize = this.$store.state.navigationStepSize;
const navigationInvert = this.$store.state.navigationInvert; const navigationInvert = this.$store.state.navigationInvert;
const x = x_steps * navigationStepSize.x * (navigationInvert.x ? -1 : 1); const x = payload.x_steps * navigationStepSize.x * (navigationInvert.x ? -1 : 1);
const y = y_steps * navigationStepSize.y * (navigationInvert.y ? -1 : 1); const y = payload.y_steps * navigationStepSize.y * (navigationInvert.y ? -1 : 1);
const z = z_steps * navigationStepSize.z * (navigationInvert.z ? -1 : 1); const z = payload.z_steps * navigationStepSize.z * (navigationInvert.z ? -1 : 1);
this.invokeAction("stage", "jog", { x: x, y: y, z: z }); this.invokeAction("stage", "jog", { x: x, y: y, z: z });
}, },