Skip to content

Commit

Permalink
CHE-3896 add the needed methods for Webview API
Browse files Browse the repository at this point in the history
Signed-off-by: Oleksii Orel <oorel@redhat.com>
  • Loading branch information
olexii4 committed Apr 18, 2019
1 parent 26b1a35 commit a0c2a07
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 19 deletions.
5 changes: 4 additions & 1 deletion packages/plugin-ext/src/api/plugin-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,10 @@ export interface StatusBarExt {
export enum EditorPosition {
ONE = 0,
TWO = 1,
THREE = 2
THREE = 2,
FOUR = 3,
FIVE = 4,
SIX = 5
}

export interface Position {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import { DebugService } from '@theia/debug/lib/common/debug-service';
import { PluginSharedStyle } from './plugin-shared-style';
import { FSResourceResolver } from './file-system-main';
import { SelectionProviderCommandContribution } from './selection-provider-command';
import { ViewColumnService } from './view-column-service';

export default new ContainerModule((bind, unbind, isBound, rebind) => {
bindHostedPluginPreferences(bind);
Expand Down Expand Up @@ -139,4 +140,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
rebind(DebugService).toService(PluginDebugService);
bind(PluginDebugSessionContributionRegistry).toSelf().inSingletonScope();
rebind(DebugSessionContributionRegistry).toService(PluginDebugSessionContributionRegistry);

bind(ViewColumnService).toSelf().inSingletonScope();
});
96 changes: 96 additions & 0 deletions packages/plugin-ext/src/main/browser/view-column-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/********************************************************************************
* Copyright (C) 2019 Red Hat, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { injectable, inject } from 'inversify';
import { Emitter, Event } from '@theia/core';
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
import { TheiaDockPanel } from '@theia/core/lib/browser/shell/theia-dock-panel';
import { toArray } from '@phosphor/algorithm';
import { Widget } from '@phosphor/widgets';

@injectable()
export class ViewColumnService {
private mainPanel: TheiaDockPanel;
private columnValues = new Map<string, number>();
private viewColumnIds = new Map<number, string[]>();

protected readonly onViewColumnChangedEmitter = new Emitter<{ id: string, viewColumn: number }>();

constructor(
@inject(ApplicationShell) private readonly shell: ApplicationShell,
) {
this.mainPanel = this.shell.mainPanel;

let oldColumnValues = new Map<string, number>();
const update = async () => {
await new Promise((resolve => setTimeout(() => resolve())));
this.updateViewColumns();
this.viewColumnIds.forEach((ids: string[], viewColumn: number) => {
ids.forEach((id: string) => {
if (!oldColumnValues.has(id) || oldColumnValues.get(id) !== viewColumn) {
this.onViewColumnChangedEmitter.fire({ id, viewColumn });
}
});
});
oldColumnValues = new Map(this.columnValues.entries());
};
this.mainPanel.widgetAdded.connect(() => update());
this.mainPanel.widgetRemoved.connect(() => update());
}

get onViewColumnChanged(): Event<{ id: string, viewColumn: number }> {
return this.onViewColumnChangedEmitter.event;
}

updateViewColumns(): void {
const positionIds = new Map<number, string[]>();
toArray(this.mainPanel.widgets()).forEach((widget: Widget) => {
const dockPanel = <{ id?: string, style?: CSSStyleDeclaration }>widget.node;
if (dockPanel.id && dockPanel.style && dockPanel.style.left) {
const position = parseInt(dockPanel.style.left);
if (!isNaN(position)) {
if (!positionIds.has(position)) {
positionIds.set(position, []);
}
positionIds.get(position)!.push(dockPanel.id);
}
}
});
this.columnValues.clear();
this.viewColumnIds.clear();
[...positionIds.keys()].sort((a, b) => a - b).forEach((key: number, viewColumn: number) => {
positionIds.get(key)!.forEach((id: string) => {
this.columnValues.set(id, viewColumn);
if (!this.viewColumnIds.has(viewColumn)) {
this.viewColumnIds.set(viewColumn, []);
}
this.viewColumnIds.get(viewColumn)!.push(id);
});
});
}

getViewColumnIds(viewColumn: number): string[] {
return this.viewColumnIds.get(viewColumn) || [];
}

getViewColumn(id: string): number | undefined {
return this.columnValues.get(id);
}

hasViewColumn(id: string): boolean {
return this.columnValues.has(id);
}
}
3 changes: 1 addition & 2 deletions packages/plugin-ext/src/main/browser/webview/webview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class WebviewWidget extends BaseWidget {
private static readonly ID = new IdGenerator('webview-widget-');
protected readonly toDispose = new DisposableCollection();
private iframe: HTMLIFrameElement;
private state: string | undefined = undefined;
private state: { [key: string]: any } | undefined = undefined;
private loadTimeout: number | undefined;

constructor(title: string, private options: WebviewWidgetOptions, private eventDelegate: WebviewEvents) {
Expand Down Expand Up @@ -73,7 +73,6 @@ export class WebviewWidget extends BaseWidget {
}

setHTML(html: string) {
html = html.replace(/theia-resource:/g, '/webview/');
const newDocument = new DOMParser().parseFromString(html, 'text/html');
if (!newDocument || !newDocument.body) {
return;
Expand Down
125 changes: 111 additions & 14 deletions packages/plugin-ext/src/main/browser/webviews-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,48 @@ import { WebviewWidget } from './webview/webview';
import { ThemeService } from '@theia/core/lib/browser/theming';
import { ThemeRulesService } from './webview/theme-rules-service';
import { DisposableCollection } from '@theia/core';
import { ViewColumnService } from './view-column-service';

export class WebviewsMainImpl implements WebviewsMain {
private readonly revivers = new Set<string>();
private readonly proxy: WebviewsExt;
protected readonly shell: ApplicationShell;
protected readonly viewColumnService: ViewColumnService;
protected readonly keybindingRegistry: KeybindingRegistry;
protected readonly themeService = ThemeService.get();
protected readonly themeRulesService = ThemeRulesService.get();

private readonly views = new Map<string, WebviewWidget>();
private readonly viewsOptions = new Map<string, { panelOptions: WebviewPanelShowOptions; panelId: string; active: boolean; visible: boolean; }>();

private timeoutHandle: NodeJS.Timer | undefined;

constructor(rpc: RPCProtocol, container: interfaces.Container) {
this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.WEBVIEWS_EXT);
this.shell = container.get(ApplicationShell);
this.keybindingRegistry = container.get(KeybindingRegistry);
this.viewColumnService = container.get(ViewColumnService);

const checkViewsOptions = () => {
if (!this.views.size) {
return;
}
if (this.timeoutHandle) {
clearTimeout(this.timeoutHandle);
}
this.timeoutHandle = setTimeout(() => {
for (const key of this.viewsOptions.keys()) {
this.checkViewOptions(key);
}
}, 50);
};
this.shell.activeChanged.connect(() => checkViewsOptions());
this.shell.currentChanged.connect(() => checkViewsOptions());
this.viewColumnService.onViewColumnChanged(() => checkViewsOptions());
}

$createWebviewPanel(
viewId: string,
panelId: string,
viewType: string,
title: string,
showOptions: WebviewPanelShowOptions,
Expand All @@ -54,7 +78,7 @@ export class WebviewsMainImpl implements WebviewsMain {
allowScripts: options ? options.enableScripts : false
}, {
onMessage: m => {
this.proxy.$onMessage(viewId, m);
this.proxy.$onMessage(panelId, m);
},
onKeyboardEvent: e => {
this.keybindingRegistry.run(e);
Expand All @@ -71,7 +95,7 @@ export class WebviewsMainImpl implements WebviewsMain {
const parent = contentDocument.head ? contentDocument.head : contentDocument.body;
styleElement = this.themeRulesService.createStyleSheet(parent);
styleElement.id = styleId;
parent.appendChild((styleElement));
parent.appendChild(styleElement);
}

this.themeRulesService.setRules(styleElement, this.themeRulesService.getCurrentThemeRules());
Expand All @@ -82,22 +106,55 @@ export class WebviewsMainImpl implements WebviewsMain {
});
view.disposed.connect(() => {
toDispose.dispose();
this.onCloseView(viewId);
this.onCloseView(panelId);
});
this.views.set(viewId, view);

this.views.set(panelId, view);
this.viewsOptions.set(view.id, { panelOptions: showOptions, panelId, visible: false, active: false });
this.addOrReattachWidget(panelId, showOptions);
}
private addOrReattachWidget(handler: string, showOptions: WebviewPanelShowOptions) {
const view = this.views.get(handler);
if (!view) {
return;
}
const widgetOptions: ApplicationShell.WidgetOptions = { area: showOptions.area ? showOptions.area : 'main' };
// FIXME translate all view columns properly

let mode = 'open-to-right';
if (showOptions.viewColumn === -2) {
const ref = this.shell.currentWidget;
if (ref && this.shell.getAreaFor(ref) === widgetOptions.area) {
Object.assign(widgetOptions, { ref, mode: 'open-to-right' });
Object.assign(widgetOptions, { ref, mode });
}
} else if (widgetOptions.area === 'main' && showOptions.viewColumn !== undefined) {
this.viewColumnService.updateViewColumns();
let widgetIds = this.viewColumnService.getViewColumnIds(showOptions.viewColumn);
if (widgetIds.length > 0) {
mode = 'tab-after';
} else if (showOptions.viewColumn > 0) {
widgetIds = this.viewColumnService.getViewColumnIds(showOptions.viewColumn - 1);
}
const ref = this.shell.getWidgets(widgetOptions.area).find(widget => widget.isVisible && widgetIds.indexOf(widget.node.id) !== -1);
if (ref) {
Object.assign(widgetOptions, { ref, mode });
}
}

this.shell.addWidget(view, widgetOptions);
const visible = true;
let active: boolean;
if (showOptions.preserveFocus) {
this.shell.revealWidget(view.id);
active = false;
} else {
this.shell.activateWidget(view.id);
active = true;
}
const options = this.viewsOptions.get(view.id);
if (options) {
options.panelOptions = showOptions;
options.visible = visible;
options.active = active;
}
}
$disposeWebview(handle: string): void {
Expand All @@ -107,15 +164,30 @@ export class WebviewsMainImpl implements WebviewsMain {
}
}
$reveal(handle: string, showOptions: WebviewPanelShowOptions): void {
const webview = this.getWebview(handle);
if (webview.isDisposed) {
const view = this.getWebview(handle);
if (view.isDisposed) {
return;
}
// FIXME handle view column here too!
if (showOptions.viewColumn !== undefined || (showOptions.area !== undefined && showOptions.area !== 'main')) {
this.viewColumnService.updateViewColumns();
const options = this.viewsOptions.get(view.id);
if (!options) {
return;
}
const columnIds = showOptions.viewColumn ? this.viewColumnService.getViewColumnIds(showOptions.viewColumn) : [];
if (columnIds.indexOf(view.id) === -1 || options.panelOptions.area !== showOptions.area) {
this.addOrReattachWidget(options.panelId, showOptions);
if (this.timeoutHandle) {
clearTimeout(this.timeoutHandle);
}
this.checkViewOptions(view.id, showOptions.viewColumn);
return;
}
}
if (showOptions.preserveFocus) {
this.shell.revealWidget(webview.id);
this.shell.revealWidget(view.id);
} else {
this.shell.activateWidget(webview.id);
this.shell.activateWidget(view.id);
}
}
$setTitle(handle: string, value: string): void {
Expand Down Expand Up @@ -144,10 +216,34 @@ export class WebviewsMainImpl implements WebviewsMain {
return Promise.resolve(webview !== undefined);
}
$registerSerializer(viewType: string): void {
throw new Error('Method not implemented.');
this.revivers.add(viewType);
}
$unregisterSerializer(viewType: string): void {
throw new Error('Method not implemented.');
this.revivers.delete(viewType);
}

private async checkViewOptions(handler: string, viewColumn?: number | undefined) {
const options = this.viewsOptions.get(handler);
if (!options || !options.panelOptions) {
return;
}
const view = this.views.get(options.panelId);
if (!view) {
return;
}
const active = !!this.shell.activeWidget ? this.shell.activeWidget.id === view!.id : false;
const visible = view!.isVisible;
if (viewColumn === undefined) {
this.viewColumnService.updateViewColumns();
viewColumn = this.viewColumnService.hasViewColumn(view.id) ? this.viewColumnService.getViewColumn(view.id)! : 0;
}
if (viewColumn === undefined && options.panelOptions.viewColumn === viewColumn && options.visible === visible && options.active === active) {
return;
}
options.active = active;
options.visible = visible;
options.panelOptions.viewColumn = viewColumn;
this.proxy.$onDidChangeWebviewPanelViewState(options.panelId, { active, visible, position: options.panelOptions.viewColumn! });
}

private getWebview(viewId: string): WebviewWidget {
Expand All @@ -165,6 +261,7 @@ export class WebviewsMainImpl implements WebviewsMain {
}
const cleanUp = () => {
this.views.delete(viewId);
this.viewsOptions.delete(viewId);
};
this.proxy.$onDidDisposeWebviewPanel(viewId).then(cleanUp, cleanUp);
}
Expand Down
6 changes: 6 additions & 0 deletions packages/plugin-ext/src/plugin/type-converters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ export function toViewColumn(ep?: EditorPosition): theia.ViewColumn | undefined
return <number>types.ViewColumn.Two;
} else if (ep === EditorPosition.THREE) {
return <number>types.ViewColumn.Three;
} else if (ep === EditorPosition.FOUR) {
return <number>types.ViewColumn.Four;
} else if (ep === EditorPosition.FIVE) {
return <number>types.ViewColumn.Five;
} else if (ep === EditorPosition.SIX) {
return <number>types.ViewColumn.Six;
}

return undefined;
Expand Down
8 changes: 6 additions & 2 deletions packages/plugin-ext/src/plugin/webviews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ export class WebviewsExtImpl implements WebviewsExt {
}

private getWebviewPanel(viewId: string): WebviewPanelImpl | undefined {
return this.webviewPanels.get(viewId);
if (this.webviewPanels.has(viewId)) {
return this.webviewPanels.get(viewId);
}
return undefined;
}
}

Expand Down Expand Up @@ -164,7 +167,8 @@ export class WebviewImpl implements theia.Webview {
return this._html;
}

set html(newHtml: string) {
set html(html: string) {
const newHtml = html.replace(new RegExp('theia-resource:/', 'g'), '/webview/');
this.checkIsDisposed();
if (this._html !== newHtml) {
this._html = newHtml;
Expand Down

0 comments on commit a0c2a07

Please sign in to comment.