Skip to content

Commit

Permalink
[vscode] fix #5853: support when closure for views
Browse files Browse the repository at this point in the history
- a view visibility should be auto toggled based on `when` view closure
- if a user explicitly hides a view, then auto toggling should be disabled
- and it should be enabled again when a user explicitly unhides the view

Signed-off-by: Anton Kosyakov <anton.kosyakov@typefox.io>
  • Loading branch information
akosyakov committed Aug 7, 2019
1 parent f48398e commit 15b94f3
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 16 deletions.
12 changes: 12 additions & 0 deletions packages/core/src/browser/context-key-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
********************************************************************************/

import { injectable } from 'inversify';
import { Emitter } from '../common/event';

export interface ContextKey<T> {
set(value: T | undefined): void;
Expand All @@ -30,8 +31,19 @@ export namespace ContextKey {
});
}

export interface ContextKeyChangeEvent {
affects(keys: Set<string>): boolean;
}

@injectable()
export class ContextKeyService {

protected readonly onDidChangeEmitter = new Emitter<ContextKeyChangeEvent>();
readonly onDidChange = this.onDidChangeEmitter.event;
protected fireDidChange(event: ContextKeyChangeEvent): void {
this.onDidChangeEmitter.fire(event);
}

createKey<T>(key: string, defaultValue: T | undefined): ContextKey<T> {
return ContextKey.None;
}
Expand Down
22 changes: 11 additions & 11 deletions packages/core/src/browser/view-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
execute: () => {
const toHide = find(this.containerLayout.iter(), part => part.id === toRegister.id);
if (toHide) {
this.toggleVisibility(toHide);
toHide.setHidden(!toHide.isHidden);
}
},
isToggled: () => {
Expand Down Expand Up @@ -469,15 +469,6 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
return `${this.id}:toggle-visibility`;
}

protected toggleVisibility(part: ViewContainerPart): void {
if (part.canHide) {
part.setHidden(!part.isHidden);
if (!part.isHidden) {
part.collapsed = false;
}
}
}

protected moveBefore(toMovedId: string, moveBeforeThisId: string): void {
const parts = this.getParts();
const toMoveIndex = parts.findIndex(part => part.id === toMovedId);
Expand Down Expand Up @@ -519,7 +510,6 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
return undefined;
}
part.setHidden(false);
part.collapsed = false;
return part;
}

Expand Down Expand Up @@ -697,6 +687,16 @@ export class ViewContainerPart extends BaseWidget {
this.collapsedEmitter.fire(collapsed);
}

setHidden(hidden: boolean): void {
if (!this.canHide) {
return;
}
super.setHidden(hidden);
if (!this.isHidden) {
this.collapsed = false;
}
}

get canHide(): boolean {
return this.options.canHide === undefined || this.options.canHide;
}
Expand Down
11 changes: 10 additions & 1 deletion packages/monaco/src/browser/monaco-context-key-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { injectable, inject } from 'inversify';
import { injectable, inject, postConstruct } from 'inversify';
import { ContextKeyService, ContextKey } from '@theia/core/lib/browser/context-key-service';

@injectable()
Expand All @@ -23,6 +23,15 @@ export class MonacoContextKeyService extends ContextKeyService {
@inject(monaco.contextKeyService.ContextKeyService)
protected readonly contextKeyService: monaco.contextKeyService.ContextKeyService;

@postConstruct()
protected init(): void {
this.contextKeyService.onDidChangeContext(e =>
this.fireDidChange({
affects: keys => e.affectsSome(keys)
})
);
}

createKey<T>(key: string, defaultValue: T | undefined): ContextKey<T> {
return this.contextKeyService.createKey(key, defaultValue);
}
Expand Down
5 changes: 5 additions & 0 deletions packages/monaco/src/typings/monaco/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1102,12 +1102,17 @@ declare module monaco.contextKeyService {

export interface IContext { }

export interface IContextKeyChangeEvent {
affectsSome(keys: Set<string>): boolean;
}

export class ContextKeyService implements IContextKeyService {
constructor(configurationService: monaco.services.IConfigurationService);
createScoped(target?: HTMLElement): IContextKeyService;
getContext(target?: HTMLElement): IContext;
createKey<T>(key: string, defaultValue: T | undefined): IContextKey<T>;
contextMatchesRules(rules: monaco.contextkey.ContextKeyExpr | undefined): boolean;
onDidChangeContext: monaco.IEvent<IContextKeyChangeEvent>;
}

}
Expand Down
2 changes: 2 additions & 0 deletions packages/plugin-ext/src/common/plugin-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export interface PluginPackageViewContainer {
export interface PluginPackageView {
id: string;
name: string;
when?: string;
}

export interface PluginPackageCommand {
Expand Down Expand Up @@ -497,6 +498,7 @@ export interface ViewContainer {
export interface View {
id: string;
name: string;
when?: string;
}

export interface PluginCommand {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@ export class TheiaPluginScanner implements PluginScanner {
private readView(rawView: PluginPackageView): View {
const result: View = {
id: rawView.id,
name: rawView.name
name: rawView.name,
when: rawView.when
};

return result;
Expand Down
68 changes: 65 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 @@ -33,6 +33,7 @@ import { CommandRegistry } from '@theia/core/lib/common/command';
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';

export const PLUGIN_VIEW_FACTORY_ID = 'plugin-view';
export const PLUGIN_VIEW_CONTAINER_FACTORY_ID = 'plugin-view-container';
Expand Down Expand Up @@ -70,6 +71,9 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
@inject(QuickViewService)
protected readonly quickView: QuickViewService;

@inject(ContextKeyService)
protected readonly contextKeyService: ContextKeyService;

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

Expand Down Expand Up @@ -105,6 +109,48 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
iconClass: 'theia-plugin-test-tab-icon',
closeable: true
});
this.contextKeyService.onDidChange(e => {
for (const [, view] of this.views.values()) {
if (view.when === undefined) {
continue;
}
if (e.affects(new Set([view.when]))) {
this.updateViewVisibility(view.id);
}
}
});
}

protected async updateViewVisibility(viewId: string): Promise<void> {
const viewInfo = this.views.get(viewId);
if (!viewInfo) {
return;
}
const [viewContainerId] = viewInfo;
const viewContainer = await this.getPluginViewContainer(viewContainerId);
if (!viewContainer) {
return;
}
const widget = await this.getView(viewId);
if (!widget) {
return;
}
const part = viewContainer.getPartFor(widget);
if (!part) {
return;
}
widget.updateViewVisibility(() =>
part.setHidden(!this.isViewVisible(viewId))
);
}

protected isViewVisible(viewId: string): boolean {
const viewInfo = this.views.get(viewId);
if (!viewInfo) {
return false;
}
const [, view] = viewInfo;
return view.when === undefined || this.contextKeyService.match(view.when);
}

registerViewContainer(location: string, viewContainer: ViewContainer): void {
Expand Down Expand Up @@ -255,11 +301,15 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
const widget = await this.widgetManager.getOrCreateWidget<PluginViewWidget>(PLUGIN_VIEW_FACTORY_ID, identifier);
if (containerWidget.getTrackableWidgets().indexOf(widget) === -1) {
containerWidget.addWidget(widget, {
initiallyCollapsed: !!containerWidget.getParts().length
initiallyCollapsed: !!containerWidget.getParts().length,
initiallyHidden: !this.isViewVisible(viewId)
});
}
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);

const tryFireOnDidExpandView = () => {
if (!part.collapsed && part.isVisible) {
toFire.dispose();
Expand All @@ -275,9 +325,21 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
}
}

protected getPluginViewContainer(viewContainerId: string): Promise<ViewContainerWidget | undefined> {
protected async getPluginViewContainer(viewContainerId: string): Promise<ViewContainerWidget | undefined> {
if (viewContainerId === 'explorer') {
return this.widgetManager.getWidget<ViewContainerWidget>(EXPLORER_VIEW_CONTAINER_ID);
}
if (viewContainerId === 'scm') {
return this.widgetManager.getWidget<ViewContainerWidget>(SCM_VIEW_CONTAINER_ID);
}
if (viewContainerId === 'debug') {
const debug = await this.widgetManager.getWidget(DebugWidget.ID);
if (debug instanceof DebugWidget) {
return debug['sessionWidget']['viewContainer'];
}
}
const identifier = this.toViewContainerIdentifier(viewContainerId);
return this.widgetManager.getWidget(PLUGIN_VIEW_CONTAINER_FACTORY_ID, identifier);
return this.widgetManager.getWidget<ViewContainerWidget>(PLUGIN_VIEW_CONTAINER_FACTORY_ID, identifier);
}

async initWidgets(): Promise<void> {
Expand Down
18 changes: 18 additions & 0 deletions packages/plugin-ext/src/main/browser/view/plugin-view-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,24 @@ export class PluginViewWidget extends Panel implements StatefulWidget {
}
}

protected _suppressUpdateViewVisibility = false;
set suppressUpdateViewVisibility(suppressUpdateViewVisibility: boolean) {
this._suppressUpdateViewVisibility = !this.updatingViewVisibility && suppressUpdateViewVisibility;
}

protected updatingViewVisibility = false;
updateViewVisibility(cb: () => void): void {
if (this._suppressUpdateViewVisibility) {
return;
}
try {
this.updatingViewVisibility = true;
cb();
} finally {
this.updatingViewVisibility = false;
}
}

}
export namespace PluginViewWidget {
export interface State {
Expand Down

0 comments on commit 15b94f3

Please sign in to comment.