From 3a66879b889dce85d7498e216980a2c08288b36f Mon Sep 17 00:00:00 2001 From: Terry Truong Date: Fri, 6 Jan 2023 23:58:25 +1100 Subject: Keep track of a 'current' timeline Make timeline addition and searching use the current timeline. Add keyboard controls for timeline panning, zooming, switching, opening, and closing. Fix zoomTimeline() bug when not centering zoom on pointer. --- src/App.vue | 68 ++++++++++++++++++++++++++++++++++++--------- src/components/TimeLine.vue | 47 +++++++++++++++++++++++++++---- 2 files changed, 96 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/App.vue b/src/App.vue index 960e648..803b53a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -2,7 +2,7 @@
-

Histplorer

+

Histplorer

@@ -23,10 +23,11 @@ :style="{backgroundColor: store.color.bg}" ref="contentAreaRef"> + @info-click="onInfoClick" @pointerenter="currentTimelineIdx = idx"/>
@@ -87,23 +88,24 @@ onMounted(updateAreaDims); // Timeline data const timelines: Ref = ref([]); +const currentTimelineIdx = ref(0); let nextTimelineId = 1; function addTimeline(){ if (timelines.value.length == 0){ timelines.value.push(new TimelineState(nextTimelineId, store.initialStartDate, store.initialEndDate)); } else { - let last = timelines.value[timelines.value.length - 1]; - timelines.value.push(new TimelineState( - nextTimelineId, last.startDate, - last.endDate, last.startOffset, last.endOffset, last.scaleIdx - )); + let state = timelines.value[currentTimelineIdx.value]; + timelines.value.splice(currentTimelineIdx.value, 0, new TimelineState( + nextTimelineId, state.startDate, state.endDate, state.startOffset, state.endOffset, state.scaleIdx)); } - timelineTargets.value.push([null, false]); - nextTimelineId++; + timelineTargets.value.splice(currentTimelineIdx.value, 0, [null, false]); + currentTimelineIdx.value += 1; + nextTimelineId += 1; } onMounted(addTimeline); function onTimelineChg(state: TimelineState, idx: number){ timelines.value[idx] = state; + currentTimelineIdx.value = idx; } // For timeline addition/removal @@ -123,6 +125,9 @@ function onTimelineClose(idx: number){ } timelines.value.splice(idx, 1); timelineTargets.value.splice(idx, 1); + if (currentTimelineIdx.value >= idx){ + currentTimelineIdx.value = Math.max(0, idx - 1); + } } // For storing and looking up events @@ -319,9 +324,8 @@ const timelineTargets = ref([] as [HistEvent | null, boolean][]); // For communi function onSearch(event: HistEvent){ searchOpen.value = false; // Trigger jump in endmost timeline - let timelineIdx = timelineTargets.value.length - 1; - let oldFlag = timelineTargets.value[timelineIdx]; - timelineTargets.value.splice(timelineIdx, 1, [event, !oldFlag[1]]); + let oldVal = timelineTargets.value[currentTimelineIdx.value]; + timelineTargets.value.splice(currentTimelineIdx.value, 1, [event, !oldVal[1]]); } // For settings modal @@ -330,6 +334,10 @@ const settingsOpen = ref(false); // For help modal const helpOpen = ref(false); +// +const modalOpen = computed(() => + (infoModalData.value != null || searchOpen.value || settingsOpen.value || helpOpen.value)); + // For resize handling let lastResizeHdlrTime = 0; // Used to throttle resize handling let afterResizeHdlr = 0; // Used to trigger handler after ending a run of resize events @@ -366,6 +374,10 @@ function onKeyDown(evt: KeyboardEvent){ infoModalData.value = null; } else if (searchOpen.value){ searchOpen.value = false; + } else if (settingsOpen.value){ + settingsOpen.value = false; + } else if (helpOpen.value){ + helpOpen.value = false; } } else if (evt.key == 'f' && evt.ctrlKey){ evt.preventDefault(); @@ -373,6 +385,36 @@ function onKeyDown(evt: KeyboardEvent){ if (!searchOpen.value){ searchOpen.value = true; } + } else if (evt.key.startsWith('Arrow') && !modalOpen.value && !evt.shiftKey){ + if (evt.key == 'ArrowUp'){ + if (!vert.value){ + if (currentTimelineIdx.value > 0){ + currentTimelineIdx.value -= 1; + } + } + } else if (evt.key == 'ArrowDown'){ + if (!vert.value){ + if (currentTimelineIdx.value < timelines.value.length - 1){ + currentTimelineIdx.value += 1; + } + } + } else if (evt.key == 'ArrowLeft'){ + if (vert.value){ + if (currentTimelineIdx.value > 0){ + currentTimelineIdx.value -= 1; + } + } + } else if (evt.key == 'ArrowRight'){ + if (vert.value){ + if (currentTimelineIdx.value < timelines.value.length - 1){ + currentTimelineIdx.value += 1; + } + } + } + } else if (evt.key == '+' && !modalOpen.value){ + onTimelineAdd(); + } else if (evt.key == 'Delete' && !modalOpen.value){ + onTimelineClose(currentTimelineIdx.value); } } onMounted(() => { diff --git a/src/components/TimeLine.vue b/src/components/TimeLine.vue index 1583aef..d6bb36a 100644 --- a/src/components/TimeLine.vue +++ b/src/components/TimeLine.vue @@ -58,7 +58,7 @@
-
+
{{timelinePosStr}}
@@ -71,7 +71,7 @@