From b94f45fc9699ea3989532a1526ad847ec6357cfb Mon Sep 17 00:00:00 2001 From: Jonah <47046556+jwbonner@users.noreply.github.com> Date: Mon, 28 Oct 2024 00:38:14 -0400 Subject: [PATCH] Add option to loop visible range (#254) --- docsSite/docs/getting-started/navigation.md | 2 +- src/hub/SelectionImpl.ts | 15 +++++++++++-- src/hub/hub.ts | 5 +++-- src/main/main.ts | 25 ++++++++++++++++----- src/satellite.ts | 4 ++++ src/shared/Selection.ts | 3 +++ 6 files changed, 44 insertions(+), 10 deletions(-) diff --git a/docsSite/docs/getting-started/navigation.md b/docsSite/docs/getting-started/navigation.md index 6396e503..d83f5cb6 100644 --- a/docsSite/docs/getting-started/navigation.md +++ b/docsSite/docs/getting-started/navigation.md @@ -35,7 +35,7 @@ The navigation buttons (green) on the top manage the tabs and control playback. - **Plus Button**: Opens a dropdown menu to create a new tab. - **Window Button:** Creates a new pop-out window with the tab tab. This feature can be used to view data from multiple tabs simultaneously. - **X Button:** Closes the current tab. -- **Play Button:** Start and stop real-time playback. _Right-click to change playback speed._ +- **Play Button:** Start and stop real-time playback. _Right-click to change playback speed or enable looping._ ## Viewer Pane diff --git a/src/hub/SelectionImpl.ts b/src/hub/SelectionImpl.ts index 6177cdeb..a8dc1e90 100644 --- a/src/hub/SelectionImpl.ts +++ b/src/hub/SelectionImpl.ts @@ -20,6 +20,7 @@ export default class SelectionImpl implements Selection { private playbackStartLog: number = 0; private playbackStartReal: number = 0; private playbackSpeed: number = 1; + private playbackLooping = false; private liveConnected: boolean = false; private liveTimeSupplier: (() => number) | null = null; @@ -35,10 +36,11 @@ export default class SelectionImpl implements Selection { [this.PLAY_BUTTON, this.PAUSE_BUTTON].forEach((button) => { button.addEventListener("contextmenu", () => { let rect = button.getBoundingClientRect(); - window.sendMainMessage("ask-playback-speed", { + window.sendMainMessage("ask-playback-options", { x: Math.round(rect.right), y: Math.round(rect.top), - speed: this.playbackSpeed + speed: this.playbackSpeed, + looping: this.playbackLooping }); }); }); @@ -104,6 +106,10 @@ export default class SelectionImpl implements Selection { return Math.max(this.staticTime, window.log.getTimestampRange()[0]); case SelectionMode.Playback: let time = (this.now() - this.playbackStartReal) * this.playbackSpeed + this.playbackStartLog; + if (this.playbackLooping && time > this.timelineRange[1]) { + time = this.timelineRange[0]; + this.setSelectedTime(time); + } let maxTime = window.log.getTimestampRange()[1]; if (this.liveTimeSupplier !== null) { maxTime = Math.max(maxTime, this.liveTimeSupplier()); @@ -302,6 +308,11 @@ export default class SelectionImpl implements Selection { this.playbackSpeed = speed; } + /** Updates whether playback is looping. */ + setPlaybackLooping(looping: boolean) { + this.playbackLooping = looping; + } + /** Sets a new time range for an in-progress grab zoom. */ setGrabZoomRange(range: [number, number] | null) { if (range !== null) { diff --git a/src/hub/hub.ts b/src/hub/hub.ts index 0b46fa5e..ed01b46f 100644 --- a/src/hub/hub.ts +++ b/src/hub/hub.ts @@ -769,8 +769,9 @@ async function handleMainMessage(message: NamedMessage) { } break; - case "set-playback-speed": - window.selection.setPlaybackSpeed(message.data); + case "set-playback-options": + window.selection.setPlaybackSpeed(message.data.speed); + window.selection.setPlaybackLooping(message.data.looping); break; case "toggle-sidebar": diff --git a/src/main/main.ts b/src/main/main.ts index e8e7dd56..c7a06046 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -507,21 +507,36 @@ async function handleHubMessage(window: BrowserWindow, message: NamedMessage) { } break; - case "ask-playback-speed": - const playbackSpeedMenu = new Menu(); + case "ask-playback-options": + const playbackOptionsMenu = new Menu(); Array(0.25, 0.5, 1, 1.5, 2, 4, 8).forEach((value) => { - playbackSpeedMenu.append( + playbackOptionsMenu.append( new MenuItem({ label: (value * 100).toString() + "%", type: "checkbox", checked: value === message.data.speed, click() { - sendMessage(window, "set-playback-speed", value); + sendMessage(window, "set-playback-options", { speed: value, looping: message.data.looping }); } }) ); }); - playbackSpeedMenu.popup({ + playbackOptionsMenu.append( + new MenuItem({ + type: "separator" + }) + ); + playbackOptionsMenu.append( + new MenuItem({ + label: "Loop Visible Range", + type: "checkbox", + checked: message.data.looping, + click() { + sendMessage(window, "set-playback-options", { speed: message.data.speed, looping: !message.data.looping }); + } + }) + ); + playbackOptionsMenu.popup({ window: window, x: message.data.x, y: message.data.y diff --git a/src/satellite.ts b/src/satellite.ts index 764a1a18..0cf9d586 100644 --- a/src/satellite.ts +++ b/src/satellite.ts @@ -312,6 +312,10 @@ class MockSelection implements Selection { throw new Error("Method not implemented."); } + setPlaybackLooping(looping: boolean): void { + throw new Error("Method not implemented."); + } + setGrabZoomRange(range: [number, number] | null) { window.sendMainMessage("call-selection-setter", { name: "setGrabZoomRange", args: [range] }); } diff --git a/src/shared/Selection.ts b/src/shared/Selection.ts index 1840aa10..60a6f2b2 100644 --- a/src/shared/Selection.ts +++ b/src/shared/Selection.ts @@ -53,6 +53,9 @@ export default interface Selection { /** Updates the playback speed. */ setPlaybackSpeed(speed: number): void; + /** Updates whether playback is looping. */ + setPlaybackLooping(looping: boolean): void; + /** Sets a new time range for an in-progress grab zoom. */ setGrabZoomRange(range: [number, number] | null): void;