Browse Source

state undo label

David Sehnal 5 years ago
parent
commit
083daa0b76

+ 8 - 8
src/mol-plugin-state/manager/structure/component.ts

@@ -115,7 +115,7 @@ class StructureComponentManager extends PluginComponent<StructureComponentManage
             for (const s of structures) {
                 await this.plugin.builders.structure.representation.applyPreset(s.cell, provider, params);
             }
-        }, { canUndo: true });
+        }, { canUndo: 'Preset' });
     }
 
     clear(structures: ReadonlyArray<StructureRef>) {
@@ -145,7 +145,7 @@ class StructureComponentManager extends PluginComponent<StructureComponentManage
                 if (!selection || selection.elementCount === 0) continue;                
                 this.modifyComponent(b, c, selection, action);
             }
-            await this.dataState.updateTree(b, { canUndo: true }).runInContext(taskCtx);
+            await this.dataState.updateTree(b, { canUndo: 'Modify Selection' }).runInContext(taskCtx);
         }));
     }
 
@@ -196,7 +196,7 @@ class StructureComponentManager extends PluginComponent<StructureComponentManage
             update.to(repr.cell).update(params);
         }
 
-        return this.plugin.runTask(this.dataState.updateTree(update, { canUndo: true }));
+        return this.plugin.runTask(this.dataState.updateTree(update, { canUndo: 'Update Representation' }));
     }
 
     updateRepresentationsTheme<C extends ColorTheme.BuiltIn, S extends SizeTheme.BuiltIn>(components: ReadonlyArray<StructureComponentRef>, params: StructureComponentManager.UpdateThemeParams<C, S>): Promise<any> {
@@ -220,7 +220,7 @@ class StructureComponentManager extends PluginComponent<StructureComponentManage
             }
         }
 
-        return this.plugin.runTask(this.dataState.updateTree(update, { canUndo: true }));
+        return this.plugin.runTask(this.dataState.updateTree(update, { canUndo: 'Update Theme' }));
     }
 
     addRepresentation(components: ReadonlyArray<StructureComponentRef>, type: string) {
@@ -237,7 +237,7 @@ class StructureComponentManager extends PluginComponent<StructureComponentManage
                     typeParams
                 });
             }
-        }, { canUndo: true });
+        }, { canUndo: 'Add Representation' });
     }
 
     async add(params: StructureComponentManager.AddParams, structures?: ReadonlyArray<StructureRef>) {
@@ -258,7 +258,7 @@ class StructureComponentManager extends PluginComponent<StructureComponentManage
                     type: this.plugin.representation.structure.registry.get(params.representation)
                 });
             }
-        }, { canUndo: true });
+        }, { canUndo: 'Add Selection' });
     }
 
     async applyColor(params: StructureComponentManager.ColorParams, structures?: ReadonlyArray<StructureRef>) {
@@ -275,7 +275,7 @@ class StructureComponentManager extends PluginComponent<StructureComponentManage
                     await setStructureOverpaint(this.plugin, s.components, p.color, getLoci, params.representations, p.opacity);
                 }
             }
-        }, { canUndo: true });
+        }, { canUndo: 'Apply Color' });
     }
 
     private modifyComponent(builder: StateBuilder.Root, component: StructureComponentRef, by: Structure, action: StructureComponentManager.ModifyAction) {
@@ -318,7 +318,7 @@ class StructureComponentManager extends PluginComponent<StructureComponentManage
                 if (s.currentFocus.surroundings) deletes.delete(s.currentFocus.surroundings.cell.transform.ref);
             }
         }
-        return this.plugin.runTask(this.dataState.updateTree(deletes, { canUndo: true }));
+        return this.plugin.runTask(this.dataState.updateTree(deletes, { canUndo: 'Clear Selections' }));
     }
 
     constructor(public plugin: PluginContext) {

+ 4 - 1
src/mol-plugin-ui/structure/components.tsx

@@ -109,12 +109,15 @@ class ComponentEditorControls extends PurePluginUIComponent<{}, ComponentEditorC
     }
 
     render() {
+        const undoTitle = this.state.canUndo
+            ? `Undo ${this.plugin.state.dataState.latestUndoLabel}`
+            : 'Some mistakes of the past can be undone.';
         return <>
             <div className='msp-control-row msp-select-row'>
                 <ToggleButton icon='bookmarks' label='Preset' toggle={this.togglePreset} isSelected={this.state.action === 'preset'} disabled={this.isDisabled} />
                 <ToggleButton icon='plus' label='Add' toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.isDisabled} />
                 <ToggleButton icon='cog' label='' title='Options' style={{ flex: '0 0 40px' }} toggle={this.toggleOptions} isSelected={this.state.action === 'options'} disabled={this.isDisabled} />
-                <IconButton customClass='msp-flex-item' style={{ flex: '0 0 40px' }} onClick={this.undo} disabled={!this.state.canUndo || this.isDisabled} icon='back' title='Some mistakes of the past can be undone.' />
+                <IconButton customClass='msp-flex-item' style={{ flex: '0 0 40px' }} onClick={this.undo} disabled={!this.state.canUndo || this.isDisabled} icon='back' title={undoTitle} />
             </div>
             {this.state.action === 'preset' && this.presetControls}
             {this.state.action === 'add' && <div className='msp-control-offset'>

+ 1 - 1
src/mol-plugin/context.ts

@@ -198,7 +198,7 @@ export class PluginContext {
         return this.tasks.run(task);
     }
 
-    dataTransaction(f: () => Promise<void> | void, options?: { canUndo?: boolean }) {
+    dataTransaction(f: () => Promise<void> | void, options?: { canUndo?: string | boolean }) {
         return this.runTask(this.state.dataState.transaction(f, options));
     }
 

+ 14 - 10
src/mol-state/state.ts

@@ -68,12 +68,12 @@ class State {
     private spine = new StateTreeSpine.Impl(this.cells);
 
     private historyCapacity = 5;
-    private history: StateTree[] = [];
+    private history: [StateTree, string][] = [];
 
-    private addHistory(tree: StateTree) {
+    private addHistory(tree: StateTree, label?: string) {
         if (this.historyCapacity === 0) return;
 
-        this.history.unshift(tree);
+        this.history.unshift([tree, label || 'Update']);
         if (this.history.length > this.historyCapacity) this.history.pop();
 
         this.events.historyUpdated.next({ state: this });
@@ -85,6 +85,10 @@ class State {
         this.events.historyUpdated.next({ state: this });
     }
 
+    get latestUndoLabel() {
+        return this.history.length > 0 ? this.history[0][1] : void 0;
+    }
+
     get canUndo() {
         return this.history.length > 0;
     }
@@ -93,12 +97,12 @@ class State {
 
     undo() {
         return Task.create('Undo', async ctx => {
-            const tree = this.history.shift();
-            if (!tree) return;
+            const e = this.history.shift();
+            if (!e) return;
             this.events.historyUpdated.next({ state: this });
             this.undoingHistory = true;
             try {
-                await this.updateTree(tree, { canUndo: false }).runInContext(ctx);
+                await this.updateTree(e[0], { canUndo: false }).runInContext(ctx);
             } finally {
                 this.undoingHistory = false;
             }
@@ -169,7 +173,7 @@ class State {
     private inTransaction = false;
 
     /** Apply series of updates to the state. If any of them fail, revert to the original state. */
-    transaction(edits: () => Promise<void> | void, options?: { canUndo?: boolean }) {
+    transaction(edits: () => Promise<void> | void, options?: { canUndo?: string | boolean }) {
         return Task.create('State Transaction', async ctx => {
             const isNested = this.inTransaction;
 
@@ -202,7 +206,7 @@ class State {
                     this.events.isUpdating.next(false);
 
                     if (!restored) {
-                        if (options?.canUndo) this.addHistory(snapshot);
+                        if (options?.canUndo) this.addHistory(snapshot, typeof options.canUndo === 'string' ? options.canUndo : void 0);
                         else this.clearHistory();
                     }
                 }
@@ -245,7 +249,7 @@ class State {
                 if (!options?.canUndo) {
                     if (!this.undoingHistory) this.clearHistory();
                 } else if (!reverted) {
-                    this.addHistory(snapshot!);
+                    this.addHistory(snapshot!, typeof options.canUndo === 'string' ? options.canUndo : void 0);
                 }
             }
         }, () => {
@@ -368,7 +372,7 @@ namespace State {
         doNotUpdateCurrent: boolean,
         revertIfAborted: boolean,
         revertOnError: boolean,
-        canUndo: boolean
+        canUndo: boolean | string
     }
 
     export function create(rootObject: StateObject, params?: { globalContext?: unknown, rootState?: StateTransform.State }) {