Browse Source

basic overlay task support

David Sehnal 4 years ago
parent
commit
3c1a26c4f5

+ 1 - 1
src/extensions/mp4-export/controls.ts

@@ -78,7 +78,7 @@ export class Mp4Controls extends PluginComponent {
             }
         });
 
-        return this.plugin.runTask(task);
+        return this.plugin.runTask(task, { useOverlay: true });
     }
 
     private get manager() {

+ 2 - 1
src/mol-plugin-ui/plugin.tsx

@@ -14,7 +14,7 @@ import { PluginReactContext, PluginUIComponent } from './base';
 import { AnimationViewportControls, DefaultStructureTools, LociLabels, StateSnapshotViewportControls, TrajectoryViewportControls, SelectionViewportControls } from './controls';
 import { LeftPanelControls } from './left-panel';
 import { SequenceView } from './sequence';
-import { BackgroundTaskProgress } from './task';
+import { BackgroundTaskProgress, OverlayTaskProgress } from './task';
 import { Toasts } from './toast';
 import { Viewport, ViewportControls } from './viewport';
 import { PluginCommands } from '../mol-plugin/commands';
@@ -139,6 +139,7 @@ class Layout extends PluginUIComponent {
                     {layout.showControls && controls.right !== 'none' && this.region('right', controls.right || ControlsWrapper)}
                     {layout.showControls && controls.bottom !== 'none' && this.region('bottom', controls.bottom || Log)}
                 </div>
+                {!this.plugin.spec.components?.hideTaskOverlay && <OverlayTaskProgress />}
             </div>
         </div>;
     }

+ 28 - 55
src/mol-plugin-ui/skin/base/components/tasks.scss

@@ -12,72 +12,45 @@
     }
 }
 
-/* overlay */
-
-.msp-overlay {
+.msp-overlay-tasks {
     position: absolute;
+    display: flex;
     top: 0;
     left: 0;
     bottom: 0;
     right: 0;
+    height: 100%;
+    width: 100%;
     z-index: 1000;
+    justify-content: center;
+    align-items: center;
+    background: rgba(0, 0, 0, 0.25);
 
-    .msp-overlay-background {
-        position: absolute;
-        top: 0;
-        left: 0;
-        bottom: 0;
-        right: 0;
-        background: transparent;
-        //background: black;
-        //opacity: 0.5;
-    }
-
-    .msp-overlay-content-wrap {
-        position: absolute;
-        top: 0;
-        left: 0;
-        bottom: 0;
-        right: 0;
-        display: block;
-        width: 100%;
-        height: 100%;
-    }
-
-    .msp-overlay-content {
-        text-align: center;
+    .msp-task-state {
+        $size: $row-height;
 
         > div {
+            height: $size;
+            margin-top: 1px;
+            position: relative;
+            width: 100%;
+            background: $default-background;
 
-            padding-top: 2 * $row-height;
-
-            .msp-task-state {
-                $size: $row-height;
-                text-align: center;
-
-                > div {
-                    height: $size;
-                    margin-top: $control-spacing;
-                    position: relative;
-                    text-align: center;
-                    width: 100%;
-
-                    > div {
-                        height: $size;
-                        line-height: $size;
-                        display: inline-block;
-                        background: $default-background;
-                        padding: 0 ($control-spacing);
-                        font-weight: bold;
-                        @include non-selectable;
-                    }
+            > div {
+                height: $size;
+                line-height: $size;
+                display: inline-block;
+                padding: 0 ($control-spacing);
+                @include non-selectable;
+                white-space: nowrap;
+                background: $default-background;
+                position: absolute;
+            }
 
-                    > button {
-                        display: inline-block;
-                        margin-top: -3px;
-                        font-size: 140%;
-                    }
-                }
+            > button {
+                display: inline-block;
+                margin-top: -3px;
+                // font-size: 140%;
             }
         }
     }

+ 23 - 1
src/mol-plugin-ui/task.tsx

@@ -15,7 +15,8 @@ import { CancelSvg } from './controls/icons';
 
 export class BackgroundTaskProgress extends PluginUIComponent<{ }, { tracked: OrderedMap<number, TaskManager.ProgressEvent> }> {
     componentDidMount() {
-        this.subscribe(this.plugin.events.task.progress.pipe(filter(e => e.level !== 'none')), e => {
+        const hideOverlay = !!this.plugin.spec.components?.hideTaskOverlay;
+        this.subscribe(this.plugin.events.task.progress.pipe(filter(e => e.level === 'background' && (hideOverlay || !e.useOverlay))), e => {
             this.setState({ tracked: this.state.tracked.set(e.id, e) });
         });
         this.subscribe(this.plugin.events.task.finished, ({ id }) => {
@@ -63,4 +64,25 @@ function countSubtasks(progress: Progress.Node) {
     let sum = 0;
     for (const c of progress.children) sum += countSubtasks(c);
     return sum;
+}
+
+export class OverlayTaskProgress extends PluginUIComponent<{ }, { tracked: OrderedMap<number, TaskManager.ProgressEvent> }> {
+    componentDidMount() {
+        this.subscribe(this.plugin.events.task.progress.pipe(filter(e => !!e.useOverlay)), e => {
+            this.setState({ tracked: this.state.tracked.set(e.id, e) });
+        });
+        this.subscribe(this.plugin.events.task.finished, ({ id }) => {
+            this.setState({ tracked: this.state.tracked.delete(id) });
+        });
+    }
+
+    state = { tracked: OrderedMap<number, TaskManager.ProgressEvent>() };
+
+    render() {
+        if (this.state.tracked.size === 0) return null;
+
+        return <div className='msp-overlay-tasks'>
+            {this.state.tracked.valueSeq().map(e => <ProgressEntry key={e!.id} event={e!} />)}
+        </div>;
+    }
 }

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

@@ -63,7 +63,7 @@ import { Vec2 } from '../mol-math/linear-algebra';
 import { PluginAnimationLoop } from './animation-loop';
 
 export class PluginContext {
-    runTask = <T>(task: Task<T>) => this.managers.task.run(task);
+    runTask = <T>(task: Task<T>, params?: { useOverlay?: boolean }) => this.managers.task.run(task, params);
     resolveTask = <T>(object: Task<T> | T | undefined) => {
         if (!object) return void 0;
         if (Task.is(object)) return this.runTask(object);

+ 2 - 1
src/mol-plugin/spec.ts

@@ -32,7 +32,8 @@ interface PluginSpec {
             view?: React.ComponentClass,
             controls?: React.ComponentClass,
             canvas3d?: PartialCanvas3DProps
-        }
+        },
+        hideTaskOverlay?: boolean
     },
     config?: [PluginConfigItem, unknown][]
 }

+ 9 - 4
src/mol-plugin/util/task-manager.ts

@@ -17,6 +17,7 @@ class TaskManager {
     private id = 0;
     private runningTasks = new Set<number>();
     private abortRequests = new Map<number, string | undefined>();
+    private options = new Map<number, { useOverlay: boolean }>();
     private currentContext: { ctx: RuntimeContext, refCount: number }[] = [];
 
     readonly events = {
@@ -42,18 +43,19 @@ class TaskManager {
             const elapsed = now() - progress.root.progress.startedTime;
             this.events.progress.next({
                 id: internalId,
-                level: elapsed < 250 ? 'none' : elapsed < 1500 ? 'background' : 'overlay',
+                useOverlay: this.options.get(taskId)?.useOverlay,
+                level: elapsed < 250 ? 'none' : 'background',
                 progress
             });
         };
     }
 
-    async run<T>(task: Task<T>, createNewContext = false): Promise<T> {
+    async run<T>(task: Task<T>, params?: { createNewContext?: boolean, useOverlay?: boolean }): Promise<T> {
         const id = this.id++;
 
         let ctx: TaskManager['currentContext'][0];
 
-        if (createNewContext || this.currentContext.length === 0) {
+        if (params?.createNewContext || this.currentContext.length === 0) {
             ctx = { ctx: CreateObservableCtx(task, this.track(id, task.id), 100), refCount: 1 };
         } else {
             ctx = this.currentContext[this.currentContext.length - 1];
@@ -61,10 +63,12 @@ class TaskManager {
         }
 
         try {
+            this.options.set(task.id, { useOverlay: !!params?.useOverlay });
             this.runningTasks.add(task.id);
             const ret = await ExecuteInContext(ctx.ctx, task);
             return ret;
         } finally {
+            this.options.delete(task.id);
             this.runningTasks.delete(task.id);
             this.events.finished.next({ id });
             this.abortRequests.delete(task.id);
@@ -90,10 +94,11 @@ class TaskManager {
 }
 
 namespace TaskManager {
-    export type ReportLevel = 'none' | 'background' | 'overlay'
+    export type ReportLevel = 'none' | 'background'
 
     export interface ProgressEvent {
         id: number,
+        useOverlay?: boolean,
         level: ReportLevel,
         progress: Progress
     }