mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>');
|
||||
}
|
||||
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
"version": "3.1.0",
|
||||
"license": "MIT",
|
||||
"licenseDetail": [
|
||||
"The MIT License",
|
||||
"The Source EULA (MIT)",
|
||||
"",
|
||||
"(c) 2012-2015 GitHub",
|
||||
"",
|
||||
|
||||
@@ -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
|
||||
***************************************************************************** */
|
||||
|
||||
|
||||
@@ -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 |
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
109
src/vs/base/browser/ui/splitview/panelview.css
Normal file
109
src/vs/base/browser/ui/splitview/panelview.css
Normal 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;
|
||||
}
|
||||
430
src/vs/base/browser/ui/splitview/panelview.ts
Normal file
430
src/vs/base/browser/ui/splitview/panelview.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user