@@ -41,7 +41,7 @@ Panel {
4141 property int nbMeshroomScenes: 0
4242 property int nbDraggedFiles: 0
4343
44- signal removeImageRequest (var attribute )
44+ signal removeSelectedImagesRequest (var objects )
4545 signal allViewpointsCleared ()
4646 signal filesDropped (var drop)
4747
@@ -53,6 +53,7 @@ Panel {
5353
5454 function onCameraInitChanged () {
5555 nodesCB .currentIndex = root .cameraInitIndex
56+ sortedModel .clearMultiSelection (false )
5657 }
5758 }
5859
@@ -230,6 +231,43 @@ Panel {
230231 }
231232
232233 property int selectedIndex: - 1
234+ property var selectedIndices: []
235+
236+ function toggleIndex (idx ) {
237+ var newArr = selectedIndices .slice ()
238+ var pos = newArr .indexOf (idx)
239+ if (pos >= 0 ) {
240+ newArr .splice (pos, 1 )
241+ } else {
242+ newArr .push (idx)
243+ }
244+ selectedIndices = newArr
245+ }
246+
247+ function selectRange (from , to ) {
248+ var newArr = []
249+ var start = Math .min (from, to)
250+ var end = Math .max (from, to)
251+ for (var i = start; i <= end; i++ ) {
252+ newArr .push (i)
253+ }
254+ selectedIndices = newArr
255+ }
256+
257+ function clearMultiSelection (keepPosition ) {
258+ if (keepPosition) {
259+ // Pick the lowest selected index as the landing position; after removal
260+ // the next surviving item slides up to that slot.
261+ // Clamp to the last remaining item in case the selection was at the tail.
262+ var sortedSel = selectedIndices .slice ().sort (function (a , b ){ return a - b })
263+ var remainingCount = count - selectedIndices .length
264+ selectedIndex = Math .min (sortedSel[0 ], remainingCount - 1 )
265+ selectedIndices = [selectedIndex]
266+ } else {
267+ selectedIndex = - 1
268+ selectedIndices = []
269+ }
270+ }
233271
234272 delegate: ImageDelegate {
235273 id: imageDelegate
@@ -247,21 +285,93 @@ Panel {
247285
248286 parentModel: sortedModel
249287
250- onPressed: {
288+ onPressed : function (mouse ) {
289+ if (mouse .button !== Qt .LeftButton )
290+ return
251291 if (layoutLoader .item ) {
252- layoutLoader .item .currentIndex = DelegateModel .filteredIndex
253- sortedModel .selectedIndex = DelegateModel .filteredIndex
292+ var idx = DelegateModel .filteredIndex
293+ if (mouse .modifiers & Qt .ShiftModifier && sortedModel .selectedIndex >= 0 ) {
294+ // Range select from last selectedIndex to clicked item
295+ sortedModel .selectRange (sortedModel .selectedIndex , idx)
296+ } else if (mouse .modifiers & Qt .ControlModifier ) {
297+ // Toggle this item's selection
298+ sortedModel .toggleIndex (idx)
299+ // If the item is being removed from the selection, then we should return
300+ // before setting the current index: this prevents highlighting the item which is being
301+ // removed, as it could be confusing for the user
302+ if (sortedModel .selectedIndices .indexOf (idx) < 0 ) {
303+ if (sortedModel .selectedIndices .length === 0 ) {
304+ // Last item deselected: clear the viewer entirely
305+ sortedModel .selectedIndex = - 1
306+ layoutLoader .item .currentIndex = - 1
307+ _currentScene .selectedViewId = " -1"
308+ } else if (idx === sortedModel .selectedIndex ) {
309+ // The currently viewed item was deselected: move to the
310+ // closest remaining selected item.
311+ var remaining = sortedModel .selectedIndices
312+ var next = remaining[0 ]
313+ var minDist = Math .abs (remaining[0 ] - idx)
314+ for (var r = 1 ; r < remaining .length ; r++ ) {
315+ var dist = Math .abs (remaining[r] - idx)
316+ if (dist < minDist) {
317+ minDist = dist
318+ next = remaining[r]
319+ }
320+ }
321+ sortedModel .selectedIndex = next
322+ layoutLoader .item .currentIndex = next
323+ }
324+ return
325+ }
326+ } else {
327+ // Normal click: clear multi-selection, select only this item
328+ sortedModel .selectedIndices = [idx]
329+ }
330+ // Update selectedIndex before currentIndex to prevent onCurrentItemChanged
331+ // from incorrectly resetting the multi-selection
332+ sortedModel .selectedIndex = idx
333+ layoutLoader .item .currentIndex = idx
254334 }
255335 }
256336
257- function sendRemoveRequest () {
337+ function sendRemoveSelectedRequest () {
258338 if (readOnly)
259339 return
260340
261- root .removeImageRequest (object)
262-
341+ // Capture delegate-scope references immediately: this prevents falling into
342+ // cases where "sortedModel" is unresolvable because the delegate has been destroyed before
343+ // the line accessing "sortedModel" is reached
344+ var model = sortedModel
345+ var view = root .galleryGrid
346+
347+ // If all the images are selected, we can just remove all of them at once
348+ if (model .selectedIndices .length === m .viewpoints .count ) {
349+ removeAllImages ()
350+ return
351+ }
352+
353+ var objects = []
354+ for (var i = 0 ; i < model .selectedIndices .length ; i++ ) {
355+ var obj = model .getObjectAt (model .selectedIndices [i])
356+ if (obj)
357+ objects .push (obj)
358+ }
359+ if (objects .length > 0 ) {
360+ root .removeSelectedImagesRequest (objects)
361+ model .clearMultiSelection (true )
362+
363+ // Restore a sensible position once the model has finished updating
364+ var targetIndex = model .selectedIndex
365+ Qt .callLater (function () {
366+ if (targetIndex >= 0 && view) {
367+ view .currentIndex = targetIndex
368+ view .makeCurrentItemVisible ()
369+ }
370+ })
371+ }
372+
263373 // If the last image has been removed, make sure the viewpoints and intrinsics are reset
264- if (m .viewpoints .count === 0 )
374+ if (m .viewpoints !== undefined && m . viewpoints .count === 0 )
265375 root .allViewpointsCleared ()
266376 }
267377
@@ -270,12 +380,12 @@ Panel {
270380 _currentScene .selectedViewId = " -1"
271381 }
272382
273- onRemoveRequest : sendRemoveRequest ()
383+ onRemoveSelectedRequest : sendRemoveSelectedRequest ()
274384 Keys .onPressed : function (event ) {
275385 if (event .key === Qt .Key_Delete && event .modifiers === Qt .ShiftModifier ) {
276386 removeAllImages ()
277387 } else if (event .key === Qt .Key_Delete ) {
278- sendRemoveRequest ()
388+ sendRemoveSelectedRequest ()
279389 }
280390 }
281391 onRemoveAllImagesRequest: {
@@ -364,7 +474,6 @@ Panel {
364474 item .thumbnailSizeSlider = thumbnailSizeSlider
365475
366476 // Connect signals
367- item .removeImageRequest .connect (root .removeImageRequest )
368477 item .allViewpointsCleared .connect (root .allViewpointsCleared )
369478
370479 // Restore currentIndex (before connecting signals to avoid unwanted selection change)
0 commit comments