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

feat: support editor/title context menus for web views #6030

Merged
merged 1 commit into from
Aug 29, 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
32 changes: 32 additions & 0 deletions packages/mini-browser/src/browser/mini-browser-content-style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/********************************************************************************
* Copyright (C) 2019 TypeFox 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
********************************************************************************/
export namespace MiniBrowserContentStyle {
akosyakov marked this conversation as resolved.
Show resolved Hide resolved
export const MINI_BROWSER = 'theia-mini-browser';
export const TOOLBAR = 'theia-mini-browser-toolbar';
export const TOOLBAR_READ_ONLY = 'theia-mini-browser-toolbar-read-only';
export const PRE_LOAD = 'theia-mini-browser-load-indicator';
export const FADE_OUT = 'theia-fade-out';
export const CONTENT_AREA = 'theia-mini-browser-content-area';
export const PDF_CONTAINER = 'theia-mini-browser-pdf-container';
export const PREVIOUS = 'theia-mini-browser-previous';
export const NEXT = 'theia-mini-browser-next';
export const REFRESH = 'theia-mini-browser-refresh';
export const OPEN = 'theia-mini-browser-open';
export const BUTTON = 'theia-mini-browser-button';
export const DISABLED = 'theia-mini-browser-button-disabled';
export const TRANSPARENT_OVERLAY = 'theia-mini-browser-transparent-overlay';
export const ERROR_BAR = 'theia-mini-browser-error-bar';
}
61 changes: 19 additions & 42 deletions packages/mini-browser/src/browser/mini-browser-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { parseCssTime, Key, KeyCode } from '@theia/core/lib/browser';
import { FileSystemWatcher, FileChangeEvent } from '@theia/filesystem/lib/browser/filesystem-watcher';
import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable';
import { BaseWidget, addEventListener} from '@theia/core/lib/browser/widgets/widget';
import { BaseWidget, addEventListener } from '@theia/core/lib/browser/widgets/widget';
import { LocationMapperService } from './location-mapper-service';
import { ApplicationShellMouseTracker } from '@theia/core/lib/browser/shell/application-shell-mouse-tracker';

import debounce = require('lodash.debounce');
import { MiniBrowserContentStyle } from './mini-browser-content-style';

/**
* Initializer properties for the embedded browser widget.
Expand Down Expand Up @@ -205,7 +206,7 @@ export class MiniBrowserContent extends BaseWidget {
constructor(@inject(MiniBrowserProps) protected readonly props: MiniBrowserProps) {
super();
this.node.tabIndex = 0;
this.addClass(MiniBrowserContent.Styles.MINI_BROWSER);
this.addClass(MiniBrowserContentStyle.MINI_BROWSER);
this.input = this.createToolbar(this.node).input;
const contentArea = this.createContentArea(this.node);
this.frame = contentArea.frame;
Expand Down Expand Up @@ -271,7 +272,7 @@ export class MiniBrowserContent extends BaseWidget {

protected createToolbar(parent: HTMLElement): HTMLDivElement & Readonly<{ input: HTMLInputElement }> {
const toolbar = document.createElement('div');
toolbar.classList.add(this.getToolbarProps() === 'read-only' ? MiniBrowserContent.Styles.TOOLBAR_READ_ONLY : MiniBrowserContent.Styles.TOOLBAR);
toolbar.classList.add(this.getToolbarProps() === 'read-only' ? MiniBrowserContentStyle.TOOLBAR_READ_ONLY : MiniBrowserContentStyle.TOOLBAR);
parent.appendChild(toolbar);
this.createPrevious(toolbar);
this.createNext(toolbar);
Expand All @@ -292,10 +293,10 @@ export class MiniBrowserContent extends BaseWidget {
// tslint:disable-next-line:max-line-length
protected createContentArea(parent: HTMLElement): HTMLElement & Readonly<{ frame: HTMLIFrameElement, loadIndicator: HTMLElement, errorBar: HTMLElement & Readonly<{ message: HTMLElement }>, pdfContainer: HTMLElement, transparentOverlay: HTMLElement }> {
const contentArea = document.createElement('div');
contentArea.classList.add(MiniBrowserContent.Styles.CONTENT_AREA);
contentArea.classList.add(MiniBrowserContentStyle.CONTENT_AREA);

const loadIndicator = document.createElement('div');
loadIndicator.classList.add(MiniBrowserContent.Styles.PRE_LOAD);
loadIndicator.classList.add(MiniBrowserContentStyle.PRE_LOAD);
loadIndicator.style.display = 'none';

const errorBar = this.createErrorBar();
Expand All @@ -310,11 +311,11 @@ export class MiniBrowserContent extends BaseWidget {
this.openEmitter.event(this.handleOpen.bind(this));

const transparentOverlay = document.createElement('div');
transparentOverlay.classList.add(MiniBrowserContent.Styles.TRANSPARENT_OVERLAY);
transparentOverlay.classList.add(MiniBrowserContentStyle.TRANSPARENT_OVERLAY);
transparentOverlay.style.display = 'none';

const pdfContainer = document.createElement('div');
pdfContainer.classList.add(MiniBrowserContent.Styles.PDF_CONTAINER);
pdfContainer.classList.add(MiniBrowserContentStyle.PDF_CONTAINER);
pdfContainer.id = `${this.id}-pdf-container`;
pdfContainer.style.display = 'none';

Expand All @@ -339,7 +340,7 @@ export class MiniBrowserContent extends BaseWidget {

protected createErrorBar(): HTMLElement & Readonly<{ message: HTMLElement }> {
const errorBar = document.createElement('div');
errorBar.classList.add(MiniBrowserContent.Styles.ERROR_BAR);
errorBar.classList.add(MiniBrowserContentStyle.ERROR_BAR);
errorBar.style.display = 'none';

const icon = document.createElement('span');
Expand Down Expand Up @@ -374,21 +375,21 @@ export class MiniBrowserContent extends BaseWidget {
}

protected showLoadIndicator(): void {
this.loadIndicator.classList.remove(MiniBrowserContent.Styles.FADE_OUT);
this.loadIndicator.classList.remove(MiniBrowserContentStyle.FADE_OUT);
this.loadIndicator.style.display = 'block';
}

protected hideLoadIndicator(): void {
// Start the fade-out transition.
this.loadIndicator.classList.add(MiniBrowserContent.Styles.FADE_OUT);
this.loadIndicator.classList.add(MiniBrowserContentStyle.FADE_OUT);
// Actually hide the load indicator after the transition is finished.
const preloadStyle = window.getComputedStyle(this.loadIndicator);
const transitionDuration = parseCssTime(preloadStyle.transitionDuration, 0);
setTimeout(() => {
// But don't hide it if it was shown again since the transition started.
if (this.loadIndicator.classList.contains(MiniBrowserContent.Styles.FADE_OUT)) {
if (this.loadIndicator.classList.contains(MiniBrowserContentStyle.FADE_OUT)) {
this.loadIndicator.style.display = 'none';
this.loadIndicator.classList.remove(MiniBrowserContent.Styles.FADE_OUT);
this.loadIndicator.classList.remove(MiniBrowserContentStyle.FADE_OUT);
}
}, transitionDuration);
}
Expand Down Expand Up @@ -480,34 +481,34 @@ export class MiniBrowserContent extends BaseWidget {
}

protected createPrevious(parent: HTMLElement): HTMLElement {
return this.onClick(this.createButton(parent, 'Show The Previous Page', MiniBrowserContent.Styles.PREVIOUS), this.navigateBackEmitter);
return this.onClick(this.createButton(parent, 'Show The Previous Page', MiniBrowserContentStyle.PREVIOUS), this.navigateBackEmitter);
}

protected createNext(parent: HTMLElement): HTMLElement {
return this.onClick(this.createButton(parent, 'Show The Next Page', MiniBrowserContent.Styles.NEXT), this.navigateForwardEmitter);
return this.onClick(this.createButton(parent, 'Show The Next Page', MiniBrowserContentStyle.NEXT), this.navigateForwardEmitter);
}

protected createRefresh(parent: HTMLElement): HTMLElement {
return this.onClick(this.createButton(parent, 'Reload This Page', MiniBrowserContent.Styles.REFRESH), this.refreshEmitter);
return this.onClick(this.createButton(parent, 'Reload This Page', MiniBrowserContentStyle.REFRESH), this.refreshEmitter);
}

protected createOpen(parent: HTMLElement): HTMLElement {
const button = this.onClick(this.createButton(parent, 'Open In A New Window', MiniBrowserContent.Styles.OPEN), this.openEmitter);
const button = this.onClick(this.createButton(parent, 'Open In A New Window', MiniBrowserContentStyle.OPEN), this.openEmitter);
return button;
}

protected createButton(parent: HTMLElement, title: string, ...className: string[]): HTMLElement {
const button = document.createElement('div');
button.title = title;
button.classList.add(...className, MiniBrowserContent.Styles.BUTTON);
button.classList.add(...className, MiniBrowserContentStyle.BUTTON);
parent.appendChild(button);
return button;
}

// tslint:disable-next-line:no-any
protected onClick(element: HTMLElement, emitter: Emitter<any>): HTMLElement {
this.toDispose.push(addEventListener(element, 'click', () => {
if (!element.classList.contains(MiniBrowserContent.Styles.DISABLED)) {
if (!element.classList.contains(MiniBrowserContentStyle.DISABLED)) {
emitter.fire(undefined);
}
}));
Expand Down Expand Up @@ -629,27 +630,3 @@ export class MiniBrowserContent extends BaseWidget {
}

}

export namespace MiniBrowserContent {

export namespace Styles {

export const MINI_BROWSER = 'theia-mini-browser';
export const TOOLBAR = 'theia-mini-browser-toolbar';
export const TOOLBAR_READ_ONLY = 'theia-mini-browser-toolbar-read-only';
export const PRE_LOAD = 'theia-mini-browser-load-indicator';
export const FADE_OUT = 'theia-fade-out';
export const CONTENT_AREA = 'theia-mini-browser-content-area';
export const PDF_CONTAINER = 'theia-mini-browser-pdf-container';
export const PREVIOUS = 'theia-mini-browser-previous';
export const NEXT = 'theia-mini-browser-next';
export const REFRESH = 'theia-mini-browser-refresh';
export const OPEN = 'theia-mini-browser-open';
export const BUTTON = 'theia-mini-browser-button';
export const DISABLED = 'theia-mini-browser-button-disabled';
export const TRANSPARENT_OVERLAY = 'theia-mini-browser-transparent-overlay';
export const ERROR_BAR = 'theia-mini-browser-error-bar';

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

// tslint:disable:no-any

import CodeUri from 'vscode-uri';
import { injectable, inject } from 'inversify';
import { MenuPath, ILogger, CommandRegistry, Command, Mutable, MenuAction, SelectionService, CommandHandler } from '@theia/core';
import { EDITOR_CONTEXT_MENU, EditorWidget } from '@theia/editor/lib/browser';
Expand All @@ -35,6 +36,19 @@ import { PluginScmProvider, PluginScmResourceGroup, PluginScmResource } from '..
import { ResourceContextKey } from '@theia/core/lib/browser/resource-context-key';
import { PluginViewWidget } from '../view/plugin-view-widget';
import { ViewContextKeyService } from '../view/view-context-key-service';
import { WebviewWidget } from '../webview/webview';
import { Navigatable } from '@theia/core/lib/browser/navigatable';

type CodeEditorWidget = EditorWidget | WebviewWidget;
export namespace CodeEditorWidget {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is the appropriate place for this type and namespace.
I don't expect menus-contribution-handler to have this information.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copy the code from #5527 @akosyakov

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vince-fugnitto Where would you suggest to put it? It is require for now only for menu contributions to provide mapping between vscode core editor meaning to Theia.

export function is(arg: any): arg is CodeEditorWidget {
return arg instanceof EditorWidget || arg instanceof WebviewWidget;
}
export function getResourceUri(editor: CodeEditorWidget): CodeUri | undefined {
const resourceUri = Navigatable.is(editor) && editor.getResourceUri();
return resourceUri ? resourceUri['codeUri'] : undefined;
}
}

@injectable()
export class MenusContributionPointHandler {
Expand Down Expand Up @@ -80,14 +94,10 @@ export class MenusContributionPointHandler {
}
} else if (location === 'editor/title') {
for (const action of allMenus[location]) {
const selectedResource = (widget: EditorWidget) => {
const resourceUri = widget.getResourceUri();
return resourceUri && resourceUri['codeUri'];
};
this.registerTitleAction(location, action, {
execute: widget => widget instanceof EditorWidget && this.commands.executeCommand(action.command, selectedResource(widget)),
isEnabled: widget => widget instanceof EditorWidget && this.commands.isEnabled(action.command, selectedResource(widget)),
isVisible: widget => widget instanceof EditorWidget && this.commands.isVisible(action.command, selectedResource(widget))
execute: widget => CodeEditorWidget.is(widget) && this.commands.executeCommand(action.command, CodeEditorWidget.getResourceUri(widget)),
isEnabled: widget => CodeEditorWidget.is(widget) && this.commands.isEnabled(action.command, CodeEditorWidget.getResourceUri(widget)),
isVisible: widget => CodeEditorWidget.is(widget) && this.commands.isVisible(action.command, CodeEditorWidget.getResourceUri(widget))
});
}
} else if (location === 'view/title') {
Expand Down Expand Up @@ -180,8 +190,16 @@ export class MenusContributionPointHandler {
const command: Command = { id };
this.commands.registerCommand(command, handler);

const { group, when } = action;
const item: Mutable<TabBarToolbarItem> = { id, command: id, group: group || '', when };
const { when } = action;
// handle group and priority
akosyakov marked this conversation as resolved.
Show resolved Hide resolved
// if group is empty or white space is will be set to navigation
// ' ' => ['navigation', 0]
// 'navigation@1' => ['navigation', 1]
// '1_rest-client@2' => ['1_rest-client', 2]
// if priority is not a number it will be set to 0
// navigation@test => ['navigation', 0]
const [group, sort] = (action.group || 'navigation').split('@');
akosyakov marked this conversation as resolved.
Show resolved Hide resolved
502647092 marked this conversation as resolved.
Show resolved Hide resolved
const item: Mutable<TabBarToolbarItem> = { id, command: id, group: group.trim() || 'navigation', priority: ~~sort || undefined, when };
akosyakov marked this conversation as resolved.
Show resolved Hide resolved
this.tabBarToolbar.registerItem(item);

this.onDidRegisterCommand(action.command, pluginCommand => {
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin-ext/src/main/browser/webview/webview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import { BaseWidget, Message } from '@theia/core/lib/browser/widgets/widget';
import { IdGenerator } from '../../../common/id-generator';
import { Disposable } from '@theia/core';
import { MiniBrowserContent } from '@theia/mini-browser/lib/browser/mini-browser-content';
import { MiniBrowserContentStyle } from '@theia/mini-browser/lib/browser/mini-browser-content-style';
import { ApplicationShellMouseTracker } from '@theia/core/lib/browser/shell/application-shell-mouse-tracker';

// tslint:disable:no-any
Expand Down Expand Up @@ -55,7 +55,7 @@ export class WebviewWidget extends BaseWidget {
this.scrollY = 0;

this.transparentOverlay = document.createElement('div');
this.transparentOverlay.classList.add(MiniBrowserContent.Styles.TRANSPARENT_OVERLAY);
this.transparentOverlay.classList.add(MiniBrowserContentStyle.TRANSPARENT_OVERLAY);
this.transparentOverlay.style.display = 'none';
this.node.appendChild(this.transparentOverlay);

Expand Down