From d8aac915e830bd0f0be62519ea883864141d77e1 Mon Sep 17 00:00:00 2001 From: Anton Kosyakov Date: Wed, 31 Jul 2019 14:18:59 +0000 Subject: [PATCH] fix #1707: check widget activation only for last widget, after 2s Signed-off-by: Anton Kosyakov --- .../src/browser/shell/application-shell.ts | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/packages/core/src/browser/shell/application-shell.ts b/packages/core/src/browser/shell/application-shell.ts index 4240982216b15..66c3b901d6386 100644 --- a/packages/core/src/browser/shell/application-shell.ts +++ b/packages/core/src/browser/shell/application-shell.ts @@ -23,7 +23,7 @@ import { } from '@phosphor/widgets'; import { Message } from '@phosphor/messaging'; import { IDragEvent } from '@phosphor/dragdrop'; -import { RecursivePartial, MaybePromise, Event as CommonEvent } from '../../common'; +import { RecursivePartial, MaybePromise, Event as CommonEvent, DisposableCollection, Disposable } from '../../common'; import { Saveable } from '../saveable'; import { StatusBarImpl, StatusBarEntry, StatusBarAlignment } from '../status-bar/status-bar'; import { TheiaDockPanel, BOTTOM_AREA_ID, MAIN_AREA_ID } from './theia-dock-panel'; @@ -865,7 +865,11 @@ export class ApplicationShell extends Widget { * Track the given widget so it is considered in the `current` and `active` state of the shell. */ protected async track(widget: Widget): Promise { + if (this.tracker.widgets.indexOf(widget) !== -1) { + return; + } this.tracker.add(widget); + this.checkActivation(widget); Saveable.apply(widget); if (ApplicationShell.TrackableWidgetProvider.is(widget)) { for (const toTrack of await widget.getTrackableWidgets()) { @@ -915,7 +919,7 @@ export class ApplicationShell extends Widget { current = child; } } - return current && this.checkActivation(current); + return current; } /** @@ -951,14 +955,37 @@ export class ApplicationShell extends Widget { * `activeWidget` property. */ private checkActivation(widget: Widget): Widget { - window.requestAnimationFrame(() => { - if (this.activeWidget !== widget) { - console.warn('Widget was activated, but did not accept focus: ' + widget.id); - } - }); + const onActivateRequest = widget['onActivateRequest'].bind(widget); + widget['onActivateRequest'] = (msg: Message) => { + onActivateRequest(msg); + this.assertActivated(widget); + }; return widget; } + private readonly activationTimeout = 2000; + private readonly toDisposeOnActivationCheck = new DisposableCollection(); + private assertActivated(widget: Widget): void { + this.toDisposeOnActivationCheck.dispose(); + let start = 0; + const step: FrameRequestCallback = timestamp => { + if (document.activeElement && widget.node.contains(document.activeElement)) { + return; + } + if (!start) { + start = timestamp; + } + const delta = timestamp - start; + if (delta < this.activationTimeout) { + request = window.requestAnimationFrame(step); + } else { + console.warn(`Widget was activated, but did not accept focus after ${this.activationTimeout}ms: ${widget.id}`); + } + }; + let request = window.requestAnimationFrame(step); + this.toDisposeOnActivationCheck.push(Disposable.create(() => window.cancelAnimationFrame(request))); + } + /** * Reveal a widget in the application shell. This makes the widget visible, * but does not activate it.