Skip to content

Commit

Permalink
[vscode] fix eclipse-theia#4104: support active/focused view or panel…
Browse files Browse the repository at this point in the history
  • Loading branch information
akosyakov committed Aug 30, 2019
1 parent 3acccea commit b0d8292
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 23 deletions.
37 changes: 37 additions & 0 deletions packages/core/src/browser/shell/application-shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { SplitPositionHandler, SplitPositionOptions } from './split-panels';
import { FrontendApplicationStateService } from '../frontend-application-state';
import { TabBarToolbarRegistry, TabBarToolbarFactory, TabBarToolbar } from './tab-bar-toolbar';
import { ContextKeyService } from '../context-key-service';
import { Emitter } from '../../common/event';

/** The class name added to ApplicationShell instances. */
const APPLICATION_SHELL_CLASS = 'theia-ApplicationShell';
Expand Down Expand Up @@ -171,6 +172,24 @@ export class ApplicationShell extends Widget {
@inject(ContextKeyService)
protected readonly contextKeyService: ContextKeyService;

protected readonly onDidAddWidgetEmitter = new Emitter<Widget>();
readonly onDidAddWidget = this.onDidAddWidgetEmitter.event;
protected fireDidAddWidget(widget: Widget): void {
this.onDidAddWidgetEmitter.fire(widget);
}

protected readonly onDidRemoveWidgetEmitter = new Emitter<Widget>();
readonly onDidRemoveWidget = this.onDidRemoveWidgetEmitter.event;
protected fireDidRemoveWidget(widget: Widget): void {
this.onDidRemoveWidgetEmitter.fire(widget);
}

protected readonly onDidChangeActiveWidgetEmitter = new Emitter<FocusTracker.IChangedArgs<Widget>>();
readonly onDidChangeActiveWidget = this.onDidChangeActiveWidgetEmitter.event;

protected readonly onDidChangeCurrentWidgetEmitter = new Emitter<FocusTracker.IChangedArgs<Widget>>();
readonly onDidChangeCurrentWidget = this.onDidChangeCurrentWidgetEmitter.event;

/**
* Construct a new application shell.
*/
Expand Down Expand Up @@ -205,10 +224,17 @@ export class ApplicationShell extends Widget {
this.mainPanel = this.createMainPanel();
this.topPanel = this.createTopPanel();
this.bottomPanel = this.createBottomPanel();

this.leftPanelHandler = sidePanelHandlerFactory();
this.leftPanelHandler.create('left', this.options.leftPanel);
this.leftPanelHandler.dockPanel.widgetAdded.connect((_, widget) => this.fireDidAddWidget(widget));
this.leftPanelHandler.dockPanel.widgetRemoved.connect((_, widget) => this.fireDidRemoveWidget(widget));

this.rightPanelHandler = sidePanelHandlerFactory();
this.rightPanelHandler.create('right', this.options.rightPanel);
this.rightPanelHandler.dockPanel.widgetAdded.connect((_, widget) => this.fireDidAddWidget(widget));
this.rightPanelHandler.dockPanel.widgetRemoved.connect((_, widget) => this.fireDidRemoveWidget(widget));

this.layout = this.createLayout();

this.tracker.currentChanged.connect(this.onCurrentChanged, this);
Expand Down Expand Up @@ -417,6 +443,8 @@ export class ApplicationShell extends Widget {
spacing: 0
});
dockPanel.id = MAIN_AREA_ID;
dockPanel.widgetAdded.connect((_, widget) => this.fireDidAddWidget(widget));
dockPanel.widgetRemoved.connect((_, widget) => this.fireDidRemoveWidget(widget));
return dockPanel;
}

Expand Down Expand Up @@ -447,6 +475,8 @@ export class ApplicationShell extends Widget {
this.mainPanel.overlay.hide(0);
});
dockPanel.hide();
dockPanel.widgetAdded.connect((_, widget) => this.fireDidAddWidget(widget));
dockPanel.widgetRemoved.connect((_, widget) => this.fireDidRemoveWidget(widget));
return dockPanel;
}

Expand Down Expand Up @@ -796,6 +826,8 @@ export class ApplicationShell extends Widget {

/**
* A signal emitted whenever the `currentWidget` property is changed.
*
* @deprecated since 0.11.0, use `onDidChangeActiveWidget` instead
*/
readonly currentChanged = new Signal<this, FocusTracker.IChangedArgs<Widget>>(this);

Expand All @@ -804,10 +836,13 @@ export class ApplicationShell extends Widget {
*/
private onCurrentChanged(sender: FocusTracker<Widget>, args: FocusTracker.IChangedArgs<Widget>): void {
this.currentChanged.emit(args);
this.onDidChangeCurrentWidgetEmitter.fire(args);
}

/**
* A signal emitted whenever the `activeWidget` property is changed.
*
* @deprecated since 0.11.0, use `onDidChangeActiveWidget` instead
*/
readonly activeChanged = new Signal<this, FocusTracker.IChangedArgs<Widget>>(this);

Expand Down Expand Up @@ -850,6 +885,7 @@ export class ApplicationShell extends Widget {
this.setZIndex(newValue.node, '1');
}
this.activeChanged.emit(args);
this.onDidChangeActiveWidgetEmitter.fire(args);
}

/**
Expand Down Expand Up @@ -1627,4 +1663,5 @@ export namespace ApplicationShell {
return !!widget && 'getTrackableWidgets' in widget;
}
}

}
2 changes: 1 addition & 1 deletion packages/core/src/browser/shell/side-panel-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class SidePanelHandler {
* The widget container is a dock panel in `single-document` mode, which means that the panel
* cannot be split.
*/
dockPanel: DockPanel;
dockPanel: TheiaDockPanel;
/**
* The panel that contains the tab bar and the dock panel. This one is hidden whenever the dock
* panel is empty.
Expand Down
23 changes: 5 additions & 18 deletions packages/core/src/browser/view-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
]);
if (this.options.progressLocationId) {
const onProgress = this.progressLocationService.onProgress(this.options.progressLocationId);
this.toDispose.push(new ProgressBar({ container: this.node, insertMode: 'prepend'}, onProgress));
this.toDispose.push(new ProgressBar({ container: this.node, insertMode: 'prepend' }, onProgress));
}
}

Expand Down Expand Up @@ -658,8 +658,10 @@ export class ViewContainerPart extends BaseWidget {
protected readonly body: HTMLElement;
protected readonly collapsedEmitter = new Emitter<boolean>();
protected readonly contextMenuEmitter = new Emitter<MouseEvent>();
protected readonly onVisibilityChangedEmitter = new Emitter<boolean>();
readonly onVisibilityChanged = this.onVisibilityChangedEmitter.event;
/**
* @deprecated since 0.11.0, use `onDidChangeVisibility` instead
*/
readonly onVisibilityChanged = this.onDidChangeVisibility;
protected readonly onTitleChangedEmitter = new Emitter<void>();
readonly onTitleChanged = this.onTitleChangedEmitter.event;
protected readonly onDidFocusEmitter = new Emitter<this>();
Expand Down Expand Up @@ -699,7 +701,6 @@ export class ViewContainerPart extends BaseWidget {
disposable,
this.collapsedEmitter,
this.contextMenuEmitter,
this.onVisibilityChangedEmitter,
this.onTitleChangedEmitter,
this.registerContextMenu(),
this.onDidFocusEmitter,
Expand Down Expand Up @@ -982,20 +983,6 @@ export class ViewContainerPart extends BaseWidget {
}
}

setFlag(flag: Widget.Flag): void {
super.setFlag(flag);
if (flag === Widget.Flag.IsVisible) {
this.onVisibilityChangedEmitter.fire(this.isVisible);
}
}

clearFlag(flag: Widget.Flag): void {
super.clearFlag(flag);
if (flag === Widget.Flag.IsVisible) {
this.onVisibilityChangedEmitter.fire(this.isVisible);
}
}

}

export namespace ViewContainerPart {
Expand Down
19 changes: 18 additions & 1 deletion packages/core/src/browser/widgets/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,13 @@ export class BaseWidget extends Widget {
readonly onScrollYReachEnd: Event<void> = this.onScrollYReachEndEmitter.event;
protected readonly onScrollUpEmitter = new Emitter<void>();
readonly onScrollUp: Event<void> = this.onScrollUpEmitter.event;
protected readonly onDidChangeVisibilityEmitter = new Emitter<boolean>();
readonly onDidChangeVisibility = this.onDidChangeVisibilityEmitter.event;

protected readonly toDispose = new DisposableCollection(
this.onScrollYReachEndEmitter,
this.onScrollUpEmitter
this.onScrollUpEmitter,
this.onDidChangeVisibilityEmitter
);
protected readonly toDisposeOnDetach = new DisposableCollection();
protected scrollBar?: PerfectScrollbar;
Expand Down Expand Up @@ -149,6 +152,20 @@ export class BaseWidget extends Widget {
protected addClipboardListener<K extends 'cut' | 'copy' | 'paste'>(element: HTMLElement, type: K, listener: EventListenerOrEventListenerObject<K>): void {
this.toDisposeOnDetach.push(addClipboardListener(element, type, listener));
}

setFlag(flag: Widget.Flag): void {
super.setFlag(flag);
if (flag === Widget.Flag.IsVisible) {
this.onDidChangeVisibilityEmitter.fire(this.isVisible);
}
}

clearFlag(flag: Widget.Flag): void {
super.clearFlag(flag);
if (flag === Widget.Flag.IsVisible) {
this.onDidChangeVisibilityEmitter.fire(this.isVisible);
}
}
}

export function setEnabled(element: HTMLElement, enabled: boolean): void {
Expand Down
1 change: 1 addition & 0 deletions packages/output/src/browser/output-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class OutputWidget extends ReactWidget {
this.title.iconClass = 'fa output-tab-icon';
this.title.closable = true;
this.addClass('theia-output');
this.node.tabIndex = 0;
}

@postConstruct()
Expand Down
110 changes: 107 additions & 3 deletions packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { injectable, inject, postConstruct } from 'inversify';
import {
ApplicationShell, ViewContainer as ViewContainerWidget, WidgetManager,
ViewContainerIdentifier, ViewContainerTitleOptions, Widget, FrontendApplicationContribution,
StatefulWidget, CommonMenus
StatefulWidget, CommonMenus, BaseWidget
} from '@theia/core/lib/browser';
import { ViewContainer, View } from '../../../common';
import { PluginSharedStyle } from '../plugin-shared-style';
Expand All @@ -34,6 +34,12 @@ import { MenuModelRegistry } from '@theia/core/lib/common/menu';
import { QuickViewService } from '@theia/core/lib/browser/quick-view-service';
import { Emitter } from '@theia/core/lib/common/event';
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
import { SearchInWorkspaceWidget } from '@theia/search-in-workspace/lib/browser/search-in-workspace-widget';
import { ViewContextKeyService } from './view-context-key-service';
import { PROBLEMS_WIDGET_ID } from '@theia/markers/lib/browser/problem/problem-widget';
import { OUTPUT_WIDGET_KIND } from '@theia/output/lib/browser/output-widget';
import { DebugConsoleContribution } from '@theia/debug/lib/browser/console/debug-console-contribution';
import { TERMINAL_WIDGET_FACTORY_ID } from '@theia/terminal/lib/browser/terminal-widget-impl';

export const PLUGIN_VIEW_FACTORY_ID = 'plugin-view';
export const PLUGIN_VIEW_CONTAINER_FACTORY_ID = 'plugin-view-container';
Expand Down Expand Up @@ -74,6 +80,9 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
@inject(ContextKeyService)
protected readonly contextKeyService: ContextKeyService;

@inject(ViewContextKeyService)
protected readonly viewContextKeys: ViewContextKeyService;

protected readonly onDidExpandViewEmitter = new Emitter<string>();
readonly onDidExpandView = this.onDidExpandViewEmitter.event;

Expand All @@ -86,6 +95,24 @@ export class PluginViewRegistry implements FrontendApplicationContribution {

@postConstruct()
protected init(): void {
// VS Code Viewlets
this.trackVisibleWidget(EXPLORER_VIEW_CONTAINER_ID, { viewletId: 'workbench.view.explorer' });
this.trackVisibleWidget(SearchInWorkspaceWidget.ID, { viewletId: 'workbench.view.search', sideArea: true });
this.trackVisibleWidget(SCM_VIEW_CONTAINER_ID, { viewletId: 'workbench.view.scm' });
this.trackVisibleWidget(DebugWidget.ID, { viewletId: 'workbench.view.debug' });
// TODO workbench.view.extensions - Theia does not have a proper extension view yet

// VS Code Panels
this.trackVisibleWidget(PROBLEMS_WIDGET_ID, { panelId: 'workbench.panel.markers' });
this.trackVisibleWidget(OUTPUT_WIDGET_KIND, { panelId: 'workbench.panel.output' });
this.trackVisibleWidget(DebugConsoleContribution.options.id, { panelId: 'workbench.panel.repl' });
this.trackVisibleWidget(TERMINAL_WIDGET_FACTORY_ID, { panelId: 'workbench.panel.terminal' });
// TODO workbench.panel.comments - Theia does not have a proper comments view yet
this.trackVisibleWidget(SearchInWorkspaceWidget.ID, { panelId: 'workbench.view.search', sideArea: false });

this.updateFocusedView();
this.shell.onDidChangeActiveWidget(() => this.updateFocusedView());

this.widgetManager.onDidCreateWidget(({ factoryId, widget }) => {
if (factoryId === EXPLORER_VIEW_CONTAINER_ID && widget instanceof ViewContainerWidget) {
this.prepareViewContainer('explorer', widget);
Expand Down Expand Up @@ -308,7 +335,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
const part = containerWidget.getPartFor(widget);
if (part) {
// if a view is explicilty hidden then suppress updating visibility based on `when` closure
part.onVisibilityChanged(() => widget.suppressUpdateViewVisibility = part.isHidden);
part.onDidChangeVisibility(() => widget.suppressUpdateViewVisibility = part.isHidden);

const tryFireOnDidExpandView = () => {
if (!part.collapsed && part.isVisible) {
Expand All @@ -318,7 +345,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
const toFire = new DisposableCollection(
Disposable.create(() => this.onDidExpandViewEmitter.fire(viewId)),
part.onCollapsed(tryFireOnDidExpandView),
part.onVisibilityChanged(tryFireOnDidExpandView)
part.onDidChangeVisibility(tryFireOnDidExpandView)
);
tryFireOnDidExpandView();
}
Expand Down Expand Up @@ -478,4 +505,81 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
return widget;
}

protected trackVisibleWidget(factoryId: string, view: PluginViewRegistry.VisibleView): void {
this.doTrackVisibleWidget(this.widgetManager.tryGetWidget(factoryId), view);
this.widgetManager.onDidCreateWidget(event => {
if (factoryId === event.factoryId) {
const { widget } = event;
this.doTrackVisibleWidget(widget, view);
}
});
}

protected doTrackVisibleWidget(widget: Widget | undefined, view: PluginViewRegistry.VisibleView): void {
if (widget instanceof BaseWidget) {
widget.onDidChangeVisibility(() => this.updateVisibleWidget(widget, view));
const toDispose = new DisposableCollection(
Disposable.create(() => this.updateVisibleWidget(widget, view)),
this.shell.onDidChangeActiveWidget(() => {
if (this.shell.activeWidget === widget) {
this.updateVisibleWidget(widget, view);
}
})
);
if (view.sideArea !== undefined) {
toDispose.pushAll([
this.shell.onDidAddWidget(w => {
if (w === widget) {
this.updateVisibleWidget(widget, view);
}
})
]);
}
widget.disposed.connect(() => toDispose.dispose());
}
}

protected readonly visiblePanels = new Set<string>();
protected readonly visibleViewlets = new Set<string>();

protected updateVisibleWidget(widget: BaseWidget, view: PluginViewRegistry.VisibleView): void {
const visibleViews = 'viewletId' in view ? this.visibleViewlets : this.visiblePanels;
const viewId = 'viewletId' in view ? view.viewletId : view.panelId;
const visibleView = 'viewletId' in view ? this.viewContextKeys.activeViewlet : this.viewContextKeys.activePanel;
visibleViews.delete(viewId);
if (this.isVisibleWidget(widget, view)) {
visibleView.set(viewId);
visibleViews.add(viewId);
} else {
const lastVisibleView = [...visibleViews.values()][visibleViews.size - 1];
visibleView.set(lastVisibleView);
}
}

protected isVisibleWidget(widget: BaseWidget, view: PluginViewRegistry.VisibleView): boolean {
if (widget.isDisposed || !widget.isVisible) {
return false;
}
if (view.sideArea === undefined) {
return true;
}
const area = this.shell.getAreaFor(widget);
return view.sideArea === (area === 'left' || area === 'right');
}

protected updateFocusedView(): void {
const widget = this.shell.activeWidget;
if (widget instanceof PluginViewWidget) {
this.viewContextKeys.focusedView.set(widget.options.viewId);
} else {
this.viewContextKeys.focusedView.reset();
}
}

}
export namespace PluginViewRegistry {
export type VisibleView = ({ viewletId: string } | { panelId: string }) & {
/** `undefined` means any area */
sideArea?: boolean
};
}
Loading

0 comments on commit b0d8292

Please sign in to comment.