diff --git a/cvat/apps/engine/static/engine/js/shapeCollection.js b/cvat/apps/engine/static/engine/js/shapeCollection.js index 019fff33f9f3..9a1276e8b08e 100644 --- a/cvat/apps/engine/static/engine/js/shapeCollection.js +++ b/cvat/apps/engine/static/engine/js/shapeCollection.js @@ -621,10 +621,12 @@ class ShapeCollectionModel extends Listener { } } - switchAllLock() { + switchObjectsLock(labelId) { this.resetActive(); let value = true; - for (let shape of this._currentShapes) { + + let shapes = Number.isInteger(labelId) ? this._currentShapes.filter((el) => el.model.label === labelId) : this._currentShapes; + for (let shape of shapes) { if (shape.model.removed) continue; value = value && shape.model.lock; if (!value) break; @@ -635,7 +637,7 @@ class ShapeCollectionModel extends Listener { value: !value, }); - for (let shape of this._currentShapes) { + for (let shape of shapes) { if (shape.model.removed) continue; if (shape.model.lock === value) { shape.model.switchLock(); @@ -671,12 +673,13 @@ class ShapeCollectionModel extends Listener { } } - switchAllHide() { + switchObjectsHide(labelId) { this.resetActive(); let hiddenShape = true; let hiddenText = true; - for (let shape of this._shapes) { + let shapes = Number.isInteger(labelId) ? this._shapes.filter((el) => el.label === labelId) : this._shapes; + for (let shape of shapes) { if (shape.removed) continue; hiddenShape = hiddenShape && shape.hiddenShape; @@ -687,7 +690,7 @@ class ShapeCollectionModel extends Listener { if (!hiddenShape) { // any shape visible - for (let shape of this._shapes) { + for (let shape of shapes) { if (shape.removed) continue; hiddenText = hiddenText && shape.hiddenText; @@ -698,7 +701,7 @@ class ShapeCollectionModel extends Listener { if (!hiddenText) { // any shape text visible - for (let shape of this._shapes) { + for (let shape of shapes) { if (shape.removed) continue; while (shape.hiddenShape || !shape.hiddenText) { shape.switchHide(); @@ -707,7 +710,7 @@ class ShapeCollectionModel extends Listener { } else { // all shape text invisible - for (let shape of this._shapes) { + for (let shape of shapes) { if (shape.removed) continue; while (!shape.hiddenShape) { shape.switchHide(); @@ -717,7 +720,7 @@ class ShapeCollectionModel extends Listener { } else { // all shapes invisible - for (let shape of this._shapes) { + for (let shape of shapes) { if (shape.removed) continue; while (shape.hiddenShape || shape.hiddenText) { shape.switchHide(); @@ -726,6 +729,8 @@ class ShapeCollectionModel extends Listener { } } + + removePointFromActiveShape(idx) { if (this._activeShape && !this._activeShape.lock) { this._activeShape.removePoint(idx); @@ -838,15 +843,11 @@ class ShapeCollectionController { }.bind(this)); let switchHideHandler = Logger.shortkeyLogDecorator(function() { - if (!window.cvat.mode || window.cvat.mode === 'aam') { - this._model.switchActiveHide(); - } + this.switchActiveHide(); }.bind(this)); let switchAllHideHandler = Logger.shortkeyLogDecorator(function() { - if (!window.cvat.mode || window.cvat.mode === 'aam') { - this._model.switchAllHide(); - } + this.switchAllHide(); }.bind(this)); let removeActiveHandler = Logger.shortkeyLogDecorator(function(e) { @@ -933,7 +934,13 @@ class ShapeCollectionController { switchAllLock() { if (!window.cvat.mode || window.cvat.mode === 'aam') { - this._model.switchAllLock(); + this._model.switchObjectsLock(); + } + } + + switchLabelLock(labelId) { + if (!window.cvat.mode || window.cvat.mode === 'aam') { + this._model.switchObjectsLock(labelId); } } @@ -943,6 +950,24 @@ class ShapeCollectionController { } } + switchAllHide() { + if (!window.cvat.mode || window.cvat.mode === 'aam') { + this._model.switchObjectsHide(); + } + } + + switchLabelHide(lableId) { + if (!window.cvat.mode || window.cvat.mode === 'aam') { + this._model.switchObjectsHide(lableId); + } + } + + switchActiveHide() { + if (!window.cvat.mode || window.cvat.mode === 'aam') { + this._model.switchActiveHide(); + } + } + switchActiveColor() { let colorByInstanceInput = $('#colorByInstanceRadio'); let colorByGroupInput = $('#colorByGroupRadio'); @@ -1220,18 +1245,88 @@ class ShapeCollectionView { let labels = window.cvat.labelsInfo.labels(); for (let labelId in labels) { - let div = $('
').addClass('labelContentElement h2 regular hidden').css({ - 'background-color': collectionController.colorsByGroup(+window.cvat.labelsInfo.labelColorIdx(+labelId)), - }).attr({ - 'label_id': labelId, - }).on('mouseover mouseup', () => { - div.addClass('highlightedUI'); - collectionModel.selectAllWithLabel(+labelId); - }).on('mouseout mousedown', () => { - div.removeClass('highlightedUI'); - collectionModel.deselectAll(); - }).append( $(``) ); - div.appendTo(this._labelsContent); + let lockButton = $(``) + .addClass('graphicButton lockButton') + .attr('title', 'Switch lock for all object with same label') + .on('click', () => { + this._controller.switchLabelLock(+labelId); + }); + + lockButton[0].updateState = function(button, labelId) { + let models = this._currentModels.filter((el) => el.label === labelId); + let locked = true; + for (let model of models) { + locked = locked && model.lock; + if (!locked) { + break; + } + } + + if (!locked) { + button.removeClass('locked'); + } + else { + button.addClass('locked'); + } + }.bind(this, lockButton, +labelId); + + let hiddenButton = $(``) + .addClass('graphicButton hiddenButton') + .attr('title', 'Switch hide for all object with same label') + .on('click', () => { + this._controller.switchLabelHide(+labelId); + }); + + hiddenButton[0].updateState = function(button, labelId) { + let models = this._currentModels.filter((el) => el.label === labelId); + let hiddenShape = true; + let hiddenText = true; + for (let model of models) { + hiddenShape = hiddenShape && model.hiddenShape; + hiddenText = hiddenText && model.hiddenText; + if (!hiddenShape && !hiddenText) { + break; + } + } + + if (hiddenShape) { + button.removeClass('hiddenText'); + button.addClass('hiddenShape'); + } + else if (hiddenText) { + button.addClass('hiddenText'); + button.removeClass('hiddenShape'); + } + else { + button.removeClass('hiddenText hiddenShape'); + } + }.bind(this, hiddenButton, +labelId); + + let buttonBlock = $('
') + .append(lockButton).append(hiddenButton) + .addClass('buttonBlockOfLabelUI'); + + let title = $(``); + + let mainDiv = $('
').addClass('labelContentElement h2 regular hidden') + .css({ + 'background-color': collectionController.colorsByGroup(+window.cvat.labelsInfo.labelColorIdx(+labelId)), + }).attr({ + 'label_id': labelId, + }).on('mouseover mouseup', () => { + mainDiv.addClass('highlightedUI'); + collectionModel.selectAllWithLabel(+labelId); + }).on('mouseout mousedown', () => { + mainDiv.removeClass('highlightedUI'); + collectionModel.deselectAll(); + }).append(title).append(buttonBlock); + + mainDiv[0].updateState = function() { + lockButton[0].updateState(); + hiddenButton[0].updateState(); + } + + this._labelsContent.append(mainDiv); } let sidePanelObjectsButton = $('#sidePanelObjectsButton'); @@ -1258,6 +1353,13 @@ class ShapeCollectionView { for (let label of labels) { this._labelsContent.find(`.labelContentElement[label_id="${label}"]`).removeClass('hidden'); } + this._updateLabelUIsState(); + } + + _updateLabelUIsState() { + for (let labelUI of this._labelsContent.find('.labelContentElement:not(.hidden)')) { + labelUI.updateState(); + } } onCollectionUpdate(collection) { @@ -1390,6 +1492,12 @@ class ShapeCollectionView { this._updateLabelUIs(); break; } + case 'lock': + this._updateLabelUIsState(); + break; + case 'hidden': + this._updateLabelUIsState(); + break; } } diff --git a/cvat/apps/engine/static/engine/js/shapes.js b/cvat/apps/engine/static/engine/js/shapes.js index baa8f8a1ef7b..b878736abb17 100644 --- a/cvat/apps/engine/static/engine/js/shapes.js +++ b/cvat/apps/engine/static/engine/js/shapes.js @@ -2473,6 +2473,7 @@ class ShapeView extends Listener { this._setupLockedUI(locked); this._updateButtonsBlock(interpolation.position); + this.notify('lock'); break; } case 'occluded': @@ -2482,6 +2483,7 @@ class ShapeView extends Listener { case 'hidden': setupHidden.call(this, hiddenShape, hiddenText, activeAAM, model.active, interpolation); this._updateButtonsBlock(interpolation.position); + this.notify('hidden'); break; case 'remove': if (model.removed) { diff --git a/cvat/apps/engine/static/engine/stylesheet.css b/cvat/apps/engine/static/engine/stylesheet.css index 4e53b72d2db4..6b89d57b5bae 100644 --- a/cvat/apps/engine/static/engine/stylesheet.css +++ b/cvat/apps/engine/static/engine/stylesheet.css @@ -340,6 +340,13 @@ padding: 0; } +.buttonBlockOfLabelUI { + margin: 5px; + padding: 5px; + border: 1px solid black; + border-radius: 2px; +} + /* Each of the items in the list */ .custom-menu li { padding: 8px 12px;