@@ -15,6 +15,7 @@ import { useGlobalLayerColorConfig } from '@/src/composables/useGlobalLayerColor
1515import { usePaintToolStore } from ' @/src/store/tools/paint' ;
1616import { Maybe } from ' @/src/types' ;
1717import { reactive , ref , computed , watch , toRaw } from ' vue' ;
18+ import { useMultiSelection } from ' @/src/composables/useMultiSelection' ;
1819
1920const UNNAMED_GROUP_NAME = ' Unnamed Segment Group' ;
2021
@@ -160,6 +161,62 @@ function openSaveDialog(id: string) {
160161 saveId .value = id ;
161162 saveDialog .value = true ;
162163}
164+
165+ const segGroupIds = computed (() =>
166+ currentSegmentGroups .value .map ((group ) => group .id )
167+ );
168+
169+ const { selected, selectedAll, selectedSome } = useMultiSelection (segGroupIds );
170+
171+ // ensure currentSegmentGroupID is always in selected
172+ watch (
173+ // includes currentImageID to reselect when switching images because currentSegmentGroupID is not updated on image change
174+ [currentSegmentGroupID , currentImageID ],
175+ () => {
176+ const groupId = currentSegmentGroupID .value ;
177+ if (! groupId ) return ;
178+ selected .value = [groupId ];
179+ },
180+ { immediate: true }
181+ );
182+
183+ function toggleSelectAll() {
184+ if (selectedAll .value && currentSegmentGroupID .value ) {
185+ selected .value = [currentSegmentGroupID .value ];
186+ } else if (selectedAll .value ) {
187+ selected .value = [];
188+ } else {
189+ selected .value = segGroupIds .value ;
190+ }
191+ }
192+
193+ const allHidden = computed (() => {
194+ return selected .value
195+ .map ((id ) => currentSegmentGroups .value .find ((group ) => id === group .id ))
196+ .filter ((group ): group is NonNullable <typeof group > => group != null )
197+ .every ((group ) => ! group .visibility );
198+ });
199+
200+ function toggleGlobalVisibility() {
201+ const shouldShow = allHidden .value ;
202+ selected .value .forEach ((id ) => {
203+ const group = currentSegmentGroups .value .find ((g ) => g .id === id );
204+ if (group ) {
205+ const { sampledConfig, updateConfig } = useGlobalLayerColorConfig (id );
206+ const currentBlend = sampledConfig .value ! .config ! .blendConfig ;
207+ updateConfig ({
208+ blendConfig: {
209+ ... currentBlend ,
210+ visibility: shouldShow ,
211+ },
212+ });
213+ }
214+ });
215+ }
216+
217+ function deleteSelected() {
218+ selected .value .forEach ((id ) => deleteGroup (id ));
219+ }
163220 </script >
164221
165222<template >
@@ -203,63 +260,107 @@ function openSaveDialog(id: string) {
203260 </v-list >
204261 </v-menu >
205262 </div >
206- <v-divider class =" my-4" />
207263
208264 <segment-group-opacity
209265 v-if =" currentSegmentGroupID"
210266 :group-id =" currentSegmentGroupID"
267+ :selected =" selected"
211268 />
212- <v-radio-group
213- v-model =" currentSegmentGroupID"
214- hide-details
215- density =" comfortable"
216- class =" my-1 segment-group-list"
217- >
218- <v-radio
269+
270+ <div class =" d-flex align-center" v-if =" currentSegmentGroups.length > 0" >
271+ <v-checkbox
272+ class =" ml-3"
273+ :indeterminate =" selectedSome && !selectedAll"
274+ label =" Select All"
275+ :model-value =" selectedAll"
276+ @update:model-value =" toggleSelectAll"
277+ density =" compact"
278+ hide-details
279+ />
280+ <v-btn
281+ icon
282+ variant =" text"
283+ :disabled =" selected.length === 0"
284+ @click.stop =" toggleGlobalVisibility"
285+ >
286+ <v-icon v-if =" allHidden" >mdi-eye-off</v-icon >
287+ <v-icon v-else >mdi-eye</v-icon >
288+ <v-tooltip location =" top" activator =" parent" >
289+ {{ allHidden ? 'Show' : 'Hide' }} selected
290+ </v-tooltip >
291+ </v-btn >
292+ <v-btn
293+ icon
294+ variant =" text"
295+ :disabled =" selected.length === 0"
296+ @click.stop =" deleteSelected"
297+ >
298+ <v-icon >mdi-delete</v-icon >
299+ <v-tooltip location =" top" activator =" parent" >
300+ Delete selected
301+ </v-tooltip >
302+ </v-btn >
303+ </div >
304+ <v-list density =" comfortable" class =" my-1 segment-group-list" >
305+ <v-list-item
219306 v-for =" group in currentSegmentGroups"
220307 :key =" group.id"
221- :value =" group.id"
308+ :active =" currentSegmentGroupID === group.id"
309+ @click =" currentSegmentGroupID = group.id"
222310 >
223- <template #label >
224- <div class =" d-flex flex-row align-center w-100" :title =" group.name" >
225- <span class =" group-name" >{{ group.name }}</span >
226- <v-spacer />
227- <v-btn
228- icon
229- variant =" flat"
230- size =" small"
231- @click.stop =" group.toggleVisibility"
311+ <div class =" d-flex flex-row align-center w-100" :title =" group.name" >
312+ <v-checkbox
313+ class =" no-grow mr-4"
314+ density =" compact"
315+ hide-details
316+ @click.stop
317+ :value =" group.id"
318+ v-model =" selected"
319+ :disabled =" group.id === currentSegmentGroupID"
320+ />
321+ <span class =" group-name" >{{ group.name }}</span >
322+ <v-spacer />
323+ <v-btn
324+ icon
325+ variant =" text"
326+ size =" small"
327+ @click.stop =" group.toggleVisibility"
328+ >
329+ <v-icon v-if =" group.visibility" style =" pointer-events : none "
330+ >mdi-eye</v-icon
232331 >
233- <v-icon v-if =" group.visibility" style =" pointer-events : none "
234- >mdi-eye</v-icon
235- >
236- <v-icon v-else style =" pointer-events : none " >mdi-eye-off</v-icon >
237- <v-tooltip location =" left" activator =" parent" >{{
238- group.visibility ? 'Hide' : 'Show'
239- }}</v-tooltip >
240- </v-btn >
241- <v-btn
242- icon =" mdi-content-save"
243- size =" small"
244- variant =" flat"
245- @click.stop =" openSaveDialog(group.id)"
246- ></v-btn >
247- <v-btn
248- icon =" mdi-pencil"
249- size =" small"
250- variant =" flat"
251- @click.stop =" startEditing(group.id)"
252- ></v-btn >
253- <v-btn
254- icon =" mdi-delete"
255- size =" small"
256- variant =" flat"
257- @click.stop =" deleteGroup(group.id)"
258- ></v-btn >
259- </div >
260- </template >
261- </v-radio >
262- </v-radio-group >
332+ <v-icon v-else style =" pointer-events : none " >mdi-eye-off</v-icon >
333+ <v-tooltip location =" left" activator =" parent" >
334+ {{ group.visibility ? 'Hide' : 'Show' }}
335+ </v-tooltip >
336+ </v-btn >
337+ <v-btn
338+ icon =" mdi-content-save"
339+ size =" small"
340+ variant =" text"
341+ @click.stop =" openSaveDialog(group.id)"
342+ />
343+ <v-btn
344+ icon =" mdi-pencil"
345+ size =" small"
346+ variant =" text"
347+ @click.stop =" startEditing(group.id)"
348+ />
349+ <v-btn
350+ icon =" mdi-delete"
351+ size =" small"
352+ variant =" text"
353+ @click.stop =" deleteGroup(group.id)"
354+ />
355+ </div >
356+ </v-list-item >
357+ <v-list-item v-if =" currentSegmentGroups.length === 0" >
358+ <div class =" text-center text-grey-darken-1 py-4 w-100" >
359+ Create a segment group with the above buttons or click the paint tool
360+ </div >
361+ </v-list-item >
362+ </v-list >
363+
263364 <v-divider class =" my-4" />
264365 </div >
265366 <div v-else class =" text-center text-caption" >No selected image</div >
@@ -306,5 +407,11 @@ function openSaveDialog(id: string) {
306407 white-space : nowrap ;
307408 overflow : hidden ;
308409 text-overflow : ellipsis ;
410+ padding-right : 10px ;
411+ text-align : left ;
412+ }
413+
414+ .no-grow {
415+ flex : 0 0 auto ;
309416}
310417 </style >
0 commit comments