Skip to content
This repository was archived by the owner on Apr 18, 2024. It is now read-only.

Commit 8877bdd

Browse files
nick-skriabinhlomzikniklub
authored
feat: DEV-1414: Add setting to preserve selected tool in image segmentation (#420)
* Add setting to preserve selected tool in image segmentation * Fight race conditions on tools init * Fix default tool behavior * Properly destroy LSF Co-authored-by: hlomzik <[email protected]> Co-authored-by: niklub <[email protected]>
1 parent 95a19a8 commit 8877bdd

File tree

9 files changed

+81
-24
lines changed

9 files changed

+81
-24
lines changed

src/LabelStudio.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { toCamelCase } from "strman";
1010
import { isDefined } from "./utils/utilities";
1111
import { Hotkey } from "./core/Hotkey";
1212
import defaultOptions from './defaultOptions';
13+
import { destroy } from "mobx-state-tree";
1314

1415
configure({
1516
isolateGlobalState: true,
@@ -69,6 +70,7 @@ export class LabelStudio {
6970

7071
const destructor = () => {
7172
unmountComponentAtNode(rootElement);
73+
destroy(this.store);
7274
};
7375

7476
this.destroy = destructor;

src/components/Settings/Settings.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ export default observer(({ store }) => {
125125
<Checkbox checked={store.settings.showLineNumbers} onChange={store.settings.toggleShowLineNumbers}>
126126
Show line numbers for Text
127127
</Checkbox>
128+
<br />
129+
<Checkbox checked={store.settings.preserveSelectedTool} onChange={store.settings.togglepreserveSelectedTool}>
130+
Remember Selected Tool
131+
</Checkbox>
128132

129133
{/* <br /> */}
130134
{/* <Checkbox */}

src/env/development.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ import { TimeSeriesSingle } from "../examples/timeseries_single";
6464
*/
6565
// import { AllTypes } from "../examples/all_types";
6666

67-
const data = VideoRectangles;
67+
const data = ImageTools;
6868

6969
function getData(task) {
7070
if (task && task.data) {

src/mixins/Tool.js

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getEnv, types } from "mobx-state-tree";
1+
import { getEnv, getRoot, types } from "mobx-state-tree";
22
import { cloneNode, restoreNewsnapshot } from "../core/Helpers";
33
import { AnnotationMixin } from "./AnnotationMixin";
44

@@ -10,7 +10,7 @@ const ToolMixin = types
1010
})
1111
.views(self => ({
1212
get obj() {
13-
return self.manager?.obj;
13+
return self.manager?.obj ?? getEnv(self).object;
1414
},
1515

1616
get manager() {
@@ -25,6 +25,10 @@ const ToolMixin = types
2525
return () => null;
2626
},
2727

28+
get fullName() {
29+
return self.toolName + (self.dynamic ? '-dynamic' : '');
30+
},
31+
2832
get clonedStates() {
2933
const states = [self.control];
3034
const activeStates = states
@@ -55,11 +59,34 @@ const ToolMixin = types
5559
get extraShortcuts() {
5660
return {};
5761
},
62+
63+
get shouldPreserveSelectedState() {
64+
try {
65+
const settings = getRoot(self.obj).settings;
66+
67+
return settings.preserveSelectedTool;
68+
} catch (err) {
69+
console.log('something is missing');
70+
return false;
71+
}
72+
},
73+
74+
get isPreserved() {
75+
return window.localStorage.getItem(`selected-tool:${self.obj?.name}`) === self.fullName;
76+
},
5877
}))
5978
.actions(self => ({
60-
setSelected(val) {
61-
self.selected = val;
79+
setSelected(selected) {
80+
self.selected = selected;
6281
self.afterUpdateSelected();
82+
83+
if (selected && self.obj) {
84+
const storeName = `selected-tool:${self.obj.name}`;
85+
86+
if (self.shouldPreserveSelectedState) {
87+
window.localStorage.setItem(storeName, self.fullName);
88+
}
89+
}
6390
},
6491

6592
afterUpdateSelected() {},

src/stores/AppStore.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,5 +669,8 @@ export default types
669669
addAnnotationToTaskHistory,
670670
nextTask,
671671
prevTask,
672+
beforeDestroy() {
673+
ToolsManager.removeAllTools();
674+
},
672675
};
673676
});

src/stores/SettingsStore.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ const SettingsModel = types
5555

5656
showPredictionsPanel: types.optional(types.boolean, true),
5757
// showScore: types.optional(types.boolean, false),
58+
59+
preserveSelectedTool: types.optional(types.boolean, true),
5860
})
5961
.views(self => ({
6062
get annotation() {
@@ -139,6 +141,10 @@ const SettingsModel = types
139141
self.enableAutoSave = !self.enableAutoSave;
140142
},
141143

144+
togglepreserveSelectedTool() {
145+
self.preserveSelectedTool = !self.preserveSelectedTool;
146+
},
147+
142148
toggleHotkeys() {
143149
self.enableHotkeys = !self.enableHotkeys;
144150
if (self.enableHotkeys) {

src/tags/control/Label.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,9 @@ const Model = types.model({
164164
const selectedTool = manager.findSelectedTool();
165165
const sameType = (tool && selectedTool) ? getType(selectedTool).name === getType(tool).name : false;
166166
const sameLabel = selectedTool ? tool?.control?.name === selectedTool?.control?.name : false;
167+
const isNotSameTool = selectedTool && (!sameType || !sameLabel);
167168

168-
if (tool && (!selectedTool || (selectedTool && (!sameType || !sameLabel)))) {
169+
if (tool && (isNotSameTool || !selectedTool)) {
169170
manager.selectTool(tool, true);
170171
}
171172
}

src/tags/object/Image.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -422,32 +422,30 @@ const Model = types.model({
422422
// actions for the tools
423423
.actions(self => {
424424
const manager = ToolsManager.getInstance({ name: self.name });
425-
const env = { manager, control: self };
425+
const env = { manager, control: self, object: self };
426426

427-
manager.reload({ name: self.name });
428-
429-
function afterCreate() {
427+
function afterAttach() {
430428
if (self.selectioncontrol)
431-
manager.addTool("selection", Tools.Selection.create({}, env));
429+
manager.addTool("MoveTool", Tools.Selection.create({}, env));
432430

433431
if (self.zoomcontrol)
434-
manager.addTool("zoom", Tools.Zoom.create({}, env));
432+
manager.addTool("ZoomPanTool", Tools.Zoom.create({}, env));
435433

436434
if (self.brightnesscontrol)
437-
manager.addTool("brightness", Tools.Brightness.create({}, env));
435+
manager.addTool("BrightnessTool", Tools.Brightness.create({}, env));
438436

439437
if (self.contrastcontrol)
440-
manager.addTool("contrast", Tools.Contrast.create({}, env));
438+
manager.addTool("ContrastTool", Tools.Contrast.create({}, env));
441439

442440
if (self.rotatecontrol)
443-
manager.addTool("rotate", Tools.Rotate.create({}, env));
441+
manager.addTool("RotateTool", Tools.Rotate.create({}, env));
444442
}
445443

446444
function getToolsManager() {
447445
return manager;
448446
}
449447

450-
return { afterCreate, getToolsManager };
448+
return { afterAttach, getToolsManager };
451449
}).extend((self) => {
452450
let skipInteractions = false;
453451

src/tools/Manager.js

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,28 +31,44 @@ class ToolsManager {
3131
INSTANCES.forEach((manager) => manager.removeAllTools());
3232
}
3333

34-
constructor({ name } = {}) {
34+
constructor({
35+
name,
36+
} = {}) {
3537
this.name = name;
3638
this.tools = {};
3739
this._default_tool = null;
3840
}
3941

42+
get preservedTool() {
43+
return window.localStorage.getItem(`selected-tool:${this.name}`);
44+
}
45+
4046
get obj() {
4147
return root.annotationStore.names.get(this.name);
4248
}
4349

44-
addTool(name, tool, prefix = guidGenerator()) {
50+
addTool(toolName, tool, prefix = guidGenerator()) {
4551
if (tool.smart && tool.smartOnly) return;
46-
// todo: It seems that key is using only for storing,
52+
// todo: It seems that key is used only for storing,
4753
// but not for finding tools, so may be there might
4854
// be an array instead of an object
55+
const name = tool.toolName ?? toolName;
4956
const key = `${prefix}#${name}`;
5057

5158
this.tools[key] = tool;
5259

53-
if (tool.default && !this._default_tool && !this.hasSelected) {
54-
this._default_tool = tool;
55-
if (tool.setSelected) tool.setSelected(true);
60+
if (tool.default && !this._default_tool) this._default_tool = tool;
61+
62+
if (this.preservedTool && tool.shouldPreserveSelectedState) {
63+
if (tool.fullName === this.preservedTool && tool.setSelected) {
64+
this.unselectAll();
65+
this.selectTool(tool, true);
66+
}
67+
return;
68+
}
69+
70+
if (this._default_tool && !this.hasSelected) {
71+
this.selectTool(this._default_tool, true);
5672
}
5773
}
5874

@@ -70,8 +86,8 @@ class ToolsManager {
7086
}
7187
}
7288

73-
selectTool(tool, value) {
74-
if (value) {
89+
selectTool(tool, selected) {
90+
if (selected) {
7591
this.unselectAll();
7692
if (tool.setSelected) tool.setSelected(true);
7793
} else {

0 commit comments

Comments
 (0)