Refresh master with initial release/0.24 snapshot (#332)

* Initial port of release/0.24 source code

* Fix additional headers

* Fix a typo in launch.json
This commit is contained in:
Karl Burtram
2017-12-15 15:38:57 -08:00
committed by GitHub
parent 271b3a0b82
commit 6ad0df0e3e
7118 changed files with 107999 additions and 56466 deletions

View File

@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as platform from 'vs/base/common/platform';
import { TPromise } from 'vs/base/common/winjs.base';
import { TimeoutTimer } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
@@ -1054,3 +1055,23 @@ export function computeScreenAwareSize(cssPx: number): number {
const screenPx = window.devicePixelRatio * cssPx;
return Math.max(1, Math.floor(screenPx)) / window.devicePixelRatio;
}
/**
* See https://github.com/Microsoft/monaco-editor/issues/601
* To protect against malicious code in the linked site, particularly phishing attempts,
* the window.opener should be set to null to prevent the linked site from having access
* to change the location of the current page.
* See https://mathiasbynens.github.io/rel-noopener/
*/
export function windowOpenNoOpener(url: string): void {
if (platform.isNative) {
// In VSCode, window.open() always returns null...
window.open(url);
} else {
let newTab = window.open();
if (newTab) {
newTab.opener = null;
newTab.location.href = url;
}
}
}

View File

@@ -29,13 +29,13 @@ function createElement(options: RenderOptions): HTMLElement {
return element;
}
export function renderText(text: string, options: RenderOptions = {}): Node {
export function renderText(text: string, options: RenderOptions = {}): HTMLElement {
const element = createElement(options);
element.textContent = text;
return element;
}
export function renderFormattedText(formattedText: string, options: RenderOptions = {}): Node {
export function renderFormattedText(formattedText: string, options: RenderOptions = {}): HTMLElement {
const element = createElement(options);
_renderFormattedText(element, parseFormattedText(formattedText), options.actionCallback);
return element;
@@ -47,7 +47,7 @@ export function renderFormattedText(formattedText: string, options: RenderOption
* @param content a html element description
* @param actionCallback a callback function for any action links in the string. Argument is the zero-based index of the clicked action.
*/
export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions = {}): Node {
export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions = {}): HTMLElement {
const element = createElement(options);
const { codeBlockRenderer, actionCallback } = options;
@@ -186,7 +186,7 @@ class StringStream {
}
public next(): string {
var next = this.peek();
const next = this.peek();
this.advance();
return next;
}
@@ -219,7 +219,7 @@ interface IFormatParseTree {
}
function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionCallback?: (content: string, event?: IMouseEvent) => void) {
var child: Node;
let child: Node;
if (treeNode.type === FormatType.Text) {
child = document.createTextNode(treeNode.content);
@@ -231,7 +231,7 @@ function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionC
child = document.createElement('i');
}
else if (treeNode.type === FormatType.Action) {
var a = document.createElement('a');
const a = document.createElement('a');
a.href = '#';
DOM.addStandardDisposableListener(a, 'click', (event) => {
actionCallback(String(treeNode.index), event);
@@ -259,20 +259,20 @@ function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionC
function parseFormattedText(content: string): IFormatParseTree {
var root: IFormatParseTree = {
const root: IFormatParseTree = {
type: FormatType.Root,
children: []
};
var actionItemIndex = 0;
var current = root;
var stack: IFormatParseTree[] = [];
var stream = new StringStream(content);
let actionItemIndex = 0;
let current = root;
const stack: IFormatParseTree[] = [];
const stream = new StringStream(content);
while (!stream.eos()) {
var next = stream.next();
let next = stream.next();
var isEscapedFormatType = (next === '\\' && formatTagType(stream.peek()) !== FormatType.Invalid);
const isEscapedFormatType = (next === '\\' && formatTagType(stream.peek()) !== FormatType.Invalid);
if (isEscapedFormatType) {
next = stream.next(); // unread the backslash if it escapes a format tag type
}
@@ -284,11 +284,11 @@ function parseFormattedText(content: string): IFormatParseTree {
current = stack.pop();
}
var type = formatTagType(next);
const type = formatTagType(next);
if (current.type === type || (current.type === FormatType.Action && type === FormatType.ActionClose)) {
current = stack.pop();
} else {
var newCurrent: IFormatParseTree = {
const newCurrent: IFormatParseTree = {
type: type,
children: []
};
@@ -313,7 +313,7 @@ function parseFormattedText(content: string): IFormatParseTree {
} else {
if (current.type !== FormatType.Text) {
var textCurrent: IFormatParseTree = {
const textCurrent: IFormatParseTree = {
type: FormatType.Text,
content: next
};

View File

@@ -21,6 +21,10 @@
display: inline-block;
}
.monaco-action-bar.reverse .actions-container {
flex-direction: row-reverse;
}
.monaco-action-bar .action-item {
cursor: pointer;
display: inline-block;

View File

@@ -120,7 +120,7 @@ export class BaseActionItem extends EventEmitter implements IActionItem {
this.builder.on(DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => {
if (!enableDragging) {
DOM.EventHelper.stop(e); // do not run when dragging is on because that would disable it
DOM.EventHelper.stop(e, true); // do not run when dragging is on because that would disable it
}
if (this._action.enabled && e.button === 0) {
@@ -350,8 +350,10 @@ export class ActionItem extends BaseActionItem {
}
export enum ActionsOrientation {
HORIZONTAL = 1,
VERTICAL = 2
HORIZONTAL,
HORIZONTAL_REVERSE,
VERTICAL,
VERTICAL_REVERSE,
}
export interface IActionItemProvider {
@@ -420,18 +422,38 @@ export class ActionBar extends EventEmitter implements IActionRunner {
DOM.addClass(this.domNode, 'animated');
}
let isVertical = this.options.orientation === ActionsOrientation.VERTICAL;
if (isVertical) {
this.domNode.className += ' vertical';
let previousKey: KeyCode;
let nextKey: KeyCode;
switch (this.options.orientation) {
case ActionsOrientation.HORIZONTAL:
previousKey = KeyCode.LeftArrow;
nextKey = KeyCode.RightArrow;
break;
case ActionsOrientation.HORIZONTAL_REVERSE:
previousKey = KeyCode.RightArrow;
nextKey = KeyCode.LeftArrow;
this.domNode.className += ' reverse';
break;
case ActionsOrientation.VERTICAL:
previousKey = KeyCode.UpArrow;
nextKey = KeyCode.DownArrow;
this.domNode.className += ' vertical';
break;
case ActionsOrientation.VERTICAL_REVERSE:
previousKey = KeyCode.DownArrow;
nextKey = KeyCode.UpArrow;
this.domNode.className += ' vertical reverse';
break;
}
$(this.domNode).on(DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
let event = new StandardKeyboardEvent(e);
let eventHandled = true;
if (event.equals(isVertical ? KeyCode.UpArrow : KeyCode.LeftArrow)) {
if (event.equals(previousKey)) {
this.focusPrevious();
} else if (event.equals(isVertical ? KeyCode.DownArrow : KeyCode.RightArrow)) {
} else if (event.equals(nextKey)) {
this.focusNext();
} else if (event.equals(KeyCode.Escape)) {
this.cancel();
@@ -573,6 +595,22 @@ export class ActionBar extends EventEmitter implements IActionRunner {
});
}
public getWidth(index: number): number {
if (index >= 0 && index < this.actionsList.children.length) {
return this.actionsList.children.item(index).clientWidth;
}
return 0;
}
public getHeight(index: number): number {
if (index >= 0 && index < this.actionsList.children.length) {
return this.actionsList.children.item(index).clientHeight;
}
return 0;
}
public pull(index: number): void {
if (index >= 0 && index < this.items.length) {
this.items.splice(index, 1);

View File

@@ -31,8 +31,8 @@ const defaultOptions: IButtonStyles = {
};
export class Button extends EventEmitter {
private $el: Builder;
// {{SQL CARBON EDIT}} -- changed access modifier to protected
protected $el: Builder;
private options: IButtonOptions;
private buttonBackground: Color;

View File

@@ -73,6 +73,10 @@ export class Checkbox extends Widget {
});
}
public get enabled(): boolean {
return this.domNode.getAttribute('aria-disabled') !== 'true';
}
public focus(): void {
this.domNode.focus();
}

View File

@@ -4,11 +4,10 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { escape } from 'vs/base/common/strings';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as dom from 'vs/base/browser/dom';
import * as objects from 'vs/base/common/objects';
import { expand as expandOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { render as renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
export interface IHighlight {
start: number;
@@ -64,19 +63,19 @@ export class HighlightedLabel implements IDisposable {
}
if (pos < highlight.start) {
htmlContent.push('<span>');
htmlContent.push(expandOcticons(escape(this.text.substring(pos, highlight.start))));
htmlContent.push(renderOcticons(this.text.substring(pos, highlight.start)));
htmlContent.push('</span>');
pos = highlight.end;
}
htmlContent.push('<span class="highlight">');
htmlContent.push(expandOcticons(escape(this.text.substring(highlight.start, highlight.end))));
htmlContent.push(renderOcticons(this.text.substring(highlight.start, highlight.end)));
htmlContent.push('</span>');
pos = highlight.end;
}
if (pos < this.text.length) {
htmlContent.push('<span>');
htmlContent.push(expandOcticons(escape(this.text.substring(pos))));
htmlContent.push(renderOcticons(this.text.substring(pos)));
htmlContent.push('</span>');
}

View File

@@ -11,12 +11,18 @@ import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlighte
import { IMatch } from 'vs/base/common/filters';
import uri from 'vs/base/common/uri';
import paths = require('vs/base/common/paths');
import { IRootProvider, getPathLabel, IUserHomeProvider } from 'vs/base/common/labels';
import { IWorkspaceFolderProvider, getPathLabel, IUserHomeProvider } from 'vs/base/common/labels';
import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
export interface IIconLabelCreationOptions {
supportHighlights?: boolean;
}
export interface ILabelBadgeOptions {
title: string;
className: string;
}
export interface IIconLabelOptions {
title?: string;
extraClasses?: string[];
@@ -24,56 +30,101 @@ export interface IIconLabelOptions {
matches?: IMatch[];
}
export class IconLabel {
private domNode: HTMLElement;
private labelNode: HTMLElement | HighlightedLabel;
private descriptionNode: HTMLElement;
class FastLabelNode {
private disposed: boolean;
private _textContent: string;
private _className: string;
private _title: string;
private _empty: boolean;
constructor(container: HTMLElement, options?: IIconLabelCreationOptions) {
this.domNode = dom.append(container, dom.$('.monaco-icon-label'));
if (options && options.supportHighlights) {
this.labelNode = new HighlightedLabel(dom.append(this.domNode, dom.$('a.label-name')));
} else {
this.labelNode = dom.append(this.domNode, dom.$('a.label-name'));
}
this.descriptionNode = dom.append(this.domNode, dom.$('span.label-description'));
constructor(private _element: HTMLElement) {
}
public get element(): HTMLElement {
return this.domNode;
return this._element;
}
public get labelElement(): HTMLElement {
public set textContent(content: string) {
if (this.disposed || content === this._textContent) {
return;
}
this._textContent = content;
this._element.textContent = content;
}
public set className(className: string) {
if (this.disposed || className === this._className) {
return;
}
this._className = className;
this._element.className = className;
}
public set title(title: string) {
if (this.disposed || title === this._title) {
return;
}
this._title = title;
this._element.title = title;
}
public set empty(empty: boolean) {
if (this.disposed || empty === this._empty) {
return;
}
this._empty = empty;
this._element.style.marginLeft = empty ? '0' : null;
}
public dispose(): void {
this.disposed = true;
}
}
export class IconLabel {
private domNode: FastLabelNode;
private labelNode: FastLabelNode | HighlightedLabel;
private descriptionNode: FastLabelNode;
constructor(container: HTMLElement, options?: IIconLabelCreationOptions) {
this.domNode = new FastLabelNode(dom.append(container, dom.$('.monaco-icon-label')));
const labelDescriptionContainer = new FastLabelNode(dom.append(this.domNode.element, dom.$('.monaco-icon-label-description-container')));
if (options && options.supportHighlights) {
this.labelNode = new HighlightedLabel(dom.append(labelDescriptionContainer.element, dom.$('a.label-name')));
} else {
this.labelNode = new FastLabelNode(dom.append(labelDescriptionContainer.element, dom.$('a.label-name')));
}
this.descriptionNode = new FastLabelNode(dom.append(labelDescriptionContainer.element, dom.$('span.label-description')));
}
public get element(): HTMLElement {
return this.domNode.element;
}
public onClick(callback: (event: MouseEvent) => void): IDisposable {
return combinedDisposable([
dom.addDisposableListener(this.labelElement, dom.EventType.CLICK, (e: MouseEvent) => callback(e)),
dom.addDisposableListener(this.descriptionNode.element, dom.EventType.CLICK, (e: MouseEvent) => callback(e))
]);
}
private get labelElement(): HTMLElement {
const labelNode = this.labelNode;
if (labelNode instanceof HighlightedLabel) {
return labelNode.element;
} else {
return labelNode;
}
}
public get descriptionElement(): HTMLElement {
return this.descriptionNode;
return labelNode.element;
}
public setValue(label?: string, description?: string, options?: IIconLabelOptions): void {
const labelNode = this.labelNode;
if (labelNode instanceof HighlightedLabel) {
labelNode.set(label || '', options ? options.matches : void 0);
} else {
labelNode.textContent = label || '';
}
this.descriptionNode.textContent = description || '';
if (!description) {
dom.addClass(this.descriptionNode, 'empty');
} else {
dom.removeClass(this.descriptionNode, 'empty');
}
this.domNode.title = options && options.title ? options.title : '';
const classes = ['monaco-icon-label'];
if (options) {
if (options.extraClasses) {
@@ -86,27 +137,37 @@ export class IconLabel {
}
this.domNode.className = classes.join(' ');
this.domNode.title = options && options.title ? options.title : '';
const labelNode = this.labelNode;
if (labelNode instanceof HighlightedLabel) {
labelNode.set(label || '', options ? options.matches : void 0);
} else {
labelNode.textContent = label || '';
}
this.descriptionNode.textContent = description || '';
this.descriptionNode.empty = !description;
}
public dispose(): void {
const labelNode = this.labelNode;
if (labelNode instanceof HighlightedLabel) {
labelNode.dispose();
}
this.domNode.dispose();
this.labelNode.dispose();
this.descriptionNode.dispose();
}
}
export class FileLabel extends IconLabel {
constructor(container: HTMLElement, file: uri, provider: IRootProvider, userHome?: IUserHomeProvider) {
constructor(container: HTMLElement, file: uri, provider: IWorkspaceFolderProvider, userHome?: IUserHomeProvider) {
super(container);
this.setFile(file, provider, userHome);
}
public setFile(file: uri, provider: IRootProvider, userHome: IUserHomeProvider): void {
public setFile(file: uri, provider: IWorkspaceFolderProvider, userHome: IUserHomeProvider): void {
const parent = paths.dirname(file.fsPath);
this.setValue(paths.basename(file.fsPath), parent && parent !== '.' ? getPathLabel(parent, provider, userHome) : '', { title: file.fsPath });
}
}
}

View File

@@ -6,7 +6,7 @@
/* ---------- Icon label ---------- */
.monaco-icon-label {
display: inline-block; /* required for icons support :before rule */
display: flex; /* required for icons support :before rule */
overflow: hidden;
text-overflow: ellipsis;
}
@@ -25,25 +25,46 @@
/* fonts icons */
-webkit-font-smoothing: antialiased;
vertical-align: top;
flex-shrink: 0; /* fix for https://github.com/Microsoft/vscode/issues/13787 */
}
.monaco-icon-label > .label-name {
.monaco-icon-label > .monaco-icon-label-description-container {
overflow: hidden; /* this causes the label/description to shrink first if decorations are enabled */
text-overflow: ellipsis;
}
.monaco-icon-label > .monaco-icon-label-description-container > .label-name {
color: inherit;
white-space: pre; /* enable to show labels that include multiple whitespaces */
}
.monaco-icon-label > .label-description {
.monaco-icon-label > .monaco-icon-label-description-container > .label-description {
opacity: 0.7;
margin-left: 0.5em;
font-size: 0.9em;
white-space: pre; /* enable to show labels that include multiple whitespaces */
}
.monaco-icon-label > .label-description.empty {
margin-left: 0;
.monaco-icon-label.italic > .monaco-icon-label-description-container > .label-name,
.monaco-icon-label.italic > .monaco-icon-label-description-container > .label-description {
font-style: italic;
}
.monaco-icon-label.italic > .label-name,
.monaco-icon-label.italic > .label-description {
font-style: italic;
}
.monaco-icon-label::after {
opacity: 0.75;
font-size: 90%;
font-weight: 600;
padding: 0 12px 0 5px;
margin-left: auto;
text-align: center;
}
/* make sure selection color wins when a label is being selected */
.monaco-tree.focused .selected .monaco-icon-label, /* tree */
.monaco-tree.focused .selected .monaco-icon-label::after,
.monaco-list:focus .focused.selected .monaco-icon-label, /* list */
.monaco-list:focus .focused.selected .monaco-icon-label::after
{
color: inherit !important;
}

View File

@@ -140,7 +140,8 @@ export class InputBox extends Widget {
if (this.options.validationOptions) {
this.validation = this.options.validationOptions.validation;
this.showValidationMessage = this.options.validationOptions.showMessage || false;
// {{SQL CARBON EDIT}} Canidate for addition to vscode
this.showValidationMessage = this.options.validationOptions.showMessage || true;
}
this.element = dom.append(container, $('.monaco-inputbox.idle'));
@@ -189,7 +190,13 @@ export class InputBox extends Widget {
});
}
setTimeout(() => this.updateMirror(), 0);
setTimeout(() => {
if (!this.input) {
return;
}
this.updateMirror();
}, 0);
// Support actions
if (this.options.actions) {
@@ -376,7 +383,8 @@ export class InputBox extends Widget {
}
private _showMessage(): void {
if (!this.contextViewProvider || !this.message) {
// {{SQL CARBON EDIT}} Canidate for addition to vscode
if (!this.contextViewProvider || !this.message || !this.showValidationMessage) {
return;
}

View File

@@ -14,7 +14,7 @@ import * as platform from 'vs/base/common/platform';
import { EventType as TouchEventType } from 'vs/base/browser/touch';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import Event, { Emitter, EventBufferer, chain, mapEvent, fromCallback, any } from 'vs/base/common/event';
import Event, { Emitter, EventBufferer, chain, mapEvent, fromCallback, anyEvent } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IDelegate, IRenderer, IListEvent, IListMouseEvent, IListContextMenuEvent } from './list';
import { ListView, IListViewOptions } from './listView';
@@ -84,14 +84,9 @@ class TraitRenderer<T, D> implements IRenderer<T, ITraitTemplateData>
}
splice(start: number, deleteCount: number): void {
for (let i = 0; i < deleteCount; i++) {
const key = `key_${start + i}`;
const data = this.rendered[key];
if (data) {
data.elementDisposable.dispose();
}
}
this.rendered
.filter(({ index }) => index >= start && index < start + deleteCount)
.forEach(({ templateData }) => templateData.elementDisposable.dispose());
}
disposeTemplate(templateData: ITraitTemplateData): void {
@@ -252,6 +247,8 @@ class KeyboardController<T> implements IDisposable {
onKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(this.onDownArrow, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUpArrow, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDownArrow, this, this.disposables);
onKeyDown.filter(e => (platform.isMacintosh ? e.metaKey : e.ctrlKey) && e.keyCode === KeyCode.KEY_A).on(this.onCtrlA, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(this.onEscape, this, this.disposables);
}
private onEnter(e: StandardKeyboardEvent): void {
@@ -293,6 +290,20 @@ class KeyboardController<T> implements IDisposable {
this.view.domNode.focus();
}
private onCtrlA(e: StandardKeyboardEvent): void {
e.preventDefault();
e.stopPropagation();
this.list.setSelection(range(this.list.length));
this.view.domNode.focus();
}
private onEscape(e: StandardKeyboardEvent): void {
e.preventDefault();
e.stopPropagation();
this.list.setSelection([]);
this.view.domNode.focus();
}
dispose() {
this.disposables = dispose(this.disposables);
}
@@ -336,7 +347,7 @@ class MouseController<T> implements IDisposable {
.map(({ element, index, clientX, clientY }) => ({ element, index, anchor: { x: clientX + 1, y: clientY } }))
.event;
return any<IListContextMenuEvent<T>>(fromKeyboard, fromMouse);
return anyEvent<IListContextMenuEvent<T>>(fromKeyboard, fromMouse);
}
constructor(
@@ -353,8 +364,6 @@ class MouseController<T> implements IDisposable {
}
private onMouseDown(e: IListMouseEvent<T>): void {
e.preventDefault();
e.stopPropagation();
this.view.domNode.focus();
let reference = this.list.getFocus()[0];
@@ -378,9 +387,6 @@ class MouseController<T> implements IDisposable {
}
private onPointer(e: IListMouseEvent<T>): void {
e.preventDefault();
e.stopPropagation();
if (isSelectionChangeEvent(e)) {
return;
}
@@ -393,9 +399,6 @@ class MouseController<T> implements IDisposable {
}
private onDoubleClick(e: IListMouseEvent<T>): void {
e.preventDefault();
e.stopPropagation();
if (isSelectionChangeEvent(e)) {
return;
}
@@ -411,7 +414,7 @@ class MouseController<T> implements IDisposable {
if (isSelectionRangeChangeEvent(e) && reference !== undefined) {
const min = Math.min(reference, focus);
const max = Math.max(reference, focus);
const rangeSelection = range(max + 1, min);
const rangeSelection = range(min, max + 1);
const selection = this.list.getSelection();
const contiguousRange = getContiguousRangeContaining(disjunction(selection, [reference]), reference);
@@ -703,6 +706,10 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.view.setScrollTop(scrollTop);
}
domFocus(): void {
this.view.domNode.focus();
}
layout(height?: number): void {
this.view.layout(height);
}

View File

@@ -5,8 +5,8 @@
import octiconLabel = require('vs/base/browser/ui/octiconLabel/octiconLabel');
import { escape } from 'vs/base/common/strings';
function expand(text: string): string {
return text;
function render(text: string): string {
return escape(text);
}
class MockOcticonLabel {
@@ -18,16 +18,13 @@ class MockOcticonLabel {
}
set text(text: string) {
let innerHTML = text || '';
innerHTML = escape(innerHTML);
innerHTML = expand(innerHTML);
this._container.innerHTML = innerHTML;
this._container.innerHTML = render(text || '');
}
}
var mock: typeof octiconLabel = {
expand: expand,
render: render,
OcticonLabel: <any>MockOcticonLabel
};
export = mock;

View File

@@ -8,12 +8,16 @@ import 'vs/css!./octicons/octicons';
import 'vs/css!./octicons/octicons-animations';
import { escape } from 'vs/base/common/strings';
export function expand(text: string): string {
function expand(text: string): string {
return text.replace(/\$\(((.+?)(~(.*?))?)\)/g, (match, g1, name, g3, animation) => {
return `<span class="octicon octicon-${name} ${animation ? `octicon-animation-${animation}` : ''}"></span>`;
});
}
export function render(label: string): string {
return expand(escape(label));
}
export class OcticonLabel {
private _container: HTMLElement;
@@ -23,13 +27,10 @@ export class OcticonLabel {
}
set text(text: string) {
let innerHTML = text || '';
innerHTML = escape(innerHTML);
innerHTML = expand(innerHTML);
this._container.innerHTML = innerHTML;
this._container.innerHTML = render(text || '');
}
set title(title: string) {
this._container.title = title;
}
}
}

View File

@@ -6,7 +6,7 @@
"version": "3.1.0",
"license": "MIT",
"licenseDetail": [
"The MIT License",
"The Source EULA (MIT)",
"",
"(c) 2012-2015 GitHub",
"",

View File

@@ -6,7 +6,7 @@ When using the GitHub logos, be sure to follow the GitHub logo guidelines (https
Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL)
Applies to all font files
Code License: MIT License (http://choosealicense.com/licenses/mit/)
Code License: MIT (http://choosealicense.com/licenses/mit/)
Applies to all other files
***************************************************************************** */

View File

@@ -9,7 +9,7 @@ When using the GitHub logos, be sure to follow the GitHub logo guidelines (https
Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL)
Applies to all font files
Code License: MIT License (http://choosealicense.com/licenses/mit/)
Code License: MIT (http://choosealicense.com/licenses/mit/)
Applies to all other files
</metadata>
<defs>

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -33,6 +33,7 @@ const mapExtToMediaMimes: MapExtToMediaMimes = {
'.ico': 'image/x-icon',
'.tga': 'image/x-tga',
'.psd': 'image/vnd.adobe.photoshop',
'.webp': 'image/webp',
'.mid': 'audio/midi',
'.midi': 'audio/midi',
'.mp4a': 'audio/mp4',
@@ -115,7 +116,7 @@ export class ResourceViewer {
descriptor: IResourceDescriptor,
container: Builder,
scrollbar: DomScrollableElement,
openExternal: (URI) => void,
openExternal: (uri: URI) => void,
metadataClb?: (meta: string) => void
): void {
// Ensure CSS class

View File

@@ -268,6 +268,10 @@ export class Sash extends EventEmitter {
this.isDisabled = true;
}
get enabled(): boolean {
return !this.isDisabled;
}
public dispose(): void {
if (this.$e) {
this.$e.destroy();

View File

@@ -0,0 +1,109 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-panel-view {
width: 100%;
height: 100%;
}
.monaco-panel-view .panel {
overflow: hidden;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.monaco-panel-view .panel > .panel-header {
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
padding-left: 20px;
overflow: hidden;
display: flex;
cursor: pointer;
}
.monaco-panel-view .panel > .panel-header {
background-image: url('arrow-collapse.svg');
background-position: 2px center;
background-repeat: no-repeat;
}
.monaco-panel-view .panel > .panel-header.expanded {
background-image: url('arrow-expand.svg');
background-position: 2px center;
background-repeat: no-repeat;
}
.vs-dark .monaco-panel-view .panel > .panel-header {
background-image: url('arrow-collapse-dark.svg');
}
.vs-dark .monaco-panel-view .panel > .panel-header.expanded {
background-image: url('arrow-expand-dark.svg');
}
/* TODO: actions should be part of the panel, but they aren't yet */
.monaco-panel-view .panel > .panel-header > .actions {
display: none;
flex: 1;
}
/* TODO: actions should be part of the panel, but they aren't yet */
.monaco-panel-view .panel:hover > .panel-header.expanded > .actions,
.monaco-panel-view .panel > .panel-header.focused.expanded > .actions {
display: initial;
}
/* TODO: actions should be part of the panel, but they aren't yet */
.monaco-panel-view .panel > .panel-header > .actions .action-label {
width: 28px;
height: 22px;
background-size: 16px;
background-position: center center;
background-repeat: no-repeat;
margin-right: 0;
}
/* Bold font style does not go well with CJK fonts */
.monaco-panel-view:lang(zh-Hans) .panel > .panel-header,
.monaco-panel-view:lang(zh-Hant) .panel > .panel-header,
.monaco-panel-view:lang(ja) .panel > .panel-header,
.monaco-panel-view:lang(ko) .panel > .panel-header {
font-weight: normal;
}
.monaco-panel-view .panel > .panel-header.hidden {
display: none;
}
.monaco-panel-view .panel > .panel-body {
overflow: hidden;
flex: 1;
}
/* Animation */
.monaco-panel-view.animated .split-view-view {
transition-duration: 0.15s;
-webkit-transition-duration: 0.15s;
-moz-transition-duration: 0.15s;
transition-timing-function: ease-out;
-webkit-transition-timing-function: ease-out;
-moz-transition-timing-function: ease-out;
}
.monaco-panel-view.animated.vertical .split-view-view {
transition-property: height;
-webkit-transition-property: height;
-moz-transition-property: height;
}
.monaco-panel-view.animated.horizontal .split-view-view {
transition-property: width;
-webkit-transition-property: width;
-moz-transition-property: width;
}

View File

@@ -0,0 +1,430 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./panelview';
import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
import Event, { Emitter, chain } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { $, append, addClass, removeClass, toggleClass, trackFocus } from 'vs/base/browser/dom';
import { firstIndex } from 'vs/base/common/arrays';
import { Color, RGBA } from 'vs/base/common/color';
import { SplitView, IView } from './splitview';
export interface IPanelOptions {
ariaHeaderLabel?: string;
minimumBodySize?: number;
maximumBodySize?: number;
expanded?: boolean;
}
export interface IPanelStyles {
dropBackground?: Color;
headerForeground?: Color;
headerBackground?: Color;
headerHighContrastBorder?: Color;
}
export abstract class Panel implements IView {
private static HEADER_SIZE = 22;
protected _expanded: boolean;
private expandedSize: number | undefined = undefined;
private _headerVisible = true;
private _minimumBodySize: number;
private _maximumBodySize: number;
private ariaHeaderLabel: string;
private styles: IPanelStyles | undefined = undefined;
private el: HTMLElement;
private header: HTMLElement;
protected disposables: IDisposable[] = [];
private _onDidChange = new Emitter<number | undefined>();
readonly onDidChange: Event<number | undefined> = this._onDidChange.event;
get draggableElement(): HTMLElement {
return this.header;
}
get dropTargetElement(): HTMLElement {
return this.el;
}
private _dropBackground: Color | undefined;
get dropBackground(): Color | undefined {
return this._dropBackground;
}
get minimumBodySize(): number {
return this._minimumBodySize;
}
set minimumBodySize(size: number) {
this._minimumBodySize = size;
this._onDidChange.fire();
}
get maximumBodySize(): number {
return this._maximumBodySize;
}
set maximumBodySize(size: number) {
this._maximumBodySize = size;
this._onDidChange.fire();
}
private get headerSize(): number {
return this.headerVisible ? Panel.HEADER_SIZE : 0;
}
get minimumSize(): number {
const headerSize = this.headerSize;
const expanded = !this.headerVisible || this.isExpanded();
const minimumBodySize = expanded ? this._minimumBodySize : 0;
return headerSize + minimumBodySize;
}
get maximumSize(): number {
const headerSize = this.headerSize;
const expanded = !this.headerVisible || this.isExpanded();
const maximumBodySize = expanded ? this._maximumBodySize : 0;
return headerSize + maximumBodySize;
}
constructor(options: IPanelOptions = {}) {
this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded;
this.ariaHeaderLabel = options.ariaHeaderLabel || '';
this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : 120;
this._maximumBodySize = typeof options.maximumBodySize === 'number' ? options.maximumBodySize : Number.POSITIVE_INFINITY;
}
isExpanded(): boolean {
return this._expanded;
}
setExpanded(expanded: boolean): void {
if (this._expanded === !!expanded) {
return;
}
this._expanded = !!expanded;
this.updateHeader();
this._onDidChange.fire(expanded ? this.expandedSize : undefined);
}
get headerVisible(): boolean {
return this._headerVisible;
}
set headerVisible(visible: boolean) {
if (this._headerVisible === !!visible) {
return;
}
this._headerVisible = !!visible;
this.updateHeader();
this._onDidChange.fire();
}
render(container: HTMLElement): void {
this.el = append(container, $('.panel'));
this.header = $('.panel-header');
append(this.el, this.header);
this.header.setAttribute('tabindex', '0');
this.header.setAttribute('role', 'toolbar');
this.header.setAttribute('aria-label', this.ariaHeaderLabel);
this.renderHeader(this.header);
const focusTracker = trackFocus(this.header);
focusTracker.addFocusListener(() => addClass(this.header, 'focused'));
focusTracker.addBlurListener(() => removeClass(this.header, 'focused'));
this.updateHeader();
const onHeaderKeyDown = chain(domEvent(this.header, 'keydown'))
.map(e => new StandardKeyboardEvent(e));
onHeaderKeyDown.filter(e => e.keyCode === KeyCode.Enter || e.keyCode === KeyCode.Space)
.event(() => this.setExpanded(!this.isExpanded()), null, this.disposables);
onHeaderKeyDown.filter(e => e.keyCode === KeyCode.LeftArrow)
.event(() => this.setExpanded(false), null, this.disposables);
onHeaderKeyDown.filter(e => e.keyCode === KeyCode.RightArrow)
.event(() => this.setExpanded(true), null, this.disposables);
domEvent(this.header, 'click')
(() => this.setExpanded(!this.isExpanded()), null, this.disposables);
// TODO@Joao move this down to panelview
// onHeaderKeyDown.filter(e => e.keyCode === KeyCode.UpArrow)
// .event(focusPrevious, this, this.disposables);
// onHeaderKeyDown.filter(e => e.keyCode === KeyCode.DownArrow)
// .event(focusNext, this, this.disposables);
const body = append(this.el, $('.panel-body'));
this.renderBody(body);
}
layout(size: number): void {
const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0;
this.layoutBody(size - headerSize);
if (this.isExpanded()) {
this.expandedSize = size;
}
}
style(styles: IPanelStyles): void {
this.styles = styles;
if (!this.header) {
return;
}
this.updateHeader();
}
protected updateHeader(): void {
const expanded = !this.headerVisible || this.isExpanded();
this.header.style.height = `${this.headerSize}px`;
this.header.style.lineHeight = `${this.headerSize}px`;
toggleClass(this.header, 'hidden', !this.headerVisible);
toggleClass(this.header, 'expanded', expanded);
this.header.setAttribute('aria-expanded', String(expanded));
this.header.style.color = this.styles.headerForeground ? this.styles.headerForeground.toString() : null;
this.header.style.backgroundColor = this.styles.headerBackground ? this.styles.headerBackground.toString() : null;
this.header.style.borderTop = this.styles.headerHighContrastBorder ? `1px solid ${this.styles.headerHighContrastBorder}` : null;
this._dropBackground = this.styles.dropBackground;
}
protected abstract renderHeader(container: HTMLElement): void;
protected abstract renderBody(container: HTMLElement): void;
protected abstract layoutBody(size: number): void;
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
interface IDndContext {
draggable: PanelDraggable | null;
}
class PanelDraggable implements IDisposable {
private static DefaultDragOverBackgroundColor = new Color(new RGBA(128, 128, 128, 0.5));
// see https://github.com/Microsoft/vscode/issues/14470
private dragOverCounter = 0;
private disposables: IDisposable[] = [];
private _onDidDrop = new Emitter<{ from: Panel, to: Panel }>();
readonly onDidDrop = this._onDidDrop.event;
constructor(private panel: Panel, private context: IDndContext) {
panel.draggableElement.draggable = true;
domEvent(panel.draggableElement, 'dragstart')(this.onDragStart, this, this.disposables);
domEvent(panel.dropTargetElement, 'dragenter')(this.onDragEnter, this, this.disposables);
domEvent(panel.dropTargetElement, 'dragleave')(this.onDragLeave, this, this.disposables);
domEvent(panel.dropTargetElement, 'dragend')(this.onDragEnd, this, this.disposables);
domEvent(panel.dropTargetElement, 'drop')(this.onDrop, this, this.disposables);
}
private onDragStart(e: DragEvent): void {
e.dataTransfer.effectAllowed = 'move';
const dragImage = append(document.body, $('.monaco-panel-drag-image', {}, this.panel.draggableElement.textContent));
e.dataTransfer.setDragImage(dragImage, -10, -10);
setTimeout(() => document.body.removeChild(dragImage), 0);
this.context.draggable = this;
}
private onDragEnter(e: DragEvent): void {
if (!this.context.draggable || this.context.draggable === this) {
return;
}
this.dragOverCounter++;
this.render();
}
private onDragLeave(e: DragEvent): void {
if (!this.context.draggable || this.context.draggable === this) {
return;
}
this.dragOverCounter--;
if (this.dragOverCounter === 0) {
this.render();
}
}
private onDragEnd(e: DragEvent): void {
if (!this.context.draggable) {
return;
}
this.dragOverCounter = 0;
this.render();
this.context.draggable = null;
}
private onDrop(e: DragEvent): void {
if (!this.context.draggable) {
return;
}
this.dragOverCounter = 0;
this.render();
if (this.context.draggable !== this) {
this._onDidDrop.fire({ from: this.context.draggable.panel, to: this.panel });
}
this.context.draggable = null;
}
private render(): void {
let backgroundColor: string = null;
if (this.dragOverCounter > 0) {
backgroundColor = (this.panel.dropBackground || PanelDraggable.DefaultDragOverBackgroundColor).toString();
}
this.panel.dropTargetElement.style.backgroundColor = backgroundColor;
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
export class IPanelViewOptions {
dnd?: boolean;
}
interface IPanelItem {
panel: Panel;
disposable: IDisposable;
}
export class PanelView implements IDisposable {
private dnd: boolean;
private dndContext: IDndContext = { draggable: null };
private el: HTMLElement;
private panelItems: IPanelItem[] = [];
private splitview: SplitView;
private animationTimer: number | null = null;
private _onDidDrop = new Emitter<{ from: Panel, to: Panel }>();
readonly onDidDrop: Event<{ from: Panel, to: Panel }> = this._onDidDrop.event;
readonly onDidSashChange: Event<void>;
constructor(private container: HTMLElement, options: IPanelViewOptions = {}) {
this.dnd = !!options.dnd;
this.el = append(container, $('.monaco-panel-view'));
this.splitview = new SplitView(this.el);
this.onDidSashChange = this.splitview.onDidSashChange;
}
addPanel(panel: Panel, size: number, index = this.splitview.length): void {
const disposables: IDisposable[] = [];
panel.onDidChange(this.setupAnimation, this, disposables);
const panelItem = { panel, disposable: combinedDisposable(disposables) };
this.panelItems.splice(index, 0, panelItem);
this.splitview.addView(panel, size, index);
if (this.dnd) {
const draggable = new PanelDraggable(panel, this.dndContext);
disposables.push(draggable);
draggable.onDidDrop(this._onDidDrop.fire, this._onDidDrop, disposables);
}
}
removePanel(panel: Panel): void {
const index = firstIndex(this.panelItems, item => item.panel === panel);
if (index === -1) {
return;
}
this.splitview.removeView(index);
const panelItem = this.panelItems.splice(index, 1)[0];
panelItem.disposable.dispose();
}
movePanel(from: Panel, to: Panel): void {
const fromIndex = firstIndex(this.panelItems, item => item.panel === from);
const toIndex = firstIndex(this.panelItems, item => item.panel === to);
if (fromIndex === -1 || toIndex === -1) {
return;
}
const [panelItem] = this.panelItems.splice(fromIndex, 1);
this.panelItems.splice(toIndex, 0, panelItem);
this.splitview.moveView(fromIndex, toIndex);
}
resizePanel(panel: Panel, size: number): void {
const index = firstIndex(this.panelItems, item => item.panel === panel);
if (index === -1) {
return;
}
this.splitview.resizeView(index, size);
}
getPanelSize(panel: Panel): number {
const index = firstIndex(this.panelItems, item => item.panel === panel);
if (index === -1) {
return -1;
}
return this.splitview.getViewSize(index);
}
layout(size: number): void {
this.splitview.layout(size);
}
private setupAnimation(): void {
if (typeof this.animationTimer === 'number') {
window.clearTimeout(this.animationTimer);
}
addClass(this.el, 'animated');
this.animationTimer = window.setTimeout(() => {
this.animationTimer = null;
removeClass(this.el, 'animated');
}, 200);
}
dispose(): void {
this.panelItems.forEach(i => i.disposable.dispose());
this.splitview.dispose();
}
}

View File

@@ -3,96 +3,21 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-split-view {
.monaco-split-view2 {
position: relative;
}
.monaco-split-view > .split-view-view {
overflow: hidden;
}
.monaco-split-view.vertical > .split-view-view {
width: 100%;
}
.monaco-split-view.horizontal > .split-view-view {
height: 100%;
}
.monaco-split-view > .split-view-view > .header {
position: relative;
line-height: 22px;
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
padding-left: 20px;
.monaco-split-view2 > .split-view-view {
overflow: hidden;
display: flex;
}
.monaco-split-view > .split-view-view > .header.hide {
display: none;
.monaco-split-view2.vertical > .split-view-view {
width: 100%;
}
/* Bold font style does not go well with CJK fonts */
.monaco-split-view:lang(zh-Hans) > .split-view-view > .header,
.monaco-split-view:lang(zh-Hant) > .split-view-view > .header,
.monaco-split-view:lang(ja) > .split-view-view > .header,
.monaco-split-view:lang(ko) > .split-view-view > .header { font-weight: normal; }
.monaco-split-view > .split-view-view > .header.collapsible {
cursor: pointer;
.monaco-split-view2.horizontal > .split-view-view {
height: 100%;
}
.monaco-split-view > .split-view-view > .header.collapsible {
background-image: url('arrow-collapse.svg');
background-position: 2px center;
background-repeat: no-repeat;
}
.monaco-split-view > .split-view-view > .header.collapsible:not(.collapsed) {
background-image: url('arrow-expand.svg');
background-position: 2px center;
background-repeat: no-repeat;
}
.vs-dark .monaco-split-view > .split-view-view > .header.collapsible {
background-image: url('arrow-collapse-dark.svg');
}
.vs-dark .monaco-split-view > .split-view-view > .header.collapsible:not(.collapsed) {
background-image: url('arrow-expand-dark.svg');
background-position: 2px center;
background-repeat: no-repeat;
}
/* Animation */
.monaco-split-view.animated > .split-view-view {
transition-duration: 0.15s;
-webkit-transition-duration: 0.15s;
-moz-transition-duration: 0.15s;
transition-timing-function: ease-out;
-webkit-transition-timing-function: ease-out;
-moz-transition-timing-function: ease-out;
}
.monaco-split-view.vertical.animated > .split-view-view {
transition-property: height;
-webkit-transition-property: height;
-moz-transition-property: height;
}
.monaco-split-view.horizontal.animated > .split-view-view {
transition-property: width;
-webkit-transition-property: width;
-moz-transition-property: width;
}
.hc-black .split-view-view .action-label {
background: none;
}
.hc-black .split-view-view > .header .action-label:before {
top: 4px !important;
}

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@ export interface IToolBarOptions {
actionItemProvider?: IActionItemProvider;
ariaLabel?: string;
getKeyBinding?: (action: IAction) => ResolvedKeybinding;
actionRunner?: IActionRunner;
}
/**
@@ -49,6 +50,7 @@ export class ToolBar {
this.actionBar = new ActionBar($(element), {
orientation: options.orientation,
ariaLabel: options.ariaLabel,
actionRunner: options.actionRunner,
actionItemProvider: (action: Action) => {
// Return special action item for the toggle menu action