Skip to content

Add multi-object canvas selection - #11084

Open
senketsukamui wants to merge 48 commits into
developfrom
gp/multiselection
Open

Add multi-object canvas selection#11084
senketsukamui wants to merge 48 commits into
developfrom
gp/multiselection

Conversation

@senketsukamui

@senketsukamui senketsukamui commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Motivation and context

This PR introduces multi-object selection and batch operations in the 2D annotation workspace.

It addresses one of the most requested CVAT features:

Closes #2340

Users can now:

  • Select multiple objects using a selection bounding box.
  • Add or remove individual objects with Ctrl + click.
  • Select an object range in the Objects sidebar with Shift + click.
  • Select all visible objects with Ctrl + A.
  • Move selected movable objects together using the selection bounding box.
  • Keep locked and pinned objects selected but stationary during movement.
  • Change a common applicable label for all selected objects.
  • Copy, paste, and delete a selection.
  • Lock, unlock, pin, or unpin selected objects.
  • Use label shortcuts with the active selection.
  • Restore selection changes through Undo and Redo.
  • Restore a selection after navigating away from its frame and returning.
  • Clear the selection by clicking outside it, pressing Escape, or changing frames.

The selection is represented by a visible bounding box and a SELECTION (N) label. Its context menu exposes batch actions and disables operations that cannot be applied to every selected object.

Batch updates, deletion, creation, and movement are recorded as single history actions where applicable. Ground-truth and unsupported objects are validated before a batch operation begins.

The implementation also adds immutable selection clipboard snapshots, transformed bounding-box checks for rotated objects, and shared helpers for selection state and batch lock/pin behavior.

How has this been tested?

The following checks were run locally:

  • TypeScript checks for cvat-core, cvat-canvas, and cvat-ui.
  • ESLint for all changed TypeScript and React files.
  • git diff --check.
  • Existing label shortcut E2E scenarios were extended for multi-selection.

The following workflows were also tested manually in the 2D annotation workspace:

  • Bounding-box selection and individual Ctrl + click selection.
  • Shift + click range selection in the Objects sidebar.
  • Selecting all visible objects with Ctrl + A.
  • Adding and removing objects from an existing selection.
  • Moving selections containing movable, locked, and pinned objects.
  • Batch copy, paste, deletion, label change, lock, unlock, pin, and unpin.
  • Undo and Redo for selection changes and batch operations.
  • Restoring a selection after frame navigation.
  • Selection behavior for rectangles, polygons, polylines, ellipses, masks, tracks, and tags.

Checklist

  • I submit my changes into the develop branch
  • I have created a changelog fragment
  • I have updated the documentation accordingly
  • I have added tests to cover my changes
  • I have linked related issues (see GitHub docs)

License

  • I submit my code changes under the same MIT License that covers the project.
    Feel free to contact the maintainers if that's a concern.

@senketsukamui

senketsukamui commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Proposed testing scenarios:

Creating and refining a selection

  • Shift-drag creates a selection only from objects fully contained by the bbox.
  • Partially intersecting objects are excluded.
  • Rotated objects use their transformed bounds.
  • Hidden, filtered, outside, and hidden-layer objects are excluded.
  • A one-object selection still shows SELECTION (1) and its bbox.
  • An empty bbox clears the existing selection.
  • Ctrl-click adds and removes individual objects on canvas and in the Objects tab.
  • Ctrl-click works through the selection bbox.
  • Ctrl combined with Shift or Alt does not trigger object selection.

Selection mode

  • Clicking the Select tool enables persistent bbox selection.
  • Shift-drag from cursor mode activates the Select tool.
  • Releasing Shift does not leave selection mode.
  • The toolbar button remains highlighted while objects are selected.
  • Clicking the highlighted Select tool returns to cursor mode.

Clearing and replacing selection

  • Clicking outside the bbox clears the selection.
  • Escape clears the selection.
  • Changing frame clears the visible selection.
  • Clicking an outside object clears the selection and activates that object.
  • Hovering outside objects does not activate them while selection is active.
  • Hovering Objects-tab entries does not activate them while selection is active.
  • Clicking an Objects-tab entry without a modifier clears selection and activates it.

Selection presentation

  • Selected shapes retain their label colors.
  • Selected objects use increased selected opacity.
  • The combined bbox is black and includes padding around the objects.
  • Masks have a visible selection bbox.
  • SELECTION (N) updates as membership changes.
  • The actions menu opens using both the ellipsis and right-click.
  • The selection context menu stays open while using its label selector.

Moving selected objects

  • Dragging the combined bbox moves all movable members by the same delta.
  • Dragging a selected movable object moves the remaining movable members.
  • Relative geometry and z-order remain unchanged.
  • Selection remains active after movement.
  • Locked, pinned, ground-truth, and skeleton members remain selected but stationary.
  • A selection containing only stationary members cannot be moved.
  • Masks move correctly by updating their RLE bounding coordinates.
  • Undo reverts the complete movement in one action; redo reapplies it.

Deleting a selection

  • Delete and Backspace remove an unlocked selection.
  • A locked member prevents regular deletion atomically.
  • Shift+Delete and Shift+Backspace force-delete locked members.
  • Ground-truth members prevent the complete deletion.
  • Undo restores all deleted objects and their selection.
  • Redo deletes all objects again and clears the selection.

Copying and pasting

  • Ctrl+C copies the complete selection.
  • Ctrl+V creates all copied objects in one operation.
  • Relative positions, labels, attributes, descriptions, object types, and rotations are preserved.
  • Shapes are offset by 20 pixels and placed on the current z-layer.
  • Tags are copied without a geometry offset.
  • Tracks remain tracks.
  • Pasted objects become the active selection.
  • Changing source objects after copying does not mutate clipboard data.
  • Undo and redo treat the paste as one history action.

Batch properties

  • Context-menu lock applies to every selected object; the next action unlocks all.
  • Context-menu pin applies to every compatible object; the next action unpins all.
  • S L toggles lock/unlock for the selection.
  • S P toggles pin/unpin for the selection.
  • Pin is rejected for selections containing locked, ground-truth, tag, or points objects.
  • Lock and label changes support tags.
  • Failed batch operations leave every member unchanged.

Batch label changes

  • The menu shows the current label when all members share it.
  • Mixed selections display Multiple labels.
  • Only labels applicable to every member are available.
  • No common label disables the selector.
  • Locked, ground-truth, and skeleton members disable label changes.
  • Ctrl+number changes the label for a compatible selection.
  • An incompatible shortcut displays the warning and changes nothing.

Selection history

  • Undo and redo restore each add/remove selection change.
  • Undo restores a selection after clicking outside or pressing Escape.
  • A new selection after Undo clears the redo stack.
  • Select objects on frame A, move to frame B, return to A, then Undo restores the frame-A selection.
  • Undo/redo frame navigation does not create extra selection-history entries.
  • Move, delete, paste, lock, pin, and label updates each use one history action.

Objects sidebar

  • Shift-click selects a continuous range using the visible sort order.
  • Range selection works with ID, label, updated-time, and layer ordering.
  • Collapsed layer members are excluded from the Shift range.
  • Ctrl+A selects all visible shapes and tracks.
  • Ctrl+A excludes tags, filtered, hidden, outside, and hidden-layer objects.
  • Ctrl+A does not run while an input, textarea, selector, or editable field has focus.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Ctrl+click on a shape
  2. Click on ... - nothing happens
    The selection menu is only opened on the second click on .... Also, it's never closed on subsequent clicks.
    Note: if I make selection with Shift+drag it opens the selection menu on the first click on ...

title={labelSelectorDisabled ? labelSelectorDisabledReason : undefined}
>
{applicableLabels.length ? (
<LabelSelector

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After selecting a new label or just opening/closing labels selector the expander icon becomes invisible on hover:
Before:

Image

After:

Image

}}
/>

{selectionMenuPosition && (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All selection actions but Copy drop the selection. I'm not sure it's convenient. E.g. what if you want to change both "Pinned" and "Label" for the same selection?

}}
/>

{selectionMenuPosition && (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy copies the selection but the only way to actually Paste is is via the Ctrl+v shortcut. It's a bit confusing and not consistent with how Make a copy works for one shape.

}}
/>

{selectionMenuPosition && (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When all selected items share the same label and some are locked the tooltip about why changing label is disable is only displayed when hovering outside of the selected value.
Hover over the selected value:
Image
Hover over the frame:
Image

It is displayed when labels are different though:
Hover over the selected value:
Image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Click Select objects mode
  2. Click any shape so 1-shape multi-selection is displayed
  3. Right click on the selection bbox
    Selection is dropped instead of showing the selection menu

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit confusing that it's not possible to drag the selection by its SELECTION(N) header only the bbox.

Image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When multi-selecting tracks the selection is dropped when going to the next frame.
Regular active track remains active when moving to the next frame.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why it should be a modal for multi-selection?
Overall, why can't we make the selection controls look the same as the control displayed when a single shape is right clicked instead of the context menu?
It will be consistent and familiar to the user.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Select multiple shapes with the same label
  2. Edit attributes, click apply
  3. Click Undo -> first it brings back the selection
  4. Click Undo again -> it reverts the attributes change
  5. Redo is not available at this point for some reason
    3+4 - should be single undo operation
    5 - Redo should be availble

SWITCH_SELECTION_LOCK: {
name: 'Lock/unlock selected objects',
description: 'Change locked state for all selected objects',
sequences: ['s l'],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need separate sequences for multi-selection actions? Can we reuse the active component sequences? Like simply l here, p for pinned etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea actually, I thought to make it separate like t l and t p for all objects, but I guess using l and p is better way

@senketsukamui

Copy link
Copy Markdown
Contributor Author

Also, how do we handle points for selection? I proposed allowing adding points to selection to move them all at once. What do you think about this behavior? I can also make so we can add individual points to the selection, but it's kinda complicated.

@lexiv0re

Copy link
Copy Markdown
Contributor

I think we definitely don't want to add individual points to selection, at least not with the way they are organized now.
As a user I'd expect the points shape to be movable as a part of the selection. So I'd suggest to add Pin property to the Points shape. In regular mode it would control whether individual points are movable. In multi-selection it would allow/disallow moving the whole set.

@bsekachev

bsekachev commented Sep 2, 2026

Copy link
Copy Markdown
Member

Hello

  1. Select a group
  2. Selection actions button never works from the first time (only from the second one) [bug]
  3. Double click on this button fits the object [bug]
  4. Finally open menu
  5. Zoom, drag image. Menu moves away from "..." [bug]
  6. Start dragging objects. Menu stays opened [bug]
  7. Select two masks. Lock them via "...". Objects cannot be moved - OK
  8. Lock all with T L or with button in side menu - all locked. OK.
  9. Unlock all with T L or with button in side menu - all unlocked. OK
  10. Start dragging masks - they can be dragged but after dragging return to initial place [bug]

UPD: probably latest related with that one of masks in the group slightly crossed the image border.

  1. This is not UX friendly solution.
  2. If we drag a single mask or bounding box, production behaviour is broken. [bug]

Production: mask/bounding box cut when partially moved outside of frame.
This branch: they returned to the initial position when partially moved outside of frame.

So, these are found for 5 minutes of using the feature.
Please, test your patch meticulously first.

@bsekachev

Copy link
Copy Markdown
Member
image

Icons swapped. Black lock icon means object is locked now. But in menu you implemented such icon means unlocked now.

@senketsukamui

senketsukamui commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Can you explain more? I don't think I get it. You mean that for Lock selection option is context menu we need to add unlocked icon?

@bsekachev

Copy link
Copy Markdown
Member
2026-09-02.11.46.42.AM.mov

One object get selected when switching lock/unlock.

@bsekachev

bsekachev commented Sep 2, 2026

Copy link
Copy Markdown
Member

Can you explain more? I don't think I get it. You mean that for Lock selection option is context menu we need to add unlocked icon?

image

One object is selected, right?
The object is not locked. Icon outlined (in sidebar).
But in selection menu icon is filled.

@senketsukamui

Copy link
Copy Markdown
Contributor Author

That makes sense, but in the sidebar icon is both button and the indicator of lock/unlock state of an object while inside selection context menu it's just a button that makes certain action. I'm not sure if it's a mismatch.

@bsekachev

Copy link
Copy Markdown
Member
Снимок экрана — 2026-09-02 в 11 56 21 AM

Similar issue when one object is locked, and one unlocked (both in selection).
We should keep behaviour like for all lock / unlock button.

If at least one unlocked, common button state is unlocked and switching it will lock objects in the group.

Please, check that logic is consistent for left part of functionality

@bsekachev

Copy link
Copy Markdown
Member

it's just a button that makes certain action. I'm not sure if it's a mismatch

It is not just a button that makes certain actions. This icon is switched after clicking the button. So, it is a button + indicator of current state.

@senketsukamui

Copy link
Copy Markdown
Contributor Author

Production: mask/bounding box cut when partially moved outside of frame.
This branch: they returned to the initial position when partially moved outside of frame.

Is it expected behavior? I fixed it manually because it seemed like a bug to me

@bsekachev

Copy link
Copy Markdown
Member

Is it expected behavior? I fixed it manually because it seemed like a bug to me

It is expected and very important. Without this, how could you drag a shape to an object on frame edge?

@senketsukamui

Copy link
Copy Markdown
Contributor Author

Is it expected behavior? I fixed it manually because it seemed like a bug to me

It is expected and very important. Without this, how could you drag a shape to an object on frame edge?

Yeah, I missed this. I will revert this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Selecting more than one object - Moving/Copying/Pasting or Assigning Label - Enhancement

4 participants