Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support active/focused view or panel when clause context #6062

Merged
merged 1 commit into from
Aug 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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