Skip to content

Commit be24d8b

Browse files
Enable bulk track selection for operations (#1506)
* Listen to ctrl click events on annotations * Handle ctrl+click tracks * Force sidebar into attribute view for multi tracks * Implement multi-delete tracks * Allow users to change type for all selected tracks * Add additional filtering to tracks This prevents multi track deletion from attempting to auto select a track that no longer exists because it was included in a bulk delete operation. * Allow TypePicker to emit on input * Allow skipping auto select after track removal * Enable ctrl+click from track list in sidebar * Enable ctrl+click from the event viewer * Deselect selected track with second ctrl+click * Allow deselecting individual tracks in the sidebar * Don't use `null` as default for modifiers * Respect `lockType` setting for new TypePicker * Use handler instead of re-emitting select event * Use correct type annotation for `trackId` Co-authored-by: Bryon Lewis <61746913+BryonLewis@users.noreply.github.com> * Swap sidebar view after deleting multiple tracks * Use correct types ref for new type picker Co-authored-by: Bryon Lewis <61746913+BryonLewis@users.noreply.github.com> --------- Co-authored-by: Bryon Lewis <61746913+BryonLewis@users.noreply.github.com>
1 parent 40e3968 commit be24d8b

File tree

13 files changed

+228
-21
lines changed

13 files changed

+228
-21
lines changed

client/dive-common/components/ControlsContainer.vue

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ import {
1414
Timeline,
1515
} from 'vue-media-annotator/components';
1616
import { clientSettings } from 'dive-common/store/settings';
17-
import { useAttributesFilters, useCameraStore, useSelectedCamera } from '../../src/provides';
17+
import {
18+
useHandler,
19+
useAttributesFilters,
20+
useCameraStore,
21+
useSelectedCamera,
22+
} from '../../src/provides';
1823
1924
export default defineComponent({
2025
components: {
@@ -47,6 +52,7 @@ export default defineComponent({
4752
},
4853
},
4954
setup(_, { emit }) {
55+
const handler = useHandler();
5056
const currentView = ref('Detections');
5157
const ticks = ref([0.25, 0.5, 0.75, 1.0, 2.0, 4.0, 8.0]);
5258
const cameraStore = useCameraStore();
@@ -104,6 +110,10 @@ export default defineComponent({
104110
clientSettings.timelineCountSettings.defaultView = countView.value;
105111
};
106112
113+
function handleSelectTrack(trackId: number, modifiers?: { ctrl: boolean }) {
114+
handler.trackSelect(trackId, false, modifiers);
115+
}
116+
107117
const {
108118
maxFrame, frame, seek, volume, setVolume, setSpeed, speed,
109119
} = injectAggregateController().value;
@@ -127,6 +137,7 @@ export default defineComponent({
127137
countView,
128138
help,
129139
toggleCountView,
140+
handleSelectTrack,
130141
};
131142
},
132143
});
@@ -429,7 +440,7 @@ export default defineComponent({
429440
:data="eventChartData"
430441
:client-width="clientWidth"
431442
:margin="margin"
432-
@select-track="$emit('select-track', $event)"
443+
@select-track="handleSelectTrack"
433444
/>
434445
<event-chart
435446
v-if="currentView === 'Groups'"

client/dive-common/components/Sidebar.vue

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@ import {
44
defineComponent,
55
reactive,
66
toRef,
7+
watch,
78
} from 'vue';
89
910
import { FilterList, TrackList } from 'vue-media-annotator/components';
1011
import {
1112
useCameraStore,
12-
useHandler, useReadOnlyMode, useTrackFilters, useTrackStyleManager,
13+
useHandler,
14+
useReadOnlyMode,
15+
useTrackFilters,
16+
useTrackStyleManager,
17+
useEditingMultiTrack,
1318
} from 'vue-media-annotator/provides';
1419
1520
import { clientSettings } from 'dive-common/store/settings';
@@ -45,7 +50,12 @@ export default defineComponent({
4550
const readOnlyMode = useReadOnlyMode();
4651
const cameraStore = useCameraStore();
4752
const multiCam = cameraStore.camMap.value.size > 1;
48-
const { toggleMerge, commitMerge, groupAdd } = useHandler();
53+
const {
54+
toggleMerge,
55+
commitMerge,
56+
groupAdd,
57+
deleteSelectedTracks,
58+
} = useHandler();
4959
const { visible } = usePrompt();
5060
const trackSettings = toRef(clientSettings, 'trackSettings');
5161
const typeSettings = toRef(clientSettings, 'typeSettings');
@@ -84,12 +94,26 @@ export default defineComponent({
8494
return trap;
8595
});
8696
97+
const editingMultiTrack = useEditingMultiTrack();
98+
watch(editingMultiTrack, () => {
99+
if (editingMultiTrack.value) {
100+
data.currentTab = 'attributes';
101+
}
102+
});
103+
104+
async function handleDeleteSelectedTracks() {
105+
await deleteSelectedTracks();
106+
data.currentTab = 'attributes';
107+
swapTabs();
108+
}
109+
87110
return {
88111
/* data */
89112
data,
90113
allTypesRef,
91114
commitMerge,
92115
groupAdd,
116+
handleDeleteSelectedTracks,
93117
mouseTrap,
94118
trackFilterControls,
95119
trackSettings,
@@ -170,6 +194,7 @@ export default defineComponent({
170194
@back="swapTabs"
171195
@commit-merge="commitMerge"
172196
@create-group="groupAdd"
197+
@delete-selected-tracks="handleDeleteSelectedTracks"
173198
/>
174199
</v-slide-x-transition>
175200
</template>

client/dive-common/components/TrackDetailsPanel.vue

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
useReadOnlyMode,
1818
useTrackStyleManager,
1919
useEditingGroupId,
20+
useEditingMultiTrack,
2021
useGroupFilterControls,
2122
useCameraStore,
2223
useSelectedCamera,
@@ -31,6 +32,7 @@ import AttributeInput from 'dive-common/components/Attributes/AttributeInput.vue
3132
import AttributeEditor from 'dive-common/components/Attributes/AttributeEditor.vue';
3233
import AttributeSubsection from 'dive-common/components/Attributes/AttributesSubsection.vue';
3334
import ConfidenceSubsection from 'dive-common/components/ConfidenceSubsection.vue';
35+
import { clientSettings } from 'dive-common/store/settings';
3436
3537
export default defineComponent({
3638
components: {
@@ -70,6 +72,11 @@ export default defineComponent({
7072
const selectedCamera = useSelectedCamera();
7173
const { allTypes: allGroupTypesRef } = useGroupFilterControls();
7274
const multiSelectList = useMultiSelectList();
75+
const editingMultiTrack = useEditingMultiTrack();
76+
const multiTrackType: Ref<string> = ref('unknown');
77+
const updateMultiTrackType = (newValue: string) => {
78+
multiTrackType.value = newValue;
79+
};
7380
const multiSelectInProgress = computed(() => multiSelectList.value.length > 0);
7481
const {
7582
trackSelectNext, trackSplit, removeTrack, unstageFromMerge,
@@ -214,12 +221,19 @@ export default defineComponent({
214221
];
215222
});
216223
224+
function updateSelectedTracksType() {
225+
multiSelectList.value.forEach((trackId: number) => {
226+
cameraStore.setTrackType(trackId, multiTrackType.value);
227+
});
228+
}
229+
217230
return {
218231
selectedTrackIdRef,
219232
editingGroupIdRef,
220233
editingGroup,
221234
readOnlyMode,
222235
multiCam,
236+
clientSettings,
223237
/* Attributes */
224238
attributes,
225239
/* Editing */
@@ -233,6 +247,9 @@ export default defineComponent({
233247
selectedTrackList,
234248
multiSelectList,
235249
multiSelectInProgress,
250+
editingMultiTrack,
251+
multiTrackType,
252+
updateMultiTrackType,
236253
/* Update functions */
237254
closeEditor,
238255
editAttribute,
@@ -248,6 +265,7 @@ export default defineComponent({
248265
removeGroup,
249266
toggleMerge,
250267
unstageFromMerge,
268+
updateSelectedTracksType,
251269
};
252270
},
253271
});
@@ -495,6 +513,54 @@ export default defineComponent({
495513
<v-spacer />
496514
Abort (esc)
497515
</v-btn>
516+
<v-btn
517+
v-if="editingMultiTrack"
518+
color="error"
519+
class="mx-2 mb-2 grow"
520+
:disabled="readOnlyMode"
521+
depressed
522+
x-small
523+
@click="$emit('delete-selected-tracks')"
524+
>
525+
<v-icon
526+
class="pr-1"
527+
small
528+
>
529+
mdi-delete
530+
</v-icon>
531+
<v-spacer />
532+
Delete selected tracks
533+
</v-btn>
534+
<div
535+
v-if="editingMultiTrack"
536+
class="d-flex justify-center align-center mb-2 mx-2"
537+
width="100%"
538+
>
539+
<v-spacer />
540+
<v-label class="mx-2">
541+
Type:
542+
</v-label>
543+
<TypePicker
544+
:value="multiTrackType"
545+
:all-types="allTypesRef"
546+
:read-only-mode="readOnlyMode"
547+
:lock-types="clientSettings.typeSettings.lockTypes"
548+
selected
549+
update-on-input
550+
@input="updateMultiTrackType"
551+
/>
552+
</div>
553+
<v-btn
554+
class="mx-2 mb-2"
555+
:disabled="readOnlyMode"
556+
color="primary"
557+
depressed
558+
x-small
559+
@click="updateSelectedTracksType"
560+
>
561+
<v-spacer />
562+
Update type for selected tracks
563+
</v-btn>
498564
</div>
499565
<confidence-subsection
500566
v-if="editingGroupIdRef === null"

client/dive-common/components/Viewer.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ export default defineComponent({
227227
multiSelectActive,
228228
selectedFeatureHandle,
229229
selectedTrackId,
230+
editingMultiTrack,
230231
editingGroupId,
231232
handler,
232233
editingMode,
@@ -799,6 +800,7 @@ export default defineComponent({
799800
selectedCamera,
800801
selectedKey,
801802
selectedTrackId,
803+
editingMultiTrack,
802804
editingGroupId,
803805
time,
804806
trackFilters,
@@ -1146,7 +1148,6 @@ export default defineComponent({
11461148
v-bind="{
11471149
lineChartData, eventChartData, groupChartData, datasetType,
11481150
}"
1149-
@select-track="handler.trackSelect"
11501151
/>
11511152
</div>
11521153
<div

0 commit comments

Comments
 (0)