SQL Operations Studio Public Preview 1 (0.23) release source code
104
src/vs/base/browser/ui/actionbar/actionbar.css
Normal file
@@ -0,0 +1,104 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-action-bar {
|
||||
text-align: right;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.monaco-action-bar .actions-container {
|
||||
display: flex;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.monaco-action-bar.vertical .actions-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
-ms-transition: -ms-transform 50ms ease;
|
||||
-webkit-transition: -webkit-transform 50ms ease;
|
||||
-moz-transition: -moz-transform 50ms ease;
|
||||
-o-transition: -o-transform 50ms ease;
|
||||
transition: transform 50ms ease;
|
||||
position: relative; /* DO NOT REMOVE - this is the key to preventing the ghosting icon bug in Chrome 42 */
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item.disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.monaco-action-bar.animated .action-item.active {
|
||||
-ms-transform: scale(1.272019649, 1.272019649); /* 1.272019649 = √φ */
|
||||
-webkit-transform: scale(1.272019649, 1.272019649);
|
||||
-moz-transform: scale(1.272019649, 1.272019649);
|
||||
-o-transform: scale(1.272019649, 1.272019649);
|
||||
transform: scale(1.272019649, 1.272019649);
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item .icon {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-label {
|
||||
font-size: 12px;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-label.octicon {
|
||||
font-size: 15px;
|
||||
line-height: 35px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item.disabled .action-label,
|
||||
.monaco-action-bar .action-item.disabled .action-label:hover {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
/* Vertical actions */
|
||||
|
||||
.monaco-action-bar.vertical {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.monaco-action-bar.vertical .action-item {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.monaco-action-bar.vertical .action-label.separator {
|
||||
display: block;
|
||||
border-bottom: 1px solid #bbb;
|
||||
padding-top: 1px;
|
||||
margin-left: .8em;
|
||||
margin-right: .8em;
|
||||
}
|
||||
|
||||
.monaco-action-bar.animated.vertical .action-item.active {
|
||||
-ms-transform: translate(5px, 0);
|
||||
-webkit-transform: translate(5px, 0);
|
||||
-moz-transform: translate(5px, 0);
|
||||
-o-transform: translate(5px, 0);
|
||||
transform: translate(5px, 0);
|
||||
}
|
||||
|
||||
.secondary-actions .monaco-action-bar .action-label {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
/* Action Items */
|
||||
.monaco-action-bar .action-item.select-container {
|
||||
overflow: hidden; /* somehow the dropdown overflows its container, we prevent it here to not push */
|
||||
flex: 1;
|
||||
max-width: 170px;
|
||||
min-width: 60px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
768
src/vs/base/browser/ui/actionbar/actionbar.ts
Normal file
@@ -0,0 +1,768 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./actionbar';
|
||||
import nls = require('vs/nls');
|
||||
import lifecycle = require('vs/base/common/lifecycle');
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||
import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner } from 'vs/base/common/actions';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { EventType as CommonEventType } from 'vs/base/common/events';
|
||||
import types = require('vs/base/common/types');
|
||||
import { IEventEmitter, EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
import { Gesture, EventType } from 'vs/base/browser/touch';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
|
||||
export interface IActionItem extends IEventEmitter {
|
||||
actionRunner: IActionRunner;
|
||||
setActionContext(context: any): void;
|
||||
render(element: HTMLElement): void;
|
||||
isEnabled(): boolean;
|
||||
focus(fromRight?: boolean): void;
|
||||
blur(): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export interface IBaseActionItemOptions {
|
||||
draggable?: boolean;
|
||||
isMenu?: boolean;
|
||||
}
|
||||
|
||||
export class BaseActionItem extends EventEmitter implements IActionItem {
|
||||
|
||||
public builder: Builder;
|
||||
public _callOnDispose: lifecycle.IDisposable[];
|
||||
public _context: any;
|
||||
public _action: IAction;
|
||||
|
||||
private gesture: Gesture;
|
||||
private _actionRunner: IActionRunner;
|
||||
|
||||
constructor(context: any, action: IAction, protected options?: IBaseActionItemOptions) {
|
||||
super();
|
||||
|
||||
this._callOnDispose = [];
|
||||
this._context = context || this;
|
||||
this._action = action;
|
||||
|
||||
if (action instanceof Action) {
|
||||
this._callOnDispose.push(action.onDidChange(event => {
|
||||
if (!this.builder) {
|
||||
// we have not been rendered yet, so there
|
||||
// is no point in updating the UI
|
||||
return;
|
||||
}
|
||||
this._handleActionChangeEvent(event);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
protected _handleActionChangeEvent(event: IActionChangeEvent): void {
|
||||
if (event.enabled !== void 0) {
|
||||
this._updateEnabled();
|
||||
}
|
||||
if (event.checked !== void 0) {
|
||||
this._updateChecked();
|
||||
}
|
||||
if (event.class !== void 0) {
|
||||
this._updateClass();
|
||||
}
|
||||
if (event.label !== void 0) {
|
||||
this._updateLabel();
|
||||
this._updateTooltip();
|
||||
}
|
||||
if (event.tooltip !== void 0) {
|
||||
this._updateTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
public get callOnDispose() {
|
||||
return this._callOnDispose;
|
||||
}
|
||||
|
||||
public set actionRunner(actionRunner: IActionRunner) {
|
||||
this._actionRunner = actionRunner;
|
||||
}
|
||||
|
||||
public get actionRunner(): IActionRunner {
|
||||
return this._actionRunner;
|
||||
}
|
||||
|
||||
public getAction(): IAction {
|
||||
return this._action;
|
||||
}
|
||||
|
||||
public isEnabled(): boolean {
|
||||
return this._action.enabled;
|
||||
}
|
||||
|
||||
public setActionContext(newContext: any): void {
|
||||
this._context = newContext;
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
this.builder = $(container);
|
||||
this.gesture = new Gesture(container);
|
||||
|
||||
const enableDragging = this.options && this.options.draggable;
|
||||
if (enableDragging) {
|
||||
container.draggable = true;
|
||||
}
|
||||
|
||||
this.builder.on(EventType.Tap, e => this.onClick(e));
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if (this._action.enabled && e.button === 0) {
|
||||
this.builder.addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
this.builder.on(DOM.EventType.CLICK, (e: MouseEvent) => {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
// See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard
|
||||
// > Writing to the clipboard
|
||||
// > You can use the "cut" and "copy" commands without any special
|
||||
// permission if you are using them in a short-lived event handler
|
||||
// for a user action (for example, a click handler).
|
||||
|
||||
// => to get the Copy and Paste context menu actions working on Firefox,
|
||||
// there should be no timeout here
|
||||
if (this.options && this.options.isMenu) {
|
||||
this.onClick(e);
|
||||
} else {
|
||||
setTimeout(() => this.onClick(e), 50);
|
||||
}
|
||||
});
|
||||
|
||||
this.builder.on([DOM.EventType.MOUSE_UP, DOM.EventType.MOUSE_OUT], (e: MouseEvent) => {
|
||||
DOM.EventHelper.stop(e);
|
||||
this.builder.removeClass('active');
|
||||
});
|
||||
}
|
||||
|
||||
public onClick(event: Event): void {
|
||||
DOM.EventHelper.stop(event, true);
|
||||
|
||||
let context: any;
|
||||
if (types.isUndefinedOrNull(this._context)) {
|
||||
context = event;
|
||||
} else {
|
||||
context = this._context;
|
||||
context.event = event;
|
||||
}
|
||||
|
||||
this._actionRunner.run(this._action, context);
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
if (this.builder) {
|
||||
this.builder.domFocus();
|
||||
}
|
||||
}
|
||||
|
||||
public blur(): void {
|
||||
if (this.builder) {
|
||||
this.builder.domBlur();
|
||||
}
|
||||
}
|
||||
|
||||
protected _updateEnabled(): void {
|
||||
// implement in subclass
|
||||
}
|
||||
|
||||
protected _updateLabel(): void {
|
||||
// implement in subclass
|
||||
}
|
||||
|
||||
protected _updateTooltip(): void {
|
||||
// implement in subclass
|
||||
}
|
||||
|
||||
protected _updateClass(): void {
|
||||
// implement in subclass
|
||||
}
|
||||
|
||||
protected _updateChecked(): void {
|
||||
// implement in subclass
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
if (this.builder) {
|
||||
this.builder.destroy();
|
||||
this.builder = null;
|
||||
}
|
||||
|
||||
if (this.gesture) {
|
||||
this.gesture.dispose();
|
||||
this.gesture = null;
|
||||
}
|
||||
|
||||
this._callOnDispose = lifecycle.dispose(this._callOnDispose);
|
||||
}
|
||||
}
|
||||
|
||||
export class Separator extends Action {
|
||||
|
||||
public static ID = 'vs.actions.separator';
|
||||
|
||||
constructor(label?: string, order?: number) {
|
||||
super(Separator.ID, label, label ? 'separator text' : 'separator');
|
||||
this.checked = false;
|
||||
this.radio = false;
|
||||
this.enabled = false;
|
||||
this.order = order;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IActionItemOptions extends IBaseActionItemOptions {
|
||||
icon?: boolean;
|
||||
label?: boolean;
|
||||
keybinding?: string;
|
||||
}
|
||||
|
||||
export class ActionItem extends BaseActionItem {
|
||||
|
||||
protected $e: Builder;
|
||||
protected options: IActionItemOptions;
|
||||
private cssClass: string;
|
||||
|
||||
constructor(context: any, action: IAction, options: IActionItemOptions = {}) {
|
||||
super(context, action, options);
|
||||
|
||||
this.options = options;
|
||||
this.options.icon = options.icon !== undefined ? options.icon : false;
|
||||
this.options.label = options.label !== undefined ? options.label : true;
|
||||
this.cssClass = '';
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
super.render(container);
|
||||
|
||||
this.$e = $('a.action-label').appendTo(this.builder);
|
||||
if (this._action.id === Separator.ID) {
|
||||
// A separator is a presentation item
|
||||
this.$e.attr({ role: 'presentation' });
|
||||
} else {
|
||||
if (this.options.isMenu) {
|
||||
this.$e.attr({ role: 'menuitem' });
|
||||
} else {
|
||||
this.$e.attr({ role: 'button' });
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.label && this.options.keybinding) {
|
||||
$('span.keybinding').text(this.options.keybinding).appendTo(this.builder);
|
||||
}
|
||||
|
||||
this._updateClass();
|
||||
this._updateLabel();
|
||||
this._updateTooltip();
|
||||
this._updateEnabled();
|
||||
this._updateChecked();
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
super.focus();
|
||||
this.$e.domFocus();
|
||||
}
|
||||
|
||||
public _updateLabel(): void {
|
||||
if (this.options.label) {
|
||||
this.$e.text(this.getAction().label);
|
||||
}
|
||||
}
|
||||
|
||||
public _updateTooltip(): void {
|
||||
let title: string = null;
|
||||
|
||||
if (this.getAction().tooltip) {
|
||||
title = this.getAction().tooltip;
|
||||
|
||||
} else if (!this.options.label && this.getAction().label && this.options.icon) {
|
||||
title = this.getAction().label;
|
||||
|
||||
if (this.options.keybinding) {
|
||||
title = nls.localize({ key: 'titleLabel', comment: ['action title', 'action keybinding'] }, "{0} ({1})", title, this.options.keybinding);
|
||||
}
|
||||
}
|
||||
|
||||
if (title) {
|
||||
this.$e.attr({ title: title });
|
||||
}
|
||||
}
|
||||
|
||||
public _updateClass(): void {
|
||||
if (this.cssClass) {
|
||||
this.$e.removeClass(this.cssClass);
|
||||
}
|
||||
if (this.options.icon) {
|
||||
this.cssClass = this.getAction().class;
|
||||
this.$e.addClass('icon');
|
||||
if (this.cssClass) {
|
||||
this.$e.addClass(this.cssClass);
|
||||
}
|
||||
this._updateEnabled();
|
||||
} else {
|
||||
this.$e.removeClass('icon');
|
||||
}
|
||||
}
|
||||
|
||||
public _updateEnabled(): void {
|
||||
if (this.getAction().enabled) {
|
||||
this.builder.removeClass('disabled');
|
||||
this.$e.removeClass('disabled');
|
||||
this.$e.attr({ tabindex: 0 });
|
||||
} else {
|
||||
this.builder.addClass('disabled');
|
||||
this.$e.addClass('disabled');
|
||||
DOM.removeTabIndexAndUpdateFocus(this.$e.getHTMLElement());
|
||||
}
|
||||
}
|
||||
|
||||
public _updateChecked(): void {
|
||||
if (this.getAction().checked) {
|
||||
this.$e.addClass('checked');
|
||||
} else {
|
||||
this.$e.removeClass('checked');
|
||||
}
|
||||
}
|
||||
|
||||
public _updateRadio(): void {
|
||||
if (this.getAction().radio) {
|
||||
this.$e.addClass('radio');
|
||||
} else {
|
||||
this.$e.removeClass('radio');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export enum ActionsOrientation {
|
||||
HORIZONTAL = 1,
|
||||
VERTICAL = 2
|
||||
}
|
||||
|
||||
export interface IActionItemProvider {
|
||||
(action: IAction): IActionItem;
|
||||
}
|
||||
|
||||
export interface IActionBarOptions {
|
||||
orientation?: ActionsOrientation;
|
||||
context?: any;
|
||||
actionItemProvider?: IActionItemProvider;
|
||||
actionRunner?: IActionRunner;
|
||||
ariaLabel?: string;
|
||||
animated?: boolean;
|
||||
isMenu?: boolean;
|
||||
}
|
||||
|
||||
let defaultOptions: IActionBarOptions = {
|
||||
orientation: ActionsOrientation.HORIZONTAL,
|
||||
context: null
|
||||
};
|
||||
|
||||
export interface IActionOptions extends IActionItemOptions {
|
||||
index?: number;
|
||||
}
|
||||
|
||||
export class ActionBar extends EventEmitter implements IActionRunner {
|
||||
|
||||
public options: IActionBarOptions;
|
||||
|
||||
private _actionRunner: IActionRunner;
|
||||
private _context: any;
|
||||
|
||||
// Items
|
||||
public items: IActionItem[];
|
||||
|
||||
private focusedItem: number;
|
||||
private focusTracker: DOM.IFocusTracker;
|
||||
|
||||
// Elements
|
||||
public domNode: HTMLElement;
|
||||
private actionsList: HTMLElement;
|
||||
|
||||
private toDispose: lifecycle.IDisposable[];
|
||||
|
||||
constructor(container: HTMLElement | Builder, options: IActionBarOptions = defaultOptions) {
|
||||
super();
|
||||
this.options = options;
|
||||
this._context = options.context;
|
||||
this.toDispose = [];
|
||||
this._actionRunner = this.options.actionRunner;
|
||||
|
||||
if (!this._actionRunner) {
|
||||
this._actionRunner = new ActionRunner();
|
||||
this.toDispose.push(this._actionRunner);
|
||||
}
|
||||
|
||||
this.toDispose.push(this.addEmitter(this._actionRunner));
|
||||
|
||||
this.items = [];
|
||||
this.focusedItem = undefined;
|
||||
|
||||
this.domNode = document.createElement('div');
|
||||
this.domNode.className = 'monaco-action-bar';
|
||||
|
||||
if (options.animated !== false) {
|
||||
DOM.addClass(this.domNode, 'animated');
|
||||
}
|
||||
|
||||
let isVertical = this.options.orientation === ActionsOrientation.VERTICAL;
|
||||
if (isVertical) {
|
||||
this.domNode.className += ' vertical';
|
||||
}
|
||||
|
||||
$(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)) {
|
||||
this.focusPrevious();
|
||||
} else if (event.equals(isVertical ? KeyCode.DownArrow : KeyCode.RightArrow)) {
|
||||
this.focusNext();
|
||||
} else if (event.equals(KeyCode.Escape)) {
|
||||
this.cancel();
|
||||
} else if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
|
||||
// Nothing, just staying out of the else branch
|
||||
} else {
|
||||
eventHandled = false;
|
||||
}
|
||||
|
||||
if (eventHandled) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
$(this.domNode).on(DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
|
||||
let event = new StandardKeyboardEvent(e);
|
||||
|
||||
// Run action on Enter/Space
|
||||
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
|
||||
this.doTrigger(event);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
// Recompute focused item
|
||||
else if (event.equals(KeyCode.Tab) || event.equals(KeyMod.Shift | KeyCode.Tab)) {
|
||||
this.updateFocusedItem();
|
||||
}
|
||||
});
|
||||
|
||||
this.focusTracker = DOM.trackFocus(this.domNode);
|
||||
this.focusTracker.addBlurListener(() => {
|
||||
if (document.activeElement === this.domNode || !DOM.isAncestor(document.activeElement, this.domNode)) {
|
||||
this.emit(DOM.EventType.BLUR, {});
|
||||
this.focusedItem = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
this.focusTracker.addFocusListener(() => this.updateFocusedItem());
|
||||
|
||||
this.actionsList = document.createElement('ul');
|
||||
this.actionsList.className = 'actions-container';
|
||||
if (this.options.isMenu) {
|
||||
this.actionsList.setAttribute('role', 'menubar');
|
||||
} else {
|
||||
this.actionsList.setAttribute('role', 'toolbar');
|
||||
}
|
||||
if (this.options.ariaLabel) {
|
||||
this.actionsList.setAttribute('aria-label', this.options.ariaLabel);
|
||||
}
|
||||
|
||||
this.domNode.appendChild(this.actionsList);
|
||||
|
||||
((container instanceof Builder) ? container.getHTMLElement() : container).appendChild(this.domNode);
|
||||
}
|
||||
|
||||
public setAriaLabel(label: string): void {
|
||||
if (label) {
|
||||
this.actionsList.setAttribute('aria-label', label);
|
||||
} else {
|
||||
this.actionsList.removeAttribute('aria-label');
|
||||
}
|
||||
}
|
||||
|
||||
private updateFocusedItem(): void {
|
||||
for (let i = 0; i < this.actionsList.children.length; i++) {
|
||||
let elem = this.actionsList.children[i];
|
||||
if (DOM.isAncestor(document.activeElement, elem)) {
|
||||
this.focusedItem = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get context(): any {
|
||||
return this._context;
|
||||
}
|
||||
|
||||
public set context(context: any) {
|
||||
this._context = context;
|
||||
this.items.forEach(i => i.setActionContext(context));
|
||||
}
|
||||
|
||||
public get actionRunner(): IActionRunner {
|
||||
return this._actionRunner;
|
||||
}
|
||||
|
||||
public set actionRunner(actionRunner: IActionRunner) {
|
||||
if (actionRunner) {
|
||||
this._actionRunner = actionRunner;
|
||||
this.items.forEach(item => item.actionRunner = actionRunner);
|
||||
}
|
||||
}
|
||||
|
||||
public getContainer(): Builder {
|
||||
return $(this.domNode);
|
||||
}
|
||||
|
||||
public push(arg: IAction | IAction[], options: IActionOptions = {}): void {
|
||||
|
||||
const actions: IAction[] = !Array.isArray(arg) ? [arg] : arg;
|
||||
|
||||
let index = types.isNumber(options.index) ? options.index : null;
|
||||
|
||||
actions.forEach((action: IAction) => {
|
||||
const actionItemElement = document.createElement('li');
|
||||
actionItemElement.className = 'action-item';
|
||||
actionItemElement.setAttribute('role', 'presentation');
|
||||
|
||||
// Prevent native context menu on actions
|
||||
$(actionItemElement).on(DOM.EventType.CONTEXT_MENU, (e: Event) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
let item: IActionItem = null;
|
||||
|
||||
if (this.options.actionItemProvider) {
|
||||
item = this.options.actionItemProvider(action);
|
||||
}
|
||||
|
||||
if (!item) {
|
||||
item = new ActionItem(this.context, action, options);
|
||||
}
|
||||
|
||||
item.actionRunner = this._actionRunner;
|
||||
item.setActionContext(this.context);
|
||||
this.addEmitter(item);
|
||||
item.render(actionItemElement);
|
||||
|
||||
if (index === null || index < 0 || index >= this.actionsList.children.length) {
|
||||
this.actionsList.appendChild(actionItemElement);
|
||||
} else {
|
||||
this.actionsList.insertBefore(actionItemElement, this.actionsList.children[index++]);
|
||||
}
|
||||
|
||||
this.items.push(item);
|
||||
});
|
||||
}
|
||||
|
||||
public pull(index: number): void {
|
||||
if (index >= 0 && index < this.items.length) {
|
||||
this.items.splice(index, 1);
|
||||
this.actionsList.removeChild(this.actionsList.childNodes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this.items = lifecycle.dispose(this.items);
|
||||
$(this.actionsList).empty();
|
||||
}
|
||||
|
||||
public length(): number {
|
||||
return this.items.length;
|
||||
}
|
||||
|
||||
public isEmpty(): boolean {
|
||||
return this.items.length === 0;
|
||||
}
|
||||
|
||||
public focus(selectFirst?: boolean): void {
|
||||
if (selectFirst && typeof this.focusedItem === 'undefined') {
|
||||
this.focusedItem = 0;
|
||||
}
|
||||
|
||||
this.updateFocus();
|
||||
}
|
||||
|
||||
private focusNext(): void {
|
||||
if (typeof this.focusedItem === 'undefined') {
|
||||
this.focusedItem = this.items.length - 1;
|
||||
}
|
||||
|
||||
let startIndex = this.focusedItem;
|
||||
let item: IActionItem;
|
||||
|
||||
do {
|
||||
this.focusedItem = (this.focusedItem + 1) % this.items.length;
|
||||
item = this.items[this.focusedItem];
|
||||
} while (this.focusedItem !== startIndex && !item.isEnabled());
|
||||
|
||||
if (this.focusedItem === startIndex && !item.isEnabled()) {
|
||||
this.focusedItem = undefined;
|
||||
}
|
||||
|
||||
this.updateFocus();
|
||||
}
|
||||
|
||||
private focusPrevious(): void {
|
||||
if (typeof this.focusedItem === 'undefined') {
|
||||
this.focusedItem = 0;
|
||||
}
|
||||
|
||||
let startIndex = this.focusedItem;
|
||||
let item: IActionItem;
|
||||
|
||||
do {
|
||||
this.focusedItem = this.focusedItem - 1;
|
||||
|
||||
if (this.focusedItem < 0) {
|
||||
this.focusedItem = this.items.length - 1;
|
||||
}
|
||||
|
||||
item = this.items[this.focusedItem];
|
||||
} while (this.focusedItem !== startIndex && !item.isEnabled());
|
||||
|
||||
if (this.focusedItem === startIndex && !item.isEnabled()) {
|
||||
this.focusedItem = undefined;
|
||||
}
|
||||
|
||||
this.updateFocus(true);
|
||||
}
|
||||
|
||||
private updateFocus(fromRight?: boolean): void {
|
||||
if (typeof this.focusedItem === 'undefined') {
|
||||
this.domNode.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.items.length; i++) {
|
||||
let item = this.items[i];
|
||||
|
||||
let actionItem = <any>item;
|
||||
|
||||
if (i === this.focusedItem) {
|
||||
if (types.isFunction(actionItem.focus)) {
|
||||
actionItem.focus(fromRight);
|
||||
}
|
||||
} else {
|
||||
if (types.isFunction(actionItem.blur)) {
|
||||
actionItem.blur();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private doTrigger(event: StandardKeyboardEvent): void {
|
||||
if (typeof this.focusedItem === 'undefined') {
|
||||
return; //nothing to focus
|
||||
}
|
||||
|
||||
// trigger action
|
||||
let actionItem = this.items[this.focusedItem];
|
||||
if (actionItem instanceof BaseActionItem) {
|
||||
const context = (actionItem._context === null || actionItem._context === undefined) ? event : actionItem._context;
|
||||
this.run(actionItem._action, context).done();
|
||||
}
|
||||
}
|
||||
|
||||
private cancel(): void {
|
||||
if (document.activeElement instanceof HTMLElement) {
|
||||
(<HTMLElement>document.activeElement).blur(); // remove focus from focused action
|
||||
}
|
||||
|
||||
this.emit(CommonEventType.CANCEL);
|
||||
}
|
||||
|
||||
public run(action: IAction, context?: any): TPromise<void> {
|
||||
return this._actionRunner.run(action, context);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this.items !== null) {
|
||||
lifecycle.dispose(this.items);
|
||||
}
|
||||
this.items = null;
|
||||
|
||||
if (this.focusTracker) {
|
||||
this.focusTracker.dispose();
|
||||
this.focusTracker = null;
|
||||
}
|
||||
|
||||
this.toDispose = lifecycle.dispose(this.toDispose);
|
||||
|
||||
this.getContainer().destroy();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class SelectActionItem extends BaseActionItem {
|
||||
protected selectBox: SelectBox;
|
||||
protected toDispose: lifecycle.IDisposable[];
|
||||
|
||||
constructor(ctx: any, action: IAction, options: string[], selected: number) {
|
||||
super(ctx, action);
|
||||
this.selectBox = new SelectBox(options, selected);
|
||||
|
||||
this.toDispose = [];
|
||||
this.toDispose.push(this.selectBox);
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
public setOptions(options: string[], selected?: number): void {
|
||||
this.selectBox.setOptions(options, selected);
|
||||
}
|
||||
|
||||
public select(index: number): void {
|
||||
this.selectBox.select(index);
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.selectBox.onDidSelect(e => {
|
||||
this.actionRunner.run(this._action, this.getActionContext(e.selected)).done();
|
||||
}));
|
||||
}
|
||||
|
||||
protected getActionContext(option: string) {
|
||||
return option;
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
if (this.selectBox) {
|
||||
this.selectBox.focus();
|
||||
}
|
||||
}
|
||||
|
||||
public blur(): void {
|
||||
if (this.selectBox) {
|
||||
this.selectBox.blur();
|
||||
}
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
this.selectBox.render(container);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toDispose = lifecycle.dispose(this.toDispose);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
9
src/vs/base/browser/ui/aria/aria.css
Normal file
@@ -0,0 +1,9 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-aria-container {
|
||||
position: absolute; /* try to hide from workbench but not from screen readers */
|
||||
left:-999em;
|
||||
}
|
||||
69
src/vs/base/browser/ui/aria/aria.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./aria';
|
||||
import nls = require('vs/nls');
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
|
||||
let ariaContainer: HTMLElement;
|
||||
let alertContainer: HTMLElement;
|
||||
let statusContainer: HTMLElement;
|
||||
export function setARIAContainer(parent: HTMLElement) {
|
||||
ariaContainer = document.createElement('div');
|
||||
ariaContainer.className = 'monaco-aria-container';
|
||||
|
||||
alertContainer = document.createElement('div');
|
||||
alertContainer.className = 'monaco-alert';
|
||||
alertContainer.setAttribute('role', 'alert');
|
||||
alertContainer.setAttribute('aria-atomic', 'true');
|
||||
ariaContainer.appendChild(alertContainer);
|
||||
|
||||
statusContainer = document.createElement('div');
|
||||
statusContainer.className = 'monaco-status';
|
||||
statusContainer.setAttribute('role', 'status');
|
||||
statusContainer.setAttribute('aria-atomic', 'true');
|
||||
ariaContainer.appendChild(statusContainer);
|
||||
|
||||
parent.appendChild(ariaContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the provided message, will make sure that it is read as alert to screen readers.
|
||||
*/
|
||||
export function alert(msg: string): void {
|
||||
insertMessage(alertContainer, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the provided message, will make sure that it is read as status to screen readers.
|
||||
*/
|
||||
export function status(msg: string): void {
|
||||
if (isMacintosh) {
|
||||
alert(msg); // VoiceOver does not seem to support status role
|
||||
} else {
|
||||
insertMessage(statusContainer, msg);
|
||||
}
|
||||
}
|
||||
|
||||
function insertMessage(target: HTMLElement, msg: string): void {
|
||||
if (!ariaContainer) {
|
||||
// console.warn('ARIA support needs a container. Call setARIAContainer() first.');
|
||||
return;
|
||||
}
|
||||
if (target.textContent === msg) {
|
||||
msg = nls.localize('repeated', "{0} (occurred again)", msg);
|
||||
}
|
||||
|
||||
dom.clearNode(target);
|
||||
target.textContent = msg;
|
||||
|
||||
|
||||
// See https://www.paciellogroup.com/blog/2012/06/html5-accessibility-chops-aria-rolealert-browser-support/
|
||||
target.style.visibility = 'hidden';
|
||||
target.style.visibility = 'visible';
|
||||
}
|
||||
30
src/vs/base/browser/ui/button/button.css
Normal file
@@ -0,0 +1,30 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-text-button {
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-text-button:hover {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.monaco-button.disabled {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Theming support */
|
||||
|
||||
.vs .monaco-text-button:focus,
|
||||
.vs-dark .monaco-text-button:focus {
|
||||
outline-color: rgba(255, 255, 255, .5); /* buttons have a blue color, so focus indication needs to be different */
|
||||
}
|
||||
172
src/vs/base/browser/ui/button/button.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./button';
|
||||
import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
export interface IButtonOptions extends IButtonStyles {
|
||||
}
|
||||
|
||||
export interface IButtonStyles {
|
||||
buttonBackground?: Color;
|
||||
buttonHoverBackground?: Color;
|
||||
buttonForeground?: Color;
|
||||
buttonBorder?: Color;
|
||||
}
|
||||
|
||||
const defaultOptions: IButtonStyles = {
|
||||
buttonBackground: Color.fromHex('#0E639C'),
|
||||
buttonHoverBackground: Color.fromHex('#006BB3'),
|
||||
buttonForeground: Color.white
|
||||
};
|
||||
|
||||
export class Button extends EventEmitter {
|
||||
|
||||
private $el: Builder;
|
||||
private options: IButtonOptions;
|
||||
|
||||
private buttonBackground: Color;
|
||||
private buttonHoverBackground: Color;
|
||||
private buttonForeground: Color;
|
||||
private buttonBorder: Color;
|
||||
|
||||
constructor(container: Builder, options?: IButtonOptions);
|
||||
constructor(container: HTMLElement, options?: IButtonOptions);
|
||||
constructor(container: any, options?: IButtonOptions) {
|
||||
super();
|
||||
|
||||
this.options = options || Object.create(null);
|
||||
mixin(this.options, defaultOptions, false);
|
||||
|
||||
this.buttonBackground = this.options.buttonBackground;
|
||||
this.buttonHoverBackground = this.options.buttonHoverBackground;
|
||||
this.buttonForeground = this.options.buttonForeground;
|
||||
this.buttonBorder = this.options.buttonBorder;
|
||||
|
||||
this.$el = $('a.monaco-button').attr({
|
||||
'tabIndex': '0',
|
||||
'role': 'button'
|
||||
}).appendTo(container);
|
||||
|
||||
this.$el.on(DOM.EventType.CLICK, (e) => {
|
||||
if (!this.enabled) {
|
||||
DOM.EventHelper.stop(e);
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit(DOM.EventType.CLICK, e);
|
||||
});
|
||||
|
||||
this.$el.on(DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
let event = new StandardKeyboardEvent(e);
|
||||
let eventHandled = false;
|
||||
if (this.enabled && event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
|
||||
this.emit(DOM.EventType.CLICK, e);
|
||||
eventHandled = true;
|
||||
} else if (event.equals(KeyCode.Escape)) {
|
||||
this.$el.domBlur();
|
||||
eventHandled = true;
|
||||
}
|
||||
|
||||
if (eventHandled) {
|
||||
DOM.EventHelper.stop(event, true);
|
||||
}
|
||||
});
|
||||
|
||||
this.$el.on(DOM.EventType.MOUSE_OVER, (e: MouseEvent) => {
|
||||
if (!this.$el.hasClass('disabled')) {
|
||||
const hoverBackground = this.buttonHoverBackground ? this.buttonHoverBackground.toString() : null;
|
||||
if (hoverBackground) {
|
||||
this.$el.style('background-color', hoverBackground);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.$el.on(DOM.EventType.MOUSE_OUT, (e: MouseEvent) => {
|
||||
this.applyStyles(); // restore standard styles
|
||||
});
|
||||
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
style(styles: IButtonStyles): void {
|
||||
this.buttonForeground = styles.buttonForeground;
|
||||
this.buttonBackground = styles.buttonBackground;
|
||||
this.buttonHoverBackground = styles.buttonHoverBackground;
|
||||
this.buttonBorder = styles.buttonBorder;
|
||||
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}} -- removed 'private' access modifier
|
||||
applyStyles(): void {
|
||||
if (this.$el) {
|
||||
const background = this.buttonBackground ? this.buttonBackground.toString() : null;
|
||||
const foreground = this.buttonForeground ? this.buttonForeground.toString() : null;
|
||||
const border = this.buttonBorder ? this.buttonBorder.toString() : null;
|
||||
|
||||
this.$el.style('color', foreground);
|
||||
this.$el.style('background-color', background);
|
||||
|
||||
this.$el.style('border-width', border ? '1px' : null);
|
||||
this.$el.style('border-style', border ? 'solid' : null);
|
||||
this.$el.style('border-color', border);
|
||||
}
|
||||
}
|
||||
|
||||
getElement(): HTMLElement {
|
||||
return this.$el.getHTMLElement();
|
||||
}
|
||||
|
||||
set label(value: string) {
|
||||
if (!this.$el.hasClass('monaco-text-button')) {
|
||||
this.$el.addClass('monaco-text-button');
|
||||
}
|
||||
this.$el.text(value);
|
||||
}
|
||||
|
||||
set icon(iconClassName: string) {
|
||||
this.$el.addClass(iconClassName);
|
||||
}
|
||||
|
||||
set enabled(value: boolean) {
|
||||
if (value) {
|
||||
this.$el.removeClass('disabled');
|
||||
this.$el.attr({
|
||||
'aria-disabled': 'false',
|
||||
'tabIndex': '0'
|
||||
});
|
||||
} else {
|
||||
this.$el.addClass('disabled');
|
||||
this.$el.attr('aria-disabled', String(true));
|
||||
DOM.removeTabIndexAndUpdateFocus(this.$el.getHTMLElement());
|
||||
}
|
||||
}
|
||||
|
||||
get enabled() {
|
||||
return !this.$el.hasClass('disabled');
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
this.$el.domFocus();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
if (this.$el) {
|
||||
this.$el.dispose();
|
||||
this.$el = null;
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
42
src/vs/base/browser/ui/checkbox/checkbox.css
Normal file
@@ -0,0 +1,42 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.custom-checkbox {
|
||||
margin-left: 2px;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
opacity: 0.7;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 1px solid transparent;
|
||||
padding: 1px;
|
||||
|
||||
-webkit-box-sizing: border-box;
|
||||
-o-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-o-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.custom-checkbox:hover,
|
||||
.custom-checkbox.checked {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.hc-black .custom-checkbox {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.hc-black .custom-checkbox:hover {
|
||||
background: none;
|
||||
}
|
||||
121
src/vs/base/browser/ui/checkbox/checkbox.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./checkbox';
|
||||
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
export interface ICheckboxOpts extends ICheckboxStyles {
|
||||
actionClassName: string;
|
||||
title: string;
|
||||
isChecked: boolean;
|
||||
onChange: (viaKeyboard: boolean) => void;
|
||||
onKeyDown?: (e: IKeyboardEvent) => void;
|
||||
}
|
||||
|
||||
export interface ICheckboxStyles {
|
||||
inputActiveOptionBorder?: Color;
|
||||
}
|
||||
|
||||
const defaultOpts = {
|
||||
inputActiveOptionBorder: Color.fromHex('#007ACC')
|
||||
};
|
||||
|
||||
export class Checkbox extends Widget {
|
||||
|
||||
private _opts: ICheckboxOpts;
|
||||
public domNode: HTMLElement;
|
||||
|
||||
private _checked: boolean;
|
||||
|
||||
constructor(opts: ICheckboxOpts) {
|
||||
super();
|
||||
this._opts = objects.clone(opts);
|
||||
objects.mixin(this._opts, defaultOpts, false);
|
||||
this._checked = this._opts.isChecked;
|
||||
|
||||
this.domNode = document.createElement('div');
|
||||
this.domNode.title = this._opts.title;
|
||||
this.domNode.className = this._className();
|
||||
this.domNode.tabIndex = 0;
|
||||
this.domNode.setAttribute('role', 'checkbox');
|
||||
this.domNode.setAttribute('aria-checked', String(this._checked));
|
||||
this.domNode.setAttribute('aria-label', this._opts.title);
|
||||
|
||||
this.applyStyles();
|
||||
|
||||
this.onclick(this.domNode, (ev) => {
|
||||
this.checked = !this._checked;
|
||||
this._opts.onChange(false);
|
||||
ev.preventDefault();
|
||||
});
|
||||
|
||||
this.onkeydown(this.domNode, (keyboardEvent) => {
|
||||
if (keyboardEvent.keyCode === KeyCode.Space || keyboardEvent.keyCode === KeyCode.Enter) {
|
||||
this.checked = !this._checked;
|
||||
this._opts.onChange(true);
|
||||
keyboardEvent.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._opts.onKeyDown) {
|
||||
this._opts.onKeyDown(keyboardEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
this.domNode.focus();
|
||||
}
|
||||
|
||||
public get checked(): boolean {
|
||||
return this._checked;
|
||||
}
|
||||
|
||||
public set checked(newIsChecked: boolean) {
|
||||
this._checked = newIsChecked;
|
||||
this.domNode.setAttribute('aria-checked', String(this._checked));
|
||||
this.domNode.className = this._className();
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
private _className(): string {
|
||||
return 'custom-checkbox ' + this._opts.actionClassName + ' ' + (this._checked ? 'checked' : 'unchecked');
|
||||
}
|
||||
|
||||
public width(): number {
|
||||
return 2 /*marginleft*/ + 2 /*border*/ + 2 /*padding*/ + 16 /* icon width */;
|
||||
}
|
||||
|
||||
public style(styles: ICheckboxStyles): void {
|
||||
if (styles.inputActiveOptionBorder) {
|
||||
this._opts.inputActiveOptionBorder = styles.inputActiveOptionBorder;
|
||||
}
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
protected applyStyles(): void {
|
||||
if (this.domNode) {
|
||||
this.domNode.style.borderColor = this._checked && this._opts.inputActiveOptionBorder ? this._opts.inputActiveOptionBorder.toString() : 'transparent';
|
||||
}
|
||||
}
|
||||
|
||||
public enable(): void {
|
||||
this.domNode.tabIndex = 0;
|
||||
this.domNode.setAttribute('aria-disabled', String(false));
|
||||
}
|
||||
|
||||
public disable(): void {
|
||||
DOM.removeTabIndexAndUpdateFocus(this.domNode);
|
||||
this.domNode.setAttribute('aria-disabled', String(true));
|
||||
}
|
||||
}
|
||||
9
src/vs/base/browser/ui/contextview/contextview.css
Normal file
@@ -0,0 +1,9 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.context-view {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
}
|
||||
273
src/vs/base/browser/ui/contextview/contextview.ts
Normal file
@@ -0,0 +1,273 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./contextview';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
|
||||
export interface IAnchor {
|
||||
x: number;
|
||||
y: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export enum AnchorAlignment {
|
||||
LEFT, RIGHT
|
||||
}
|
||||
|
||||
export enum AnchorPosition {
|
||||
BELOW, ABOVE
|
||||
}
|
||||
|
||||
export interface IDelegate {
|
||||
getAnchor(): HTMLElement | IAnchor;
|
||||
render(container: HTMLElement): IDisposable;
|
||||
layout?(): void;
|
||||
anchorAlignment?: AnchorAlignment; // default: left
|
||||
anchorPosition?: AnchorPosition; // default: below
|
||||
canRelayout?: boolean; // default: true
|
||||
onDOMEvent?(e: Event, activeElement: HTMLElement): void;
|
||||
onHide?(data?: any): void;
|
||||
}
|
||||
|
||||
export interface IContextViewProvider {
|
||||
showContextView(delegate: IDelegate): void;
|
||||
hideContextView(): void;
|
||||
layout(): void;
|
||||
}
|
||||
|
||||
export interface IPosition {
|
||||
top: number;
|
||||
left: number;
|
||||
}
|
||||
|
||||
export interface ISize {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export interface IView extends IPosition, ISize { }
|
||||
|
||||
function layout(view: ISize, around: IView, viewport: IView, anchorPosition: AnchorPosition, anchorAlignment: AnchorAlignment): IPosition {
|
||||
|
||||
let chooseBiased = (a: number, aIsGood: boolean, b: number, bIsGood: boolean) => {
|
||||
if (aIsGood) {
|
||||
return a;
|
||||
}
|
||||
if (bIsGood) {
|
||||
return b;
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
let chooseOne = (a: number, aIsGood: boolean, b: number, bIsGood: boolean, aIsPreferred: boolean) => {
|
||||
if (aIsPreferred) {
|
||||
return chooseBiased(a, aIsGood, b, bIsGood);
|
||||
} else {
|
||||
return chooseBiased(b, bIsGood, a, aIsGood);
|
||||
}
|
||||
};
|
||||
|
||||
let top = (() => {
|
||||
// Compute both options (putting the segment above and below)
|
||||
let posAbove = around.top - view.height;
|
||||
let posBelow = around.top + around.height;
|
||||
|
||||
// Check for both options if they are good
|
||||
let aboveIsGood = (posAbove >= viewport.top && posAbove + view.height <= viewport.top + viewport.height);
|
||||
let belowIsGood = (posBelow >= viewport.top && posBelow + view.height <= viewport.top + viewport.height);
|
||||
|
||||
return chooseOne(posAbove, aboveIsGood, posBelow, belowIsGood, anchorPosition === AnchorPosition.ABOVE);
|
||||
})();
|
||||
|
||||
let left = (() => {
|
||||
// Compute both options (aligning left and right)
|
||||
let posLeft = around.left;
|
||||
let posRight = around.left + around.width - view.width;
|
||||
|
||||
// Check for both options if they are good
|
||||
let leftIsGood = (posLeft >= viewport.left && posLeft + view.width <= viewport.left + viewport.width);
|
||||
let rightIsGood = (posRight >= viewport.left && posRight + view.width <= viewport.left + viewport.width);
|
||||
|
||||
return chooseOne(posLeft, leftIsGood, posRight, rightIsGood, anchorAlignment === AnchorAlignment.LEFT);
|
||||
})();
|
||||
|
||||
return { top: top, left: left };
|
||||
}
|
||||
|
||||
export class ContextView extends EventEmitter {
|
||||
|
||||
private static BUBBLE_UP_EVENTS = ['click', 'keydown', 'focus', 'blur'];
|
||||
private static BUBBLE_DOWN_EVENTS = ['click'];
|
||||
|
||||
private $container: Builder;
|
||||
private $view: Builder;
|
||||
private delegate: IDelegate;
|
||||
private toDispose: IDisposable[];
|
||||
private toDisposeOnClean: IDisposable;
|
||||
|
||||
constructor(container: HTMLElement) {
|
||||
super();
|
||||
this.$view = $('.context-view').hide();
|
||||
this.setContainer(container);
|
||||
|
||||
this.toDispose = [{
|
||||
dispose: () => {
|
||||
this.setContainer(null);
|
||||
}
|
||||
}];
|
||||
|
||||
this.toDisposeOnClean = null;
|
||||
}
|
||||
|
||||
public setContainer(container: HTMLElement): void {
|
||||
if (this.$container) {
|
||||
this.$container.off(ContextView.BUBBLE_UP_EVENTS);
|
||||
this.$container.off(ContextView.BUBBLE_DOWN_EVENTS, true);
|
||||
this.$container = null;
|
||||
}
|
||||
if (container) {
|
||||
this.$container = $(container);
|
||||
this.$view.appendTo(this.$container);
|
||||
this.$container.on(ContextView.BUBBLE_UP_EVENTS, (e: Event) => {
|
||||
this.onDOMEvent(e, <HTMLElement>document.activeElement, false);
|
||||
});
|
||||
this.$container.on(ContextView.BUBBLE_DOWN_EVENTS, (e: Event) => {
|
||||
this.onDOMEvent(e, <HTMLElement>document.activeElement, true);
|
||||
}, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
public show(delegate: IDelegate): void {
|
||||
if (this.isVisible()) {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
// Show static box
|
||||
this.$view.setClass('context-view').empty().style({ top: '0px', left: '0px' }).show();
|
||||
|
||||
// Render content
|
||||
this.toDisposeOnClean = delegate.render(this.$view.getHTMLElement());
|
||||
|
||||
// Set active delegate
|
||||
this.delegate = delegate;
|
||||
|
||||
// Layout
|
||||
this.doLayout();
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
if (!this.isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.delegate.canRelayout === false) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.delegate.layout) {
|
||||
this.delegate.layout();
|
||||
}
|
||||
|
||||
this.doLayout();
|
||||
}
|
||||
|
||||
private doLayout(): void {
|
||||
// Get anchor
|
||||
let anchor = this.delegate.getAnchor();
|
||||
|
||||
// Compute around
|
||||
let around: IView;
|
||||
|
||||
// Get the element's position and size (to anchor the view)
|
||||
if (DOM.isHTMLElement(anchor)) {
|
||||
let elementPosition = DOM.getDomNodePagePosition(anchor);
|
||||
|
||||
around = {
|
||||
top: elementPosition.top,
|
||||
left: elementPosition.left,
|
||||
width: elementPosition.width,
|
||||
height: elementPosition.height
|
||||
};
|
||||
} else {
|
||||
let realAnchor = <IAnchor>anchor;
|
||||
|
||||
around = {
|
||||
top: realAnchor.y,
|
||||
left: realAnchor.x,
|
||||
width: realAnchor.width || 0,
|
||||
height: realAnchor.height || 0
|
||||
};
|
||||
}
|
||||
|
||||
let viewport = {
|
||||
top: DOM.StandardWindow.scrollY,
|
||||
left: DOM.StandardWindow.scrollX,
|
||||
height: window.innerHeight,
|
||||
width: window.innerWidth
|
||||
};
|
||||
|
||||
// Get the view's size
|
||||
let viewSize = this.$view.getTotalSize();
|
||||
let view = { width: viewSize.width, height: viewSize.height };
|
||||
|
||||
let anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW;
|
||||
let anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT;
|
||||
|
||||
let result = layout(view, around, viewport, anchorPosition, anchorAlignment);
|
||||
|
||||
let containerPosition = DOM.getDomNodePagePosition(this.$container.getHTMLElement());
|
||||
result.top -= containerPosition.top;
|
||||
result.left -= containerPosition.left;
|
||||
|
||||
this.$view.removeClass('top', 'bottom', 'left', 'right');
|
||||
this.$view.addClass(anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top');
|
||||
this.$view.addClass(anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right');
|
||||
this.$view.style({ top: result.top + 'px', left: result.left + 'px', width: 'initial' });
|
||||
}
|
||||
|
||||
public hide(data?: any): void {
|
||||
if (this.delegate && this.delegate.onHide) {
|
||||
this.delegate.onHide(data);
|
||||
}
|
||||
|
||||
this.delegate = null;
|
||||
|
||||
if (this.toDisposeOnClean) {
|
||||
this.toDisposeOnClean.dispose();
|
||||
this.toDisposeOnClean = null;
|
||||
}
|
||||
|
||||
this.$view.hide();
|
||||
}
|
||||
|
||||
private isVisible(): boolean {
|
||||
return !!this.delegate;
|
||||
}
|
||||
|
||||
private onDOMEvent(e: Event, element: HTMLElement, onCapture: boolean): void {
|
||||
if (this.delegate) {
|
||||
if (this.delegate.onDOMEvent) {
|
||||
this.delegate.onDOMEvent(e, <HTMLElement>document.activeElement);
|
||||
} else if (onCapture && !DOM.isAncestor(<HTMLElement>e.target, this.$container.getHTMLElement())) {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
this.hide();
|
||||
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
13
src/vs/base/browser/ui/countBadge/countBadge.css
Normal file
@@ -0,0 +1,13 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-count-badge {
|
||||
padding: 0.2em 0.5em;
|
||||
border-radius: 1em;
|
||||
font-size: 85%;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
display: inline;
|
||||
}
|
||||
94
src/vs/base/browser/ui/countBadge/countBadge.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./countBadge';
|
||||
import { $, append } from 'vs/base/browser/dom';
|
||||
import { format } from 'vs/base/common/strings';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
export interface ICountBadgeOptions extends ICountBadgetyles {
|
||||
count?: number;
|
||||
titleFormat?: string;
|
||||
}
|
||||
|
||||
export interface ICountBadgetyles {
|
||||
badgeBackground?: Color;
|
||||
badgeForeground?: Color;
|
||||
badgeBorder?: Color;
|
||||
}
|
||||
|
||||
const defaultOpts = {
|
||||
badgeBackground: Color.fromHex('#4D4D4D'),
|
||||
badgeForeground: Color.fromHex('#FFFFFF')
|
||||
};
|
||||
|
||||
export class CountBadge {
|
||||
|
||||
private element: HTMLElement;
|
||||
private count: number;
|
||||
private titleFormat: string;
|
||||
|
||||
private badgeBackground: Color;
|
||||
private badgeForeground: Color;
|
||||
private badgeBorder: Color;
|
||||
|
||||
private options: ICountBadgeOptions;
|
||||
|
||||
constructor(container: HTMLElement, options?: ICountBadgeOptions) {
|
||||
this.options = options || Object.create(null);
|
||||
mixin(this.options, defaultOpts, false);
|
||||
|
||||
this.badgeBackground = this.options.badgeBackground;
|
||||
this.badgeForeground = this.options.badgeForeground;
|
||||
this.badgeBorder = this.options.badgeBorder;
|
||||
|
||||
this.element = append(container, $('.monaco-count-badge'));
|
||||
this.titleFormat = this.options.titleFormat || '';
|
||||
this.setCount(this.options.count || 0);
|
||||
}
|
||||
|
||||
setCount(count: number) {
|
||||
this.count = count;
|
||||
this.render();
|
||||
}
|
||||
|
||||
setTitleFormat(titleFormat: string) {
|
||||
this.titleFormat = titleFormat;
|
||||
this.render();
|
||||
}
|
||||
|
||||
private render() {
|
||||
this.element.textContent = '' + this.count;
|
||||
this.element.title = format(this.titleFormat, this.count);
|
||||
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
style(styles: ICountBadgetyles): void {
|
||||
this.badgeBackground = styles.badgeBackground;
|
||||
this.badgeForeground = styles.badgeForeground;
|
||||
this.badgeBorder = styles.badgeBorder;
|
||||
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
private applyStyles(): void {
|
||||
if (this.element) {
|
||||
const background = this.badgeBackground ? this.badgeBackground.toString() : null;
|
||||
const foreground = this.badgeForeground ? this.badgeForeground.toString() : null;
|
||||
const border = this.badgeBorder ? this.badgeBorder.toString() : null;
|
||||
|
||||
this.element.style.backgroundColor = background;
|
||||
this.element.style.color = foreground;
|
||||
|
||||
this.element.style.borderWidth = border ? '1px' : null;
|
||||
this.element.style.borderStyle = border ? 'solid' : null;
|
||||
this.element.style.borderColor = border;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/vs/base/browser/ui/dropdown/dropdown.css
Normal file
@@ -0,0 +1,36 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.dropdown {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dropdown, .dropdown-group {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dropdown > .dropdown-label, .dropdown > .dropdown-action {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dropdown > .dropdown-action {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.dropdown > .dropdown-action > .action-label:hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.dropdown > .dropdown-action, .dropdown > .dropdown-action > .action-label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dropdown > .dropdown-label:not(:empty) {
|
||||
padding: 0 .5em;
|
||||
}
|
||||
265
src/vs/base/browser/ui/dropdown/dropdown.ts
Normal file
@@ -0,0 +1,265 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./dropdown';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Gesture, EventType as GestureEventType } from 'vs/base/browser/touch';
|
||||
import { ActionRunner, IAction } from 'vs/base/common/actions';
|
||||
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { IMenuOptions } from 'vs/base/browser/ui/menu/menu';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { EventHelper, EventType } from 'vs/base/browser/dom';
|
||||
|
||||
export interface ILabelRenderer {
|
||||
(container: HTMLElement): IDisposable;
|
||||
}
|
||||
|
||||
export interface IBaseDropdownOptions {
|
||||
label?: string;
|
||||
labelRenderer?: ILabelRenderer;
|
||||
}
|
||||
|
||||
export class BaseDropdown extends ActionRunner {
|
||||
private _toDispose: IDisposable[];
|
||||
private $el: Builder;
|
||||
private $boxContainer: Builder;
|
||||
private $label: Builder;
|
||||
private $contents: Builder;
|
||||
|
||||
constructor(container: HTMLElement, options: IBaseDropdownOptions) {
|
||||
super();
|
||||
|
||||
this._toDispose = [];
|
||||
|
||||
this.$el = $('.dropdown').appendTo(container);
|
||||
|
||||
this.$label = $('.dropdown-label');
|
||||
|
||||
let labelRenderer = options.labelRenderer;
|
||||
if (!labelRenderer) {
|
||||
labelRenderer = (container: HTMLElement): IDisposable => {
|
||||
$(container).text(options.label || '');
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
this.$label.on([EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap], (e: Event) => {
|
||||
EventHelper.stop(e, true); // prevent default click behaviour to trigger
|
||||
}).on([EventType.MOUSE_DOWN, GestureEventType.Tap], (e: Event) => {
|
||||
// We want to show the context menu on dropdown so that as a user you can press and hold the
|
||||
// mouse button, make a choice of action in the menu and release the mouse to trigger that
|
||||
// action.
|
||||
// Due to some weird bugs though, we delay showing the menu to unwind event stack
|
||||
// (see https://github.com/Microsoft/vscode/issues/27648)
|
||||
setTimeout(() => this.show(), 100);
|
||||
}).appendTo(this.$el);
|
||||
|
||||
let cleanupFn = labelRenderer(this.$label.getHTMLElement());
|
||||
|
||||
if (cleanupFn) {
|
||||
this._toDispose.push(cleanupFn);
|
||||
}
|
||||
|
||||
this._toDispose.push(new Gesture(this.$label.getHTMLElement()));
|
||||
}
|
||||
|
||||
public get toDispose(): IDisposable[] {
|
||||
return this._toDispose;
|
||||
}
|
||||
|
||||
public get element(): Builder {
|
||||
return this.$el;
|
||||
}
|
||||
|
||||
public get label(): Builder {
|
||||
return this.$label;
|
||||
}
|
||||
|
||||
public set tooltip(tooltip: string) {
|
||||
this.$label.title(tooltip);
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
protected onEvent(e: Event, activeElement: HTMLElement): void {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
this.hide();
|
||||
|
||||
this._toDispose = dispose(this.toDispose);
|
||||
|
||||
if (this.$boxContainer) {
|
||||
this.$boxContainer.destroy();
|
||||
this.$boxContainer = null;
|
||||
}
|
||||
|
||||
if (this.$contents) {
|
||||
this.$contents.destroy();
|
||||
this.$contents = null;
|
||||
}
|
||||
|
||||
if (this.$label) {
|
||||
this.$label.destroy();
|
||||
this.$label = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface IDropdownOptions extends IBaseDropdownOptions {
|
||||
contextViewProvider: IContextViewProvider;
|
||||
}
|
||||
|
||||
export class Dropdown extends BaseDropdown {
|
||||
private contextViewProvider: IContextViewProvider;
|
||||
|
||||
constructor(container: HTMLElement, options: IDropdownOptions) {
|
||||
super(container, options);
|
||||
|
||||
this.contextViewProvider = options.contextViewProvider;
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
this.element.addClass('active');
|
||||
|
||||
this.contextViewProvider.showContextView({
|
||||
getAnchor: () => this.element.getHTMLElement(),
|
||||
|
||||
render: (container) => {
|
||||
return this.renderContents(container);
|
||||
},
|
||||
|
||||
onDOMEvent: (e, activeElement) => {
|
||||
this.onEvent(e, activeElement);
|
||||
},
|
||||
|
||||
onHide: () => {
|
||||
this.element.removeClass('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
if (this.contextViewProvider) {
|
||||
this.contextViewProvider.hideContextView();
|
||||
}
|
||||
}
|
||||
|
||||
protected renderContents(container: HTMLElement): IDisposable {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IContextMenuDelegate {
|
||||
getAnchor(): HTMLElement | { x: number; y: number; };
|
||||
getActions(): TPromise<IAction[]>;
|
||||
getActionItem?(action: IAction): IActionItem;
|
||||
getActionsContext?(): any;
|
||||
getKeyBinding?(action: IAction): ResolvedKeybinding;
|
||||
getMenuClassName?(): string;
|
||||
onHide?(didCancel: boolean): void;
|
||||
}
|
||||
|
||||
export interface IContextMenuProvider {
|
||||
showContextMenu(delegate: IContextMenuDelegate): void;
|
||||
}
|
||||
|
||||
export interface IActionProvider {
|
||||
getActions(): IAction[];
|
||||
}
|
||||
|
||||
export interface IDropdownMenuOptions extends IBaseDropdownOptions {
|
||||
contextMenuProvider: IContextMenuProvider;
|
||||
actions?: IAction[];
|
||||
actionProvider?: IActionProvider;
|
||||
menuClassName?: string;
|
||||
}
|
||||
|
||||
export class DropdownMenu extends BaseDropdown {
|
||||
private _contextMenuProvider: IContextMenuProvider;
|
||||
private _menuOptions: IMenuOptions;
|
||||
private _actions: IAction[];
|
||||
private actionProvider: IActionProvider;
|
||||
private menuClassName: string;
|
||||
|
||||
constructor(container: HTMLElement, options: IDropdownMenuOptions) {
|
||||
super(container, options);
|
||||
|
||||
this._contextMenuProvider = options.contextMenuProvider;
|
||||
this.actions = options.actions || [];
|
||||
this.actionProvider = options.actionProvider;
|
||||
this.menuClassName = options.menuClassName || '';
|
||||
}
|
||||
|
||||
public set menuOptions(options: IMenuOptions) {
|
||||
this._menuOptions = options;
|
||||
}
|
||||
|
||||
public get menuOptions(): IMenuOptions {
|
||||
return this._menuOptions;
|
||||
}
|
||||
|
||||
private get actions(): IAction[] {
|
||||
if (this.actionProvider) {
|
||||
return this.actionProvider.getActions();
|
||||
}
|
||||
|
||||
return this._actions;
|
||||
}
|
||||
|
||||
private set actions(actions: IAction[]) {
|
||||
this._actions = actions;
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
this.element.addClass('active');
|
||||
|
||||
this._contextMenuProvider.showContextMenu({
|
||||
getAnchor: () => this.element.getHTMLElement(),
|
||||
getActions: () => TPromise.as(this.actions),
|
||||
getActionsContext: () => this.menuOptions ? this.menuOptions.context : null,
|
||||
getActionItem: (action) => this.menuOptions && this.menuOptions.actionItemProvider ? this.menuOptions.actionItemProvider(action) : null,
|
||||
getKeyBinding: (action: IAction) => this.menuOptions && this.menuOptions.getKeyBinding ? this.menuOptions.getKeyBinding(action) : null,
|
||||
getMenuClassName: () => this.menuClassName,
|
||||
onHide: () => this.element.removeClass('active')
|
||||
});
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
export class DropdownGroup extends EventEmitter {
|
||||
|
||||
private el: HTMLElement;
|
||||
|
||||
constructor(container: HTMLElement) {
|
||||
super();
|
||||
|
||||
this.el = document.createElement('div');
|
||||
this.el.className = 'dropdown-group';
|
||||
|
||||
container.appendChild(this.el);
|
||||
}
|
||||
|
||||
public get element(): HTMLElement {
|
||||
return this.el;
|
||||
}
|
||||
}
|
||||
55
src/vs/base/browser/ui/dropdown/linksDropdown.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { isFunction } from 'vs/base/common/types';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { DropdownMenu, IDropdownMenuOptions } from 'vs/base/browser/ui/dropdown/dropdown';
|
||||
|
||||
export interface ILinksDropdownMenuOptions extends IDropdownMenuOptions {
|
||||
tooltip: string;
|
||||
}
|
||||
|
||||
export class LinksDropdownMenu extends DropdownMenu {
|
||||
|
||||
constructor(container: HTMLElement, options: ILinksDropdownMenuOptions) {
|
||||
super(container, options);
|
||||
|
||||
this.tooltip = options.tooltip;
|
||||
}
|
||||
|
||||
protected onEvent(e: Event, activeElement: HTMLElement): void {
|
||||
if (e instanceof KeyboardEvent && ((<KeyboardEvent>e).ctrlKey || (isMacintosh && (<KeyboardEvent>e).metaKey))) {
|
||||
return; // allow to use Ctrl/Meta in workspace dropdown menu
|
||||
}
|
||||
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
export class LinkDropdownAction extends Action {
|
||||
|
||||
constructor(id: string, name: string, clazz: string, url: () => string, forceOpenInNewTab?: boolean);
|
||||
constructor(id: string, name: string, clazz: string, url: string, forceOpenInNewTab?: boolean);
|
||||
constructor(id: string, name: string, clazz: string, url: any, forceOpenInNewTab?: boolean) {
|
||||
super(id, name, clazz, true, (e: Event) => {
|
||||
let urlString = url;
|
||||
|
||||
if (isFunction(url)) {
|
||||
urlString = url();
|
||||
}
|
||||
|
||||
if (forceOpenInNewTab || (e instanceof MouseEvent && ((<MouseEvent>e).ctrlKey || (isMacintosh && (<MouseEvent>e).metaKey)))) {
|
||||
window.open(urlString, '_blank');
|
||||
} else {
|
||||
window.location.href = urlString;
|
||||
}
|
||||
|
||||
return TPromise.as(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
1
src/vs/base/browser/ui/findinput/case-sensitive-dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.st0{opacity:0;fill:#262626;} .st1{fill:#262626;} .st2{fill:#C5C5C5;}</style><g id="outline"><rect class="st0" width="16" height="16"/><path class="st1" d="M14.176 5.592c-.555-.6-1.336-.904-2.322-.904-.258 0-.521.024-.784.072-.246.044-.479.101-.7.169-.228.07-.432.147-.613.229-.22.099-.389.196-.512.284l-.419.299v2.701c-.086.108-.162.223-.229.344l-2.45-6.354h-2.394l-3.753 9.804v.598h3.025l.838-2.35h2.167l.891 2.35h3.237l-.001-.003c.305.092.633.15.993.15.344 0 .671-.049.978-.146h2.853v-4.903c-.001-.975-.271-1.763-.805-2.34z"/></g><g id="icon_x5F_bg"><path class="st2" d="M7.611 11.834l-.891-2.35h-3.562l-.838 2.35h-1.095l3.217-8.402h1.02l3.24 8.402h-1.091zm-2.531-6.814l-.044-.135-.038-.156-.029-.152-.024-.126h-.023l-.021.126-.032.152-.038.156-.044.135-1.307 3.574h2.918l-1.318-3.574z"/><path class="st2" d="M13.02 11.834v-.938h-.023c-.199.352-.456.62-.771.806s-.673.278-1.075.278c-.313 0-.588-.045-.826-.135s-.438-.212-.598-.366-.281-.338-.363-.551-.124-.442-.124-.688c0-.262.039-.502.117-.721s.198-.412.36-.58.367-.308.615-.419.544-.19.888-.237l1.811-.252c0-.273-.029-.507-.088-.7s-.143-.351-.252-.472-.241-.21-.396-.267-.325-.085-.513-.085c-.363 0-.714.064-1.052.193s-.638.31-.904.54v-.984c.082-.059.196-.121.343-.188s.312-.128.495-.185.378-.104.583-.141.407-.056.606-.056c.699 0 1.229.194 1.588.583s.539.942.539 1.661v3.902h-.96zm-1.454-2.83c-.273.035-.498.085-.674.149s-.313.144-.41.237-.165.205-.202.334-.055.276-.055.44c0 .141.025.271.076.393s.124.227.22.316.215.16.357.211.308.076.495.076c.242 0 .465-.045.668-.135s.378-.214.524-.372.261-.344.343-.557.123-.442.123-.688v-.609l-1.465.205z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
1
src/vs/base/browser/ui/findinput/case-sensitive.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.st0{opacity:0;fill:#F6F6F6;} .st1{fill:#F6F6F6;} .st2{fill:#424242;}</style><g id="outline"><rect class="st0" width="16" height="16"/><path class="st1" d="M14.176 5.592c-.555-.6-1.336-.904-2.322-.904-.258 0-.521.024-.784.072-.246.044-.479.101-.7.169-.228.07-.432.147-.613.229-.22.099-.389.196-.512.284l-.419.299v2.701c-.086.108-.162.223-.229.344l-2.45-6.354h-2.394l-3.753 9.804v.598h3.025l.838-2.35h2.167l.891 2.35h3.237l-.001-.003c.305.092.633.15.993.15.344 0 .671-.049.978-.146h2.853v-4.903c-.001-.975-.271-1.763-.805-2.34z"/></g><g id="icon_x5F_bg"><path class="st2" d="M7.611 11.834l-.891-2.35h-3.562l-.838 2.35h-1.095l3.217-8.402h1.02l3.24 8.402h-1.091zm-2.531-6.814l-.044-.135-.038-.156-.029-.152-.024-.126h-.023l-.021.126-.032.152-.038.156-.044.135-1.307 3.574h2.918l-1.318-3.574z"/><path class="st2" d="M13.02 11.834v-.938h-.023c-.199.352-.456.62-.771.806s-.673.278-1.075.278c-.313 0-.588-.045-.826-.135s-.438-.212-.598-.366-.281-.338-.363-.551-.124-.442-.124-.688c0-.262.039-.502.117-.721s.198-.412.36-.58.367-.308.615-.419.544-.19.888-.237l1.811-.252c0-.273-.029-.507-.088-.7s-.143-.351-.252-.472-.241-.21-.396-.267-.325-.085-.513-.085c-.363 0-.714.064-1.052.193s-.638.31-.904.54v-.984c.082-.059.196-.121.343-.188s.312-.128.495-.185.378-.104.583-.141.407-.056.606-.056c.699 0 1.229.194 1.588.583s.539.942.539 1.661v3.902h-.96zm-1.454-2.83c-.273.035-.498.085-.674.149s-.313.144-.41.237-.165.205-.202.334-.055.276-.055.44c0 .141.025.271.076.393s.124.227.22.316.215.16.357.211.308.076.495.076c.242 0 .465-.045.668-.135s.378-.214.524-.372.261-.344.343-.557.123-.442.123-.688v-.609l-1.465.205z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
74
src/vs/base/browser/ui/findinput/findInput.css
Normal file
@@ -0,0 +1,74 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/* ---------- Find input ---------- */
|
||||
|
||||
.monaco-findInput {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.monaco-findInput .monaco-inputbox {
|
||||
font-size: 13px;
|
||||
width: 100%;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.fl:after {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.monaco-findInput > .controls {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
.vs .monaco-findInput.disabled {
|
||||
background-color: #E1E1E1;
|
||||
}
|
||||
|
||||
/* Theming */
|
||||
.vs-dark .monaco-findInput.disabled {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
/* Highlighting */
|
||||
.monaco-findInput.highlight-0 .controls {
|
||||
animation: monaco-findInput-highlight-0 100ms linear 0s;
|
||||
}
|
||||
.monaco-findInput.highlight-1 .controls {
|
||||
animation: monaco-findInput-highlight-1 100ms linear 0s;
|
||||
}
|
||||
.hc-black .monaco-findInput.highlight-0 .controls,
|
||||
.vs-dark .monaco-findInput.highlight-0 .controls {
|
||||
animation: monaco-findInput-highlight-dark-0 100ms linear 0s;
|
||||
}
|
||||
.hc-black .monaco-findInput.highlight-1 .controls,
|
||||
.vs-dark .monaco-findInput.highlight-1 .controls {
|
||||
animation: monaco-findInput-highlight-dark-1 100ms linear 0s;
|
||||
}
|
||||
|
||||
@keyframes monaco-findInput-highlight-0 {
|
||||
0% { background: rgba(253, 255, 0, 0.8); }
|
||||
100% { background: transparent; }
|
||||
}
|
||||
@keyframes monaco-findInput-highlight-1 {
|
||||
0% { background: rgba(253, 255, 0, 0.8); }
|
||||
/* Made intentionally different such that the CSS minifier does not collapse the two animations into a single one*/
|
||||
99% { background: transparent; }
|
||||
}
|
||||
|
||||
@keyframes monaco-findInput-highlight-dark-0 {
|
||||
0% { background: rgba(255, 255, 255, 0.44); }
|
||||
100% { background: transparent; }
|
||||
}
|
||||
@keyframes monaco-findInput-highlight-dark-1 {
|
||||
0% { background: rgba(255, 255, 255, 0.44); }
|
||||
/* Made intentionally different such that the CSS minifier does not collapse the two animations into a single one*/
|
||||
99% { background: transparent; }
|
||||
}
|
||||
386
src/vs/base/browser/ui/findinput/findInput.ts
Normal file
@@ -0,0 +1,386 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./findInput';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IMessage as InputBoxMessage, IInputValidator, InputBox, IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { CaseSensitiveCheckbox, WholeWordsCheckbox, RegexCheckbox } from 'vs/base/browser/ui/findinput/findInputCheckboxes';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { ICheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox';
|
||||
|
||||
export interface IFindInputOptions extends IFindInputStyles {
|
||||
placeholder?: string;
|
||||
width?: number;
|
||||
validation?: IInputValidator;
|
||||
label: string;
|
||||
|
||||
appendCaseSensitiveLabel?: string;
|
||||
appendWholeWordsLabel?: string;
|
||||
appendRegexLabel?: string;
|
||||
}
|
||||
|
||||
export interface IFindInputStyles extends IInputBoxStyles {
|
||||
inputActiveOptionBorder?: Color;
|
||||
}
|
||||
|
||||
const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input");
|
||||
|
||||
export class FindInput extends Widget {
|
||||
|
||||
static OPTION_CHANGE: string = 'optionChange';
|
||||
|
||||
private contextViewProvider: IContextViewProvider;
|
||||
private width: number;
|
||||
private placeholder: string;
|
||||
private validation: IInputValidator;
|
||||
private label: string;
|
||||
|
||||
private inputActiveOptionBorder: Color;
|
||||
private inputBackground: Color;
|
||||
private inputForeground: Color;
|
||||
private inputBorder: Color;
|
||||
|
||||
private inputValidationInfoBorder: Color;
|
||||
private inputValidationInfoBackground: Color;
|
||||
private inputValidationWarningBorder: Color;
|
||||
private inputValidationWarningBackground: Color;
|
||||
private inputValidationErrorBorder: Color;
|
||||
private inputValidationErrorBackground: Color;
|
||||
|
||||
private regex: RegexCheckbox;
|
||||
private wholeWords: WholeWordsCheckbox;
|
||||
private caseSensitive: CaseSensitiveCheckbox;
|
||||
public domNode: HTMLElement;
|
||||
public inputBox: InputBox;
|
||||
|
||||
private _onDidOptionChange = this._register(new Emitter<boolean>());
|
||||
public onDidOptionChange: Event<boolean /* via keyboard */> = this._onDidOptionChange.event;
|
||||
|
||||
private _onKeyDown = this._register(new Emitter<IKeyboardEvent>());
|
||||
public onKeyDown: Event<IKeyboardEvent> = this._onKeyDown.event;
|
||||
|
||||
private _onMouseDown = this._register(new Emitter<IMouseEvent>());
|
||||
public onMouseDown: Event<IMouseEvent> = this._onMouseDown.event;
|
||||
|
||||
private _onInput = this._register(new Emitter<void>());
|
||||
public onInput: Event<void> = this._onInput.event;
|
||||
|
||||
private _onKeyUp = this._register(new Emitter<IKeyboardEvent>());
|
||||
public onKeyUp: Event<IKeyboardEvent> = this._onKeyUp.event;
|
||||
|
||||
private _onCaseSensitiveKeyDown = this._register(new Emitter<IKeyboardEvent>());
|
||||
public onCaseSensitiveKeyDown: Event<IKeyboardEvent> = this._onCaseSensitiveKeyDown.event;
|
||||
|
||||
constructor(parent: HTMLElement, contextViewProvider: IContextViewProvider, options?: IFindInputOptions) {
|
||||
super();
|
||||
this.contextViewProvider = contextViewProvider;
|
||||
this.width = options.width || 100;
|
||||
this.placeholder = options.placeholder || '';
|
||||
this.validation = options.validation;
|
||||
this.label = options.label || NLS_DEFAULT_LABEL;
|
||||
|
||||
this.inputActiveOptionBorder = options.inputActiveOptionBorder;
|
||||
this.inputBackground = options.inputBackground;
|
||||
this.inputForeground = options.inputForeground;
|
||||
this.inputBorder = options.inputBorder;
|
||||
|
||||
this.inputValidationInfoBorder = options.inputValidationInfoBorder;
|
||||
this.inputValidationInfoBackground = options.inputValidationInfoBackground;
|
||||
this.inputValidationWarningBorder = options.inputValidationWarningBorder;
|
||||
this.inputValidationWarningBackground = options.inputValidationWarningBackground;
|
||||
this.inputValidationErrorBorder = options.inputValidationErrorBorder;
|
||||
this.inputValidationErrorBackground = options.inputValidationErrorBackground;
|
||||
|
||||
this.regex = null;
|
||||
this.wholeWords = null;
|
||||
this.caseSensitive = null;
|
||||
this.domNode = null;
|
||||
this.inputBox = null;
|
||||
|
||||
this.buildDomNode(options.appendCaseSensitiveLabel || '', options.appendWholeWordsLabel || '', options.appendRegexLabel || '');
|
||||
|
||||
if (Boolean(parent)) {
|
||||
parent.appendChild(this.domNode);
|
||||
}
|
||||
|
||||
this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown.fire(e));
|
||||
this.onkeyup(this.inputBox.inputElement, (e) => this._onKeyUp.fire(e));
|
||||
this.oninput(this.inputBox.inputElement, (e) => this._onInput.fire());
|
||||
this.onmousedown(this.inputBox.inputElement, (e) => this._onMouseDown.fire(e));
|
||||
}
|
||||
|
||||
public enable(): void {
|
||||
dom.removeClass(this.domNode, 'disabled');
|
||||
this.inputBox.enable();
|
||||
this.regex.enable();
|
||||
this.wholeWords.enable();
|
||||
this.caseSensitive.enable();
|
||||
}
|
||||
|
||||
public disable(): void {
|
||||
dom.addClass(this.domNode, 'disabled');
|
||||
this.inputBox.disable();
|
||||
this.regex.disable();
|
||||
this.wholeWords.disable();
|
||||
this.caseSensitive.disable();
|
||||
}
|
||||
|
||||
public setEnabled(enabled: boolean): void {
|
||||
if (enabled) {
|
||||
this.enable();
|
||||
} else {
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this.clearValidation();
|
||||
this.setValue('');
|
||||
this.focus();
|
||||
}
|
||||
|
||||
public setWidth(newWidth: number): void {
|
||||
this.width = newWidth;
|
||||
this.domNode.style.width = this.width + 'px';
|
||||
this.contextViewProvider.layout();
|
||||
this.setInputWidth();
|
||||
}
|
||||
|
||||
public getValue(): string {
|
||||
return this.inputBox.value;
|
||||
}
|
||||
|
||||
public setValue(value: string): void {
|
||||
if (this.inputBox.value !== value) {
|
||||
this.inputBox.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public style(styles: IFindInputStyles): void {
|
||||
this.inputActiveOptionBorder = styles.inputActiveOptionBorder;
|
||||
this.inputBackground = styles.inputBackground;
|
||||
this.inputForeground = styles.inputForeground;
|
||||
this.inputBorder = styles.inputBorder;
|
||||
|
||||
this.inputValidationInfoBackground = styles.inputValidationInfoBackground;
|
||||
this.inputValidationInfoBorder = styles.inputValidationInfoBorder;
|
||||
this.inputValidationWarningBackground = styles.inputValidationWarningBackground;
|
||||
this.inputValidationWarningBorder = styles.inputValidationWarningBorder;
|
||||
this.inputValidationErrorBackground = styles.inputValidationErrorBackground;
|
||||
this.inputValidationErrorBorder = styles.inputValidationErrorBorder;
|
||||
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
protected applyStyles(): void {
|
||||
if (this.domNode) {
|
||||
const checkBoxStyles: ICheckboxStyles = {
|
||||
inputActiveOptionBorder: this.inputActiveOptionBorder,
|
||||
};
|
||||
this.regex.style(checkBoxStyles);
|
||||
this.wholeWords.style(checkBoxStyles);
|
||||
this.caseSensitive.style(checkBoxStyles);
|
||||
|
||||
const inputBoxStyles: IInputBoxStyles = {
|
||||
inputBackground: this.inputBackground,
|
||||
inputForeground: this.inputForeground,
|
||||
inputBorder: this.inputBorder,
|
||||
inputValidationInfoBackground: this.inputValidationInfoBackground,
|
||||
inputValidationInfoBorder: this.inputValidationInfoBorder,
|
||||
inputValidationWarningBackground: this.inputValidationWarningBackground,
|
||||
inputValidationWarningBorder: this.inputValidationWarningBorder,
|
||||
inputValidationErrorBackground: this.inputValidationErrorBackground,
|
||||
inputValidationErrorBorder: this.inputValidationErrorBorder
|
||||
};
|
||||
this.inputBox.style(inputBoxStyles);
|
||||
}
|
||||
}
|
||||
|
||||
public select(): void {
|
||||
this.inputBox.select();
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
this.inputBox.focus();
|
||||
}
|
||||
|
||||
public getCaseSensitive(): boolean {
|
||||
return this.caseSensitive.checked;
|
||||
}
|
||||
|
||||
public setCaseSensitive(value: boolean): void {
|
||||
this.caseSensitive.checked = value;
|
||||
this.setInputWidth();
|
||||
}
|
||||
|
||||
public getWholeWords(): boolean {
|
||||
return this.wholeWords.checked;
|
||||
}
|
||||
|
||||
public setWholeWords(value: boolean): void {
|
||||
this.wholeWords.checked = value;
|
||||
this.setInputWidth();
|
||||
}
|
||||
|
||||
public getRegex(): boolean {
|
||||
return this.regex.checked;
|
||||
}
|
||||
|
||||
public setRegex(value: boolean): void {
|
||||
this.regex.checked = value;
|
||||
this.setInputWidth();
|
||||
this.validate();
|
||||
}
|
||||
|
||||
public focusOnCaseSensitive(): void {
|
||||
this.caseSensitive.focus();
|
||||
}
|
||||
|
||||
private _lastHighlightFindOptions: number = 0;
|
||||
public highlightFindOptions(): void {
|
||||
dom.removeClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions));
|
||||
this._lastHighlightFindOptions = 1 - this._lastHighlightFindOptions;
|
||||
dom.addClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions));
|
||||
}
|
||||
|
||||
private setInputWidth(): void {
|
||||
let w = this.width - this.caseSensitive.width() - this.wholeWords.width() - this.regex.width();
|
||||
this.inputBox.width = w;
|
||||
}
|
||||
|
||||
private buildDomNode(appendCaseSensitiveLabel: string, appendWholeWordsLabel: string, appendRegexLabel: string): void {
|
||||
this.domNode = document.createElement('div');
|
||||
this.domNode.style.width = this.width + 'px';
|
||||
dom.addClass(this.domNode, 'monaco-findInput');
|
||||
|
||||
this.inputBox = this._register(new InputBox(this.domNode, this.contextViewProvider, {
|
||||
placeholder: this.placeholder || '',
|
||||
ariaLabel: this.label || '',
|
||||
validationOptions: {
|
||||
validation: this.validation || null,
|
||||
showMessage: true
|
||||
},
|
||||
inputBackground: this.inputBackground,
|
||||
inputForeground: this.inputForeground,
|
||||
inputBorder: this.inputBorder,
|
||||
inputValidationInfoBackground: this.inputValidationInfoBackground,
|
||||
inputValidationInfoBorder: this.inputValidationInfoBorder,
|
||||
inputValidationWarningBackground: this.inputValidationWarningBackground,
|
||||
inputValidationWarningBorder: this.inputValidationWarningBorder,
|
||||
inputValidationErrorBackground: this.inputValidationErrorBackground,
|
||||
inputValidationErrorBorder: this.inputValidationErrorBorder
|
||||
}));
|
||||
|
||||
this.regex = this._register(new RegexCheckbox({
|
||||
appendTitle: appendRegexLabel,
|
||||
isChecked: false,
|
||||
onChange: (viaKeyboard) => {
|
||||
this._onDidOptionChange.fire(viaKeyboard);
|
||||
if (!viaKeyboard) {
|
||||
this.inputBox.focus();
|
||||
}
|
||||
this.setInputWidth();
|
||||
this.validate();
|
||||
},
|
||||
inputActiveOptionBorder: this.inputActiveOptionBorder
|
||||
}));
|
||||
this.wholeWords = this._register(new WholeWordsCheckbox({
|
||||
appendTitle: appendWholeWordsLabel,
|
||||
isChecked: false,
|
||||
onChange: (viaKeyboard) => {
|
||||
this._onDidOptionChange.fire(viaKeyboard);
|
||||
if (!viaKeyboard) {
|
||||
this.inputBox.focus();
|
||||
}
|
||||
this.setInputWidth();
|
||||
this.validate();
|
||||
},
|
||||
inputActiveOptionBorder: this.inputActiveOptionBorder
|
||||
}));
|
||||
this.caseSensitive = this._register(new CaseSensitiveCheckbox({
|
||||
appendTitle: appendCaseSensitiveLabel,
|
||||
isChecked: false,
|
||||
onChange: (viaKeyboard) => {
|
||||
this._onDidOptionChange.fire(viaKeyboard);
|
||||
if (!viaKeyboard) {
|
||||
this.inputBox.focus();
|
||||
}
|
||||
this.setInputWidth();
|
||||
this.validate();
|
||||
},
|
||||
onKeyDown: (e) => {
|
||||
this._onCaseSensitiveKeyDown.fire(e);
|
||||
},
|
||||
inputActiveOptionBorder: this.inputActiveOptionBorder
|
||||
}));
|
||||
|
||||
// Arrow-Key support to navigate between options
|
||||
let indexes = [this.caseSensitive.domNode, this.wholeWords.domNode, this.regex.domNode];
|
||||
this.onkeydown(this.domNode, (event: IKeyboardEvent) => {
|
||||
if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) {
|
||||
let index = indexes.indexOf(<HTMLElement>document.activeElement);
|
||||
if (index >= 0) {
|
||||
let newIndex: number;
|
||||
if (event.equals(KeyCode.RightArrow)) {
|
||||
newIndex = (index + 1) % indexes.length;
|
||||
} else if (event.equals(KeyCode.LeftArrow)) {
|
||||
if (index === 0) {
|
||||
newIndex = indexes.length - 1;
|
||||
} else {
|
||||
newIndex = index - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.equals(KeyCode.Escape)) {
|
||||
indexes[index].blur();
|
||||
} else if (newIndex >= 0) {
|
||||
indexes[newIndex].focus();
|
||||
}
|
||||
|
||||
dom.EventHelper.stop(event, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.setInputWidth();
|
||||
|
||||
let controls = document.createElement('div');
|
||||
controls.className = 'controls';
|
||||
controls.appendChild(this.caseSensitive.domNode);
|
||||
controls.appendChild(this.wholeWords.domNode);
|
||||
controls.appendChild(this.regex.domNode);
|
||||
|
||||
this.domNode.appendChild(controls);
|
||||
}
|
||||
|
||||
public validate(): void {
|
||||
this.inputBox.validate();
|
||||
}
|
||||
|
||||
public showMessage(message: InputBoxMessage): void {
|
||||
this.inputBox.showMessage(message);
|
||||
}
|
||||
|
||||
public clearMessage(): void {
|
||||
this.inputBox.hideMessage();
|
||||
}
|
||||
|
||||
private clearValidation(): void {
|
||||
this.inputBox.hideMessage();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
31
src/vs/base/browser/ui/findinput/findInputCheckboxes.css
Normal file
@@ -0,0 +1,31 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.vs .custom-checkbox.monaco-case-sensitive {
|
||||
background: url('case-sensitive.svg') center center no-repeat;
|
||||
}
|
||||
.hc-black .custom-checkbox.monaco-case-sensitive,
|
||||
.hc-black .custom-checkbox.monaco-case-sensitive:hover,
|
||||
.vs-dark .custom-checkbox.monaco-case-sensitive {
|
||||
background: url('case-sensitive-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs .custom-checkbox.monaco-whole-word {
|
||||
background: url('whole-word.svg') center center no-repeat;
|
||||
}
|
||||
.hc-black .custom-checkbox.monaco-whole-word,
|
||||
.hc-black .custom-checkbox.monaco-whole-word:hover,
|
||||
.vs-dark .custom-checkbox.monaco-whole-word {
|
||||
background: url('whole-word-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs .custom-checkbox.monaco-regex {
|
||||
background: url('regex.svg') center center no-repeat;
|
||||
}
|
||||
.hc-black .custom-checkbox.monaco-regex,
|
||||
.hc-black .custom-checkbox.monaco-regex:hover,
|
||||
.vs-dark .custom-checkbox.monaco-regex {
|
||||
background: url('regex-dark.svg') center center no-repeat;
|
||||
}
|
||||
63
src/vs/base/browser/ui/findinput/findInputCheckboxes.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./findInputCheckboxes';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
export interface IFindInputCheckboxOpts {
|
||||
appendTitle: string;
|
||||
isChecked: boolean;
|
||||
onChange: (viaKeyboard: boolean) => void;
|
||||
onKeyDown?: (e: IKeyboardEvent) => void;
|
||||
inputActiveOptionBorder?: Color;
|
||||
}
|
||||
|
||||
const NLS_CASE_SENSITIVE_CHECKBOX_LABEL = nls.localize('caseDescription', "Match Case");
|
||||
const NLS_WHOLE_WORD_CHECKBOX_LABEL = nls.localize('wordsDescription', "Match Whole Word");
|
||||
const NLS_REGEX_CHECKBOX_LABEL = nls.localize('regexDescription', "Use Regular Expression");
|
||||
|
||||
export class CaseSensitiveCheckbox extends Checkbox {
|
||||
constructor(opts: IFindInputCheckboxOpts) {
|
||||
super({
|
||||
actionClassName: 'monaco-case-sensitive',
|
||||
title: NLS_CASE_SENSITIVE_CHECKBOX_LABEL + opts.appendTitle,
|
||||
isChecked: opts.isChecked,
|
||||
onChange: opts.onChange,
|
||||
onKeyDown: opts.onKeyDown,
|
||||
inputActiveOptionBorder: opts.inputActiveOptionBorder
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class WholeWordsCheckbox extends Checkbox {
|
||||
constructor(opts: IFindInputCheckboxOpts) {
|
||||
super({
|
||||
actionClassName: 'monaco-whole-word',
|
||||
title: NLS_WHOLE_WORD_CHECKBOX_LABEL + opts.appendTitle,
|
||||
isChecked: opts.isChecked,
|
||||
onChange: opts.onChange,
|
||||
onKeyDown: opts.onKeyDown,
|
||||
inputActiveOptionBorder: opts.inputActiveOptionBorder
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class RegexCheckbox extends Checkbox {
|
||||
constructor(opts: IFindInputCheckboxOpts) {
|
||||
super({
|
||||
actionClassName: 'monaco-regex',
|
||||
title: NLS_REGEX_CHECKBOX_LABEL + opts.appendTitle,
|
||||
isChecked: opts.isChecked,
|
||||
onChange: opts.onChange,
|
||||
onKeyDown: opts.onKeyDown,
|
||||
inputActiveOptionBorder: opts.inputActiveOptionBorder
|
||||
});
|
||||
}
|
||||
}
|
||||
1
src/vs/base/browser/ui/findinput/regex-dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon fill="#2d2d30" points="13.64,7.396 12.169,2.898 10.706,3.761 11.087,2 6.557,2 6.936,3.762 5.473,2.898 4,7.396 5.682,7.554 4.513,8.561 5.013,9 2,9 2,14 7,14 7,10.747 7.978,11.606 8.82,9.725 9.661,11.602 13.144,8.562 11.968,7.554"/><g fill="#C5C5C5"><path d="M12.301 6.518l-2.772.262 2.086 1.788-1.594 1.392-1.201-2.682-1.201 2.682-1.583-1.392 2.075-1.788-2.771-.262.696-2.126 2.358 1.392-.599-2.784h2.053l-.602 2.783 2.359-1.392.696 2.127z"/><rect x="3" y="10" width="3" height="3"/></g></svg>
|
||||
|
After Width: | Height: | Size: 564 B |
1
src/vs/base/browser/ui/findinput/regex.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon fill="#F6F6F6" points="13.64,7.396 12.169,2.898 10.706,3.761 11.087,2 6.557,2 6.936,3.762 5.473,2.898 4,7.396 5.682,7.554 4.513,8.561 5.013,9 2,9 2,14 7,14 7,10.747 7.978,11.606 8.82,9.725 9.661,11.602 13.144,8.562 11.968,7.554"/><g fill="#424242"><path d="M12.301 6.518l-2.772.262 2.086 1.788-1.594 1.392-1.201-2.682-1.201 2.682-1.583-1.392 2.075-1.788-2.771-.262.696-2.126 2.358 1.392-.599-2.784h2.053l-.602 2.783 2.359-1.392.696 2.127z"/><rect x="3" y="10" width="3" height="3"/></g></svg>
|
||||
|
After Width: | Height: | Size: 564 B |
1
src/vs/base/browser/ui/findinput/whole-word-dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.st0{opacity:0;fill:#262626;} .st1{fill:#262626;} .st2{fill:#C5C5C5;}</style><g id="outline"><rect class="st0" width="16" height="16"/><path class="st1" d="M16 4.022v-3.022h-16.014v3.022h3.046l-3.043 7.945h-.004v.01l.015 1.023h-.014v1.991h16.014v-3.023h-1v-7.946h1zm-5.914 5.301c0 .233-.023.441-.066.595-.047.164-.099.247-.127.284l-.078.069-.151.026-.115-.017-.139-.137c-.031-.078-.112-.332-.112-.566 0-.254.091-.561.126-.656l.069-.141.109-.082.178-.027c.077 0 .117.014.177.056l.087.179.051.237-.009.18zm-3.695-5.301v2.893l-1.116-2.893h1.116zm-3.026 7.02h1.573l.351.926h-2.254l.33-.926zm8.635-4.354c-.206-.2-.431-.38-.695-.512-.396-.198-.853-.298-1.355-.298-.215 0-.423.02-.621.058v-1.914h2.671v2.666z"/></g><g id="icon_x5F_bg"><rect x="13" y="4" class="st2" width="1" height="8"/><path class="st2" d="M11.225 8.387c-.078-.299-.199-.562-.36-.786s-.365-.401-.609-.53-.534-.193-.866-.193c-.198 0-.38.024-.547.073-.165.049-.316.117-.453.205-.136.088-.257.194-.365.318l-.179.258v-3.154h-.893v7.422h.893v-.575l.126.175c.087.102.189.19.304.269.117.078.249.14.398.186.149.046.314.068.498.068.353 0 .666-.071.937-.212.272-.143.499-.338.682-.586.183-.25.321-.543.414-.879.093-.338.14-.703.14-1.097-.001-.342-.04-.663-.12-.962zm-1.479-.607c.151.071.282.176.39.314.109.14.194.313.255.517.051.174.082.371.089.587l-.007.125c0 .327-.033.62-.1.869-.067.246-.161.453-.278.614-.117.162-.26.285-.421.366-.322.162-.76.166-1.069.015-.153-.075-.286-.175-.393-.296-.085-.096-.156-.216-.218-.367 0 0-.179-.447-.179-.947 0-.5.179-1.002.179-1.002.062-.177.136-.318.224-.43.114-.143.256-.259.424-.345.168-.086.365-.129.587-.129.19 0 .364.037.517.109z"/><rect x=".987" y="2" class="st2" width="14.013" height="1.023"/><rect x=".987" y="12.968" class="st2" width="14.013" height="1.023"/><path class="st2" d="M1.991 12.031l.728-2.031h2.219l.778 2.031h1.082l-2.485-7.158h-.941l-2.441 7.086-.025.072h1.085zm1.827-5.609h.022l.914 2.753h-1.841l.905-2.753z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
1
src/vs/base/browser/ui/findinput/whole-word.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.st0{opacity:0;fill:#F6F6F6;} .st1{fill:#F6F6F6;} .st2{fill:#424242;}</style><g id="outline"><rect class="st0" width="16" height="16"/><path class="st1" d="M16 4.022v-3.022h-16.014v3.022h3.046l-3.043 7.945h-.004v.01l.015 1.023h-.014v1.991h16.014v-3.023h-1v-7.946h1zm-5.914 5.301c0 .233-.023.441-.066.595-.047.164-.099.247-.127.284l-.078.069-.151.026-.115-.017-.139-.137c-.031-.078-.112-.332-.112-.566 0-.254.091-.561.126-.656l.069-.141.109-.082.178-.027c.077 0 .117.014.177.056l.087.179.051.237-.009.18zm-3.695-5.301v2.893l-1.116-2.893h1.116zm-3.026 7.02h1.573l.351.926h-2.254l.33-.926zm8.635-4.354c-.206-.2-.431-.38-.695-.512-.396-.198-.853-.298-1.355-.298-.215 0-.423.02-.621.058v-1.914h2.671v2.666z"/></g><g id="icon_x5F_bg"><rect x="13" y="4" class="st2" width="1" height="8"/><path class="st2" d="M11.225 8.387c-.078-.299-.199-.562-.36-.786s-.365-.401-.609-.53-.534-.193-.866-.193c-.198 0-.38.024-.547.073-.165.049-.316.117-.453.205-.136.088-.257.194-.365.318l-.179.258v-3.154h-.893v7.422h.893v-.575l.126.175c.087.102.189.19.304.269.117.078.249.14.398.186.149.046.314.068.498.068.353 0 .666-.071.937-.212.272-.143.499-.338.682-.586.183-.25.321-.543.414-.879.093-.338.14-.703.14-1.097-.001-.342-.04-.663-.12-.962zm-1.479-.607c.151.071.282.176.39.314.109.14.194.313.255.517.051.174.082.371.089.587l-.007.125c0 .327-.033.62-.1.869-.067.246-.161.453-.278.614-.117.162-.26.285-.421.366-.322.162-.76.166-1.069.015-.153-.075-.286-.175-.393-.296-.085-.096-.156-.216-.218-.367 0 0-.179-.447-.179-.947 0-.5.179-1.002.179-1.002.062-.177.136-.318.224-.43.114-.143.256-.259.424-.345.168-.086.365-.129.587-.129.19 0 .364.037.517.109z"/><rect x=".987" y="2" class="st2" width="14.013" height="1.023"/><rect x=".987" y="12.968" class="st2" width="14.013" height="1.023"/><path class="st2" d="M1.991 12.031l.728-2.031h2.219l.778 2.031h1.082l-2.485-7.158h-.941l-2.441 7.086-.025.072h1.085zm1.827-5.609h.022l.914 2.753h-1.841l.905-2.753z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
91
src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { 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';
|
||||
|
||||
export interface IHighlight {
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
export class HighlightedLabel implements IDisposable {
|
||||
|
||||
private domNode: HTMLElement;
|
||||
private text: string;
|
||||
private highlights: IHighlight[];
|
||||
private didEverRender: boolean;
|
||||
|
||||
constructor(container: HTMLElement) {
|
||||
this.domNode = document.createElement('span');
|
||||
this.domNode.className = 'monaco-highlighted-label';
|
||||
this.didEverRender = false;
|
||||
container.appendChild(this.domNode);
|
||||
}
|
||||
|
||||
get element(): HTMLElement {
|
||||
return this.domNode;
|
||||
}
|
||||
|
||||
set(text: string, highlights: IHighlight[] = []) {
|
||||
if (!text) {
|
||||
text = '';
|
||||
}
|
||||
if (this.didEverRender && this.text === text && objects.equals(this.highlights, highlights)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(highlights)) {
|
||||
highlights = [];
|
||||
}
|
||||
|
||||
this.text = text;
|
||||
this.highlights = highlights;
|
||||
this.render();
|
||||
}
|
||||
|
||||
private render() {
|
||||
dom.clearNode(this.domNode);
|
||||
|
||||
let htmlContent: string[] = [],
|
||||
highlight: IHighlight,
|
||||
pos = 0;
|
||||
|
||||
for (let i = 0; i < this.highlights.length; i++) {
|
||||
highlight = this.highlights[i];
|
||||
if (highlight.end === highlight.start) {
|
||||
continue;
|
||||
}
|
||||
if (pos < highlight.start) {
|
||||
htmlContent.push('<span>');
|
||||
htmlContent.push(expandOcticons(escape(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('</span>');
|
||||
pos = highlight.end;
|
||||
}
|
||||
|
||||
if (pos < this.text.length) {
|
||||
htmlContent.push('<span>');
|
||||
htmlContent.push(expandOcticons(escape(this.text.substring(pos))));
|
||||
htmlContent.push('</span>');
|
||||
}
|
||||
|
||||
this.domNode.innerHTML = htmlContent.join('');
|
||||
this.didEverRender = true;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.text = null;
|
||||
this.highlights = null;
|
||||
}
|
||||
}
|
||||
112
src/vs/base/browser/ui/iconLabel/iconLabel.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./iconlabel';
|
||||
import dom = require('vs/base/browser/dom');
|
||||
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
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';
|
||||
|
||||
export interface IIconLabelCreationOptions {
|
||||
supportHighlights?: boolean;
|
||||
}
|
||||
|
||||
export interface IIconLabelOptions {
|
||||
title?: string;
|
||||
extraClasses?: string[];
|
||||
italic?: boolean;
|
||||
matches?: IMatch[];
|
||||
}
|
||||
|
||||
export class IconLabel {
|
||||
private domNode: HTMLElement;
|
||||
private labelNode: HTMLElement | HighlightedLabel;
|
||||
private descriptionNode: HTMLElement;
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
public get element(): HTMLElement {
|
||||
return this.domNode;
|
||||
}
|
||||
|
||||
public get labelElement(): HTMLElement {
|
||||
const labelNode = this.labelNode;
|
||||
if (labelNode instanceof HighlightedLabel) {
|
||||
return labelNode.element;
|
||||
} else {
|
||||
return labelNode;
|
||||
}
|
||||
}
|
||||
|
||||
public get descriptionElement(): HTMLElement {
|
||||
return this.descriptionNode;
|
||||
}
|
||||
|
||||
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) {
|
||||
classes.push(...options.extraClasses);
|
||||
}
|
||||
|
||||
if (options.italic) {
|
||||
classes.push('italic');
|
||||
}
|
||||
}
|
||||
|
||||
this.domNode.className = classes.join(' ');
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
const labelNode = this.labelNode;
|
||||
if (labelNode instanceof HighlightedLabel) {
|
||||
labelNode.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class FileLabel extends IconLabel {
|
||||
|
||||
constructor(container: HTMLElement, file: uri, provider: IRootProvider, userHome?: IUserHomeProvider) {
|
||||
super(container);
|
||||
|
||||
this.setFile(file, provider, userHome);
|
||||
}
|
||||
|
||||
public setFile(file: uri, provider: IRootProvider, userHome: IUserHomeProvider): void {
|
||||
const parent = paths.dirname(file.fsPath);
|
||||
|
||||
this.setValue(paths.basename(file.fsPath), parent && parent !== '.' ? getPathLabel(parent, provider, userHome) : '', { title: file.fsPath });
|
||||
}
|
||||
}
|
||||
49
src/vs/base/browser/ui/iconLabel/iconlabel.css
Normal file
@@ -0,0 +1,49 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* ---------- Icon label ---------- */
|
||||
|
||||
.monaco-icon-label {
|
||||
display: inline-block; /* required for icons support :before rule */
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.monaco-icon-label::before {
|
||||
|
||||
/* svg icons rendered as background image */
|
||||
background-size: 16px;
|
||||
background-position: left center;
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 6px;
|
||||
width: 16px;
|
||||
height: 22px;
|
||||
display: inline-block;
|
||||
|
||||
/* fonts icons */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.monaco-icon-label > .label-name {
|
||||
color: inherit;
|
||||
white-space: pre; /* enable to show labels that include multiple whitespaces */
|
||||
}
|
||||
|
||||
.monaco-icon-label > .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 > .label-name,
|
||||
.monaco-icon-label.italic > .label-description {
|
||||
font-style: italic;
|
||||
}
|
||||
119
src/vs/base/browser/ui/inputbox/inputBox.css
Normal file
@@ -0,0 +1,119 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-inputbox {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-o-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
line-height: auto !important;
|
||||
|
||||
/* Customizable */
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.monaco-inputbox.idle {
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.monaco-inputbox > .wrapper > .input,
|
||||
.monaco-inputbox > .wrapper > .mirror {
|
||||
|
||||
/* Customizable */
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.monaco-inputbox > .wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-inputbox > .wrapper > .input {
|
||||
display: inline-block;
|
||||
-webkit-box-sizing: border-box;
|
||||
-o-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
line-height: inherit;
|
||||
border: none;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
resize: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.monaco-inputbox > .wrapper > input {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.monaco-inputbox > .wrapper > textarea.input {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monaco-inputbox > .wrapper > .mirror {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-o-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
white-space: pre-wrap;
|
||||
visibility: hidden;
|
||||
min-height: 26px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Context view */
|
||||
|
||||
.monaco-inputbox-container {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.monaco-inputbox-container .monaco-inputbox-message {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
-o-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 0.4em;
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
min-height: 34px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
/* Action bar support */
|
||||
.monaco-inputbox .monaco-action-bar {
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.monaco-inputbox .monaco-action-bar .action-item {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.monaco-inputbox .monaco-action-bar .action-item .icon {
|
||||
background-repeat: no-repeat;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
512
src/vs/base/browser/ui/inputbox/inputBox.ts
Normal file
@@ -0,0 +1,512 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./inputBox';
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import * as Bal from 'vs/base/browser/browser';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { RenderOptions, renderFormattedText, renderText } from 'vs/base/browser/htmlContentRenderer';
|
||||
import aria = require('vs/base/browser/ui/aria/aria');
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IContextViewProvider, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
export interface IInputOptions extends IInputBoxStyles {
|
||||
placeholder?: string;
|
||||
ariaLabel?: string;
|
||||
type?: string;
|
||||
validationOptions?: IInputValidationOptions;
|
||||
flexibleHeight?: boolean;
|
||||
actions?: IAction[];
|
||||
|
||||
// {{SQL CARBON EDIT}} Canidate for addition to vscode
|
||||
min?: string;
|
||||
}
|
||||
|
||||
export interface IInputBoxStyles {
|
||||
inputBackground?: Color;
|
||||
inputForeground?: Color;
|
||||
inputBorder?: Color;
|
||||
inputValidationInfoBorder?: Color;
|
||||
inputValidationInfoBackground?: Color;
|
||||
inputValidationWarningBorder?: Color;
|
||||
inputValidationWarningBackground?: Color;
|
||||
inputValidationErrorBorder?: Color;
|
||||
inputValidationErrorBackground?: Color;
|
||||
}
|
||||
|
||||
export interface IInputValidator {
|
||||
(value: string): IMessage;
|
||||
}
|
||||
|
||||
export interface IMessage {
|
||||
content: string;
|
||||
formatContent?: boolean; // defaults to false
|
||||
type?: MessageType;
|
||||
}
|
||||
|
||||
export interface IInputValidationOptions {
|
||||
validation: IInputValidator;
|
||||
showMessage?: boolean;
|
||||
}
|
||||
|
||||
export enum MessageType {
|
||||
INFO = 1,
|
||||
WARNING = 2,
|
||||
ERROR = 3
|
||||
}
|
||||
|
||||
export interface IRange {
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
export const defaultOpts = {
|
||||
inputBackground: Color.fromHex('#3C3C3C'),
|
||||
inputForeground: Color.fromHex('#CCCCCC'),
|
||||
inputValidationInfoBorder: Color.fromHex('#55AAFF'),
|
||||
inputValidationInfoBackground: Color.fromHex('#063B49'),
|
||||
inputValidationWarningBorder: Color.fromHex('#B89500'),
|
||||
inputValidationWarningBackground: Color.fromHex('#352A05'),
|
||||
inputValidationErrorBorder: Color.fromHex('#BE1100'),
|
||||
inputValidationErrorBackground: Color.fromHex('#5A1D1D')
|
||||
};
|
||||
|
||||
export class InputBox extends Widget {
|
||||
private contextViewProvider: IContextViewProvider;
|
||||
private element: HTMLElement;
|
||||
private input: HTMLInputElement;
|
||||
private mirror: HTMLElement;
|
||||
private actionbar: ActionBar;
|
||||
private options: IInputOptions;
|
||||
private message: IMessage;
|
||||
private placeholder: string;
|
||||
private ariaLabel: string;
|
||||
private validation: IInputValidator;
|
||||
private showValidationMessage: boolean;
|
||||
private state = 'idle';
|
||||
private cachedHeight: number;
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
protected inputBackground: Color;
|
||||
protected inputForeground: Color;
|
||||
protected inputBorder: Color;
|
||||
|
||||
private inputValidationInfoBorder: Color;
|
||||
private inputValidationInfoBackground: Color;
|
||||
private inputValidationWarningBorder: Color;
|
||||
private inputValidationWarningBackground: Color;
|
||||
private inputValidationErrorBorder: Color;
|
||||
private inputValidationErrorBackground: Color;
|
||||
|
||||
private _onDidChange = this._register(new Emitter<string>());
|
||||
public onDidChange: Event<string> = this._onDidChange.event;
|
||||
|
||||
private _onDidHeightChange = this._register(new Emitter<number>());
|
||||
public onDidHeightChange: Event<number> = this._onDidHeightChange.event;
|
||||
|
||||
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, options?: IInputOptions) {
|
||||
super();
|
||||
|
||||
this.contextViewProvider = contextViewProvider;
|
||||
this.options = options || Object.create(null);
|
||||
mixin(this.options, defaultOpts, false);
|
||||
this.message = null;
|
||||
this.cachedHeight = null;
|
||||
this.placeholder = this.options.placeholder || '';
|
||||
this.ariaLabel = this.options.ariaLabel || '';
|
||||
|
||||
this.inputBackground = this.options.inputBackground;
|
||||
this.inputForeground = this.options.inputForeground;
|
||||
this.inputBorder = this.options.inputBorder;
|
||||
|
||||
this.inputValidationInfoBorder = this.options.inputValidationInfoBorder;
|
||||
this.inputValidationInfoBackground = this.options.inputValidationInfoBackground;
|
||||
this.inputValidationWarningBorder = this.options.inputValidationWarningBorder;
|
||||
this.inputValidationWarningBackground = this.options.inputValidationWarningBackground;
|
||||
this.inputValidationErrorBorder = this.options.inputValidationErrorBorder;
|
||||
this.inputValidationErrorBackground = this.options.inputValidationErrorBackground;
|
||||
|
||||
if (this.options.validationOptions) {
|
||||
this.validation = this.options.validationOptions.validation;
|
||||
this.showValidationMessage = this.options.validationOptions.showMessage || false;
|
||||
}
|
||||
|
||||
this.element = dom.append(container, $('.monaco-inputbox.idle'));
|
||||
|
||||
let tagName = this.options.flexibleHeight ? 'textarea' : 'input';
|
||||
|
||||
let wrapper = dom.append(this.element, $('.wrapper'));
|
||||
this.input = <HTMLInputElement>dom.append(wrapper, $(tagName + '.input'));
|
||||
this.input.setAttribute('autocorrect', 'off');
|
||||
this.input.setAttribute('autocapitalize', 'off');
|
||||
this.input.setAttribute('spellcheck', 'false');
|
||||
|
||||
this.onfocus(this.input, () => dom.addClass(this.element, 'synthetic-focus'));
|
||||
this.onblur(this.input, () => dom.removeClass(this.element, 'synthetic-focus'));
|
||||
|
||||
if (this.options.flexibleHeight) {
|
||||
this.mirror = dom.append(wrapper, $('div.mirror'));
|
||||
} else {
|
||||
this.input.type = this.options.type || 'text';
|
||||
this.input.setAttribute('wrap', 'off');
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}} Canidate for addition to vscode
|
||||
if (this.options.min) {
|
||||
this.input.min = this.options.min;
|
||||
}
|
||||
|
||||
if (this.ariaLabel) {
|
||||
this.input.setAttribute('aria-label', this.ariaLabel);
|
||||
}
|
||||
|
||||
if (this.placeholder) {
|
||||
this.input.setAttribute('placeholder', this.placeholder);
|
||||
this.input.title = this.placeholder;
|
||||
}
|
||||
|
||||
this.oninput(this.input, () => this.onValueChange());
|
||||
this.onblur(this.input, () => this.onBlur());
|
||||
this.onfocus(this.input, () => this.onFocus());
|
||||
|
||||
// Add placeholder shim for IE because IE decides to hide the placeholder on focus (we dont want that!)
|
||||
if (this.placeholder && Bal.isIE) {
|
||||
this.onclick(this.input, (e) => {
|
||||
dom.EventHelper.stop(e, true);
|
||||
this.input.focus();
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(() => this.updateMirror(), 0);
|
||||
|
||||
// Support actions
|
||||
if (this.options.actions) {
|
||||
this.actionbar = this._register(new ActionBar(this.element));
|
||||
this.actionbar.push(this.options.actions, { icon: true, label: false });
|
||||
// {{SQL CARBON EDIT}} Canidate for addition to vscode
|
||||
this.input.style.paddingRight = (this.options.actions.length * 22) + 'px';
|
||||
}
|
||||
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
private onBlur(): void {
|
||||
this._hideMessage();
|
||||
}
|
||||
|
||||
private onFocus(): void {
|
||||
this._showMessage();
|
||||
}
|
||||
|
||||
public setPlaceHolder(placeHolder: string): void {
|
||||
if (this.input) {
|
||||
this.input.setAttribute('placeholder', placeHolder);
|
||||
}
|
||||
}
|
||||
|
||||
public setAriaLabel(label: string): void {
|
||||
this.ariaLabel = label;
|
||||
|
||||
if (this.input) {
|
||||
if (label) {
|
||||
this.input.setAttribute('aria-label', this.ariaLabel);
|
||||
} else {
|
||||
this.input.removeAttribute('aria-label');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public setContextViewProvider(contextViewProvider: IContextViewProvider): void {
|
||||
this.contextViewProvider = contextViewProvider;
|
||||
}
|
||||
|
||||
public get inputElement(): HTMLInputElement {
|
||||
return this.input;
|
||||
}
|
||||
|
||||
public get value(): string {
|
||||
return this.input.value;
|
||||
}
|
||||
|
||||
public set value(newValue: string) {
|
||||
if (this.input.value !== newValue) {
|
||||
this.input.value = newValue;
|
||||
this.onValueChange();
|
||||
}
|
||||
}
|
||||
|
||||
public get height(): number {
|
||||
return this.cachedHeight === null ? dom.getTotalHeight(this.element) : this.cachedHeight;
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
this.input.focus();
|
||||
}
|
||||
|
||||
public blur(): void {
|
||||
this.input.blur();
|
||||
}
|
||||
|
||||
public hasFocus(): boolean {
|
||||
return document.activeElement === this.input;
|
||||
}
|
||||
|
||||
public select(range: IRange = null): void {
|
||||
this.input.select();
|
||||
|
||||
if (range) {
|
||||
this.input.setSelectionRange(range.start, range.end);
|
||||
}
|
||||
}
|
||||
|
||||
public enable(): void {
|
||||
this.input.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
public disable(): void {
|
||||
this.input.disabled = true;
|
||||
this._hideMessage();
|
||||
}
|
||||
|
||||
public setEnabled(enabled: boolean): void {
|
||||
if (enabled) {
|
||||
this.enable();
|
||||
} else {
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
|
||||
public get width(): number {
|
||||
return dom.getTotalWidth(this.input);
|
||||
}
|
||||
|
||||
public set width(width: number) {
|
||||
this.input.style.width = width + 'px';
|
||||
}
|
||||
|
||||
public showMessage(message: IMessage, force?: boolean): void {
|
||||
this.message = message;
|
||||
|
||||
dom.removeClass(this.element, 'idle');
|
||||
dom.removeClass(this.element, 'info');
|
||||
dom.removeClass(this.element, 'warning');
|
||||
dom.removeClass(this.element, 'error');
|
||||
dom.addClass(this.element, this.classForType(message.type));
|
||||
|
||||
const styles = this.stylesForType(this.message.type);
|
||||
this.element.style.border = styles.border ? `1px solid ${styles.border}` : null;
|
||||
|
||||
// ARIA Support
|
||||
let alertText: string;
|
||||
if (message.type === MessageType.ERROR) {
|
||||
alertText = nls.localize('alertErrorMessage', "Error: {0}", message.content);
|
||||
} else if (message.type === MessageType.WARNING) {
|
||||
alertText = nls.localize('alertWarningMessage', "Warning: {0}", message.content);
|
||||
} else {
|
||||
alertText = nls.localize('alertInfoMessage', "Info: {0}", message.content);
|
||||
}
|
||||
|
||||
aria.alert(alertText);
|
||||
|
||||
if (this.hasFocus() || force) {
|
||||
this._showMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public hideMessage(): void {
|
||||
this.message = null;
|
||||
|
||||
dom.removeClass(this.element, 'info');
|
||||
dom.removeClass(this.element, 'warning');
|
||||
dom.removeClass(this.element, 'error');
|
||||
dom.addClass(this.element, 'idle');
|
||||
|
||||
this._hideMessage();
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
public isInputValid(): boolean {
|
||||
return !!this.validation && !this.validation(this.value);
|
||||
}
|
||||
|
||||
public validate(): boolean {
|
||||
let result: IMessage = null;
|
||||
|
||||
if (this.validation) {
|
||||
result = this.validation(this.value);
|
||||
|
||||
if (!result) {
|
||||
this.inputElement.removeAttribute('aria-invalid');
|
||||
this.hideMessage();
|
||||
} else {
|
||||
this.inputElement.setAttribute('aria-invalid', 'true');
|
||||
this.showMessage(result);
|
||||
}
|
||||
}
|
||||
|
||||
return !result;
|
||||
}
|
||||
|
||||
private stylesForType(type: MessageType): { border: Color; background: Color } {
|
||||
switch (type) {
|
||||
case MessageType.INFO: return { border: this.inputValidationInfoBorder, background: this.inputValidationInfoBackground };
|
||||
case MessageType.WARNING: return { border: this.inputValidationWarningBorder, background: this.inputValidationWarningBackground };
|
||||
default: return { border: this.inputValidationErrorBorder, background: this.inputValidationErrorBackground };
|
||||
}
|
||||
}
|
||||
|
||||
private classForType(type: MessageType): string {
|
||||
switch (type) {
|
||||
case MessageType.INFO: return 'info';
|
||||
case MessageType.WARNING: return 'warning';
|
||||
default: return 'error';
|
||||
}
|
||||
}
|
||||
|
||||
private _showMessage(): void {
|
||||
if (!this.contextViewProvider || !this.message) {
|
||||
return;
|
||||
}
|
||||
|
||||
let div: HTMLElement;
|
||||
let layout = () => div.style.width = dom.getTotalWidth(this.element) + 'px';
|
||||
|
||||
this.state = 'open';
|
||||
|
||||
this.contextViewProvider.showContextView({
|
||||
getAnchor: () => this.element,
|
||||
anchorAlignment: AnchorAlignment.RIGHT,
|
||||
render: (container: HTMLElement) => {
|
||||
div = dom.append(container, $('.monaco-inputbox-container'));
|
||||
layout();
|
||||
|
||||
const renderOptions: RenderOptions = {
|
||||
inline: true,
|
||||
className: 'monaco-inputbox-message'
|
||||
};
|
||||
|
||||
let spanElement: HTMLElement = (this.message.formatContent
|
||||
? renderFormattedText(this.message.content, renderOptions)
|
||||
: renderText(this.message.content, renderOptions)) as any;
|
||||
dom.addClass(spanElement, this.classForType(this.message.type));
|
||||
|
||||
const styles = this.stylesForType(this.message.type);
|
||||
spanElement.style.backgroundColor = styles.background ? styles.background.toString() : null;
|
||||
spanElement.style.border = styles.border ? `1px solid ${styles.border}` : null;
|
||||
|
||||
dom.append(div, spanElement);
|
||||
|
||||
return null;
|
||||
},
|
||||
layout: layout
|
||||
});
|
||||
}
|
||||
|
||||
private _hideMessage(): void {
|
||||
if (!this.contextViewProvider || this.state !== 'open') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state = 'idle';
|
||||
|
||||
this.contextViewProvider.hideContextView();
|
||||
}
|
||||
|
||||
private onValueChange(): void {
|
||||
this._onDidChange.fire(this.value);
|
||||
|
||||
this.validate();
|
||||
this.updateMirror();
|
||||
|
||||
if (this.state === 'open') {
|
||||
this.contextViewProvider.layout();
|
||||
}
|
||||
}
|
||||
|
||||
private updateMirror(): void {
|
||||
if (!this.mirror) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = this.value || this.placeholder;
|
||||
let lastCharCode = value.charCodeAt(value.length - 1);
|
||||
let suffix = lastCharCode === 10 ? ' ' : '';
|
||||
this.mirror.textContent = value + suffix;
|
||||
this.layout();
|
||||
}
|
||||
|
||||
public style(styles: IInputBoxStyles): void {
|
||||
this.inputBackground = styles.inputBackground;
|
||||
this.inputForeground = styles.inputForeground;
|
||||
this.inputBorder = styles.inputBorder;
|
||||
|
||||
this.inputValidationInfoBackground = styles.inputValidationInfoBackground;
|
||||
this.inputValidationInfoBorder = styles.inputValidationInfoBorder;
|
||||
this.inputValidationWarningBackground = styles.inputValidationWarningBackground;
|
||||
this.inputValidationWarningBorder = styles.inputValidationWarningBorder;
|
||||
this.inputValidationErrorBackground = styles.inputValidationErrorBackground;
|
||||
this.inputValidationErrorBorder = styles.inputValidationErrorBorder;
|
||||
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
protected applyStyles(): void {
|
||||
if (this.element) {
|
||||
const background = this.inputBackground ? this.inputBackground.toString() : null;
|
||||
const foreground = this.inputForeground ? this.inputForeground.toString() : null;
|
||||
const border = this.inputBorder ? this.inputBorder.toString() : null;
|
||||
|
||||
this.element.style.backgroundColor = background;
|
||||
this.element.style.color = foreground;
|
||||
this.input.style.backgroundColor = background;
|
||||
this.input.style.color = foreground;
|
||||
|
||||
this.element.style.borderWidth = border ? '1px' : null;
|
||||
this.element.style.borderStyle = border ? 'solid' : null;
|
||||
this.element.style.borderColor = border;
|
||||
}
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
if (!this.mirror) {
|
||||
return;
|
||||
}
|
||||
|
||||
const previousHeight = this.cachedHeight;
|
||||
this.cachedHeight = dom.getTotalHeight(this.mirror);
|
||||
|
||||
if (previousHeight !== this.cachedHeight) {
|
||||
this.input.style.height = this.cachedHeight + 'px';
|
||||
this._onDidHeightChange.fire(this.cachedHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._hideMessage();
|
||||
|
||||
this.element = null;
|
||||
this.input = null;
|
||||
this.contextViewProvider = null;
|
||||
this.message = null;
|
||||
this.placeholder = null;
|
||||
this.ariaLabel = null;
|
||||
this.validation = null;
|
||||
this.showValidationMessage = null;
|
||||
this.state = null;
|
||||
this.actionbar = null;
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
40
src/vs/base/browser/ui/keybindingLabel/keybindingLabel.css
Normal file
@@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-keybinding {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 10px;
|
||||
}
|
||||
|
||||
.monaco-keybinding > .monaco-keybinding-key {
|
||||
display: inline-block;
|
||||
border: solid 1px rgba(204, 204, 204, 0.4);
|
||||
border-bottom-color: rgba(187, 187, 187, 0.4);
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 -1px 0 rgba(187, 187, 187, 0.4);
|
||||
background-color: rgba(221, 221, 221, 0.4);
|
||||
vertical-align: middle;
|
||||
color: #555;
|
||||
font-size: 11px;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
.hc-black .monaco-keybinding > .monaco-keybinding-key,
|
||||
.vs-dark .monaco-keybinding > .monaco-keybinding-key {
|
||||
background-color: rgba(128, 128, 128, 0.17);
|
||||
color: #ccc;
|
||||
border: solid 1px rgba(51, 51, 51, 0.6);
|
||||
border-bottom-color: rgba(68, 68, 68, 0.6);
|
||||
box-shadow: inset 0 -1px 0 rgba(68, 68, 68, 0.6);
|
||||
}
|
||||
|
||||
.monaco-keybinding > .monaco-keybinding-key-separator {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.monaco-keybinding > .monaco-keybinding-key-chord-separator {
|
||||
width: 2px;
|
||||
}
|
||||
112
src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./keybindingLabel';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { equals } from 'vs/base/common/objects';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { ResolvedKeybinding, ResolvedKeybindingPart } from 'vs/base/common/keyCodes';
|
||||
import { UILabelProvider } from 'vs/base/common/keybindingLabels';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
export interface PartMatches {
|
||||
ctrlKey?: boolean;
|
||||
shiftKey?: boolean;
|
||||
altKey?: boolean;
|
||||
metaKey?: boolean;
|
||||
keyCode?: boolean;
|
||||
}
|
||||
|
||||
export interface Matches {
|
||||
firstPart: PartMatches;
|
||||
chordPart: PartMatches;
|
||||
}
|
||||
|
||||
export class KeybindingLabel implements IDisposable {
|
||||
|
||||
private domNode: HTMLElement;
|
||||
private keybinding: ResolvedKeybinding;
|
||||
private matches: Matches;
|
||||
private didEverRender: boolean;
|
||||
|
||||
constructor(container: HTMLElement, private os: OperatingSystem) {
|
||||
this.domNode = dom.append(container, $('.monaco-keybinding'));
|
||||
this.didEverRender = false;
|
||||
container.appendChild(this.domNode);
|
||||
}
|
||||
|
||||
get element(): HTMLElement {
|
||||
return this.domNode;
|
||||
}
|
||||
|
||||
set(keybinding: ResolvedKeybinding, matches: Matches) {
|
||||
if (this.didEverRender && this.keybinding === keybinding && KeybindingLabel.areSame(this.matches, matches)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.keybinding = keybinding;
|
||||
this.matches = matches;
|
||||
this.render();
|
||||
}
|
||||
|
||||
private render() {
|
||||
dom.clearNode(this.domNode);
|
||||
|
||||
if (this.keybinding) {
|
||||
let [firstPart, chordPart] = this.keybinding.getParts();
|
||||
if (firstPart) {
|
||||
this.renderPart(this.domNode, firstPart, this.matches ? this.matches.firstPart : null);
|
||||
}
|
||||
if (chordPart) {
|
||||
dom.append(this.domNode, $('span.monaco-keybinding-key-chord-separator', null, ' '));
|
||||
this.renderPart(this.domNode, chordPart, this.matches ? this.matches.chordPart : null);
|
||||
}
|
||||
this.domNode.title = this.keybinding.getAriaLabel();
|
||||
}
|
||||
|
||||
this.didEverRender = true;
|
||||
}
|
||||
|
||||
private renderPart(parent: HTMLElement, part: ResolvedKeybindingPart, match: PartMatches) {
|
||||
const modifierLabels = UILabelProvider.modifierLabels[this.os];
|
||||
if (part.ctrlKey) {
|
||||
this.renderKey(parent, modifierLabels.ctrlKey, match && match.ctrlKey, modifierLabels.separator);
|
||||
}
|
||||
if (part.shiftKey) {
|
||||
this.renderKey(parent, modifierLabels.shiftKey, match && match.shiftKey, modifierLabels.separator);
|
||||
}
|
||||
if (part.altKey) {
|
||||
this.renderKey(parent, modifierLabels.altKey, match && match.altKey, modifierLabels.separator);
|
||||
}
|
||||
if (part.metaKey) {
|
||||
this.renderKey(parent, modifierLabels.metaKey, match && match.metaKey, modifierLabels.separator);
|
||||
}
|
||||
const keyLabel = part.keyLabel;
|
||||
if (keyLabel) {
|
||||
this.renderKey(parent, keyLabel, match && match.keyCode, '');
|
||||
}
|
||||
}
|
||||
|
||||
private renderKey(parent: HTMLElement, label: string, highlight: boolean, separator: string): void {
|
||||
dom.append(parent, $('span.monaco-keybinding-key' + (highlight ? '.highlight' : ''), null, label));
|
||||
if (separator) {
|
||||
dom.append(parent, $('span.monaco-keybinding-key-separator', null, separator));
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.keybinding = null;
|
||||
}
|
||||
|
||||
private static areSame(a: Matches, b: Matches): boolean {
|
||||
if (a === b || (!a && !b)) {
|
||||
return true;
|
||||
}
|
||||
return !!a && !!b && equals(a.firstPart, b.firstPart) && equals(a.chordPart, b.chordPart);
|
||||
}
|
||||
}
|
||||
46
src/vs/base/browser/ui/list/list.css
Normal file
@@ -0,0 +1,46 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-list {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: -moz-none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.monaco-list > .monaco-scrollable-element {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-list-rows {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-list-row {
|
||||
position: absolute;
|
||||
-moz-box-sizing: border-box;
|
||||
-o-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
/* for OS X ballistic scrolling */
|
||||
.monaco-list-row.scrolling {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Focus */
|
||||
.monaco-list.element-focused { outline: 0 !important; }
|
||||
38
src/vs/base/browser/ui/list/list.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export interface IDelegate<T> {
|
||||
getHeight(element: T): number;
|
||||
getTemplateId(element: T): string;
|
||||
}
|
||||
|
||||
export interface IRenderer<TElement, TTemplateData> {
|
||||
templateId: string;
|
||||
renderTemplate(container: HTMLElement): TTemplateData;
|
||||
renderElement(element: TElement, index: number, templateData: TTemplateData): void;
|
||||
disposeTemplate(templateData: TTemplateData): void;
|
||||
}
|
||||
|
||||
export interface IListElementEvent<T, E> {
|
||||
element: T;
|
||||
index: number;
|
||||
event: E;
|
||||
}
|
||||
|
||||
export interface IListEvent<T> {
|
||||
elements: T[];
|
||||
indexes: number[];
|
||||
}
|
||||
|
||||
export interface IListMouseEvent<T> extends MouseEvent {
|
||||
element: T;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export interface IListContextMenuEvent<T> {
|
||||
element: T;
|
||||
index: number;
|
||||
anchor: HTMLElement | { x: number; y: number; };
|
||||
}
|
||||
152
src/vs/base/browser/ui/list/listPaging.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./list';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { range } from 'vs/base/common/arrays';
|
||||
import { IDelegate, IRenderer, IListEvent } from './list';
|
||||
import { List, IListOptions } from './listWidget';
|
||||
import { IPagedModel } from 'vs/base/common/paging';
|
||||
import Event, { mapEvent } from 'vs/base/common/event';
|
||||
|
||||
export interface IPagedRenderer<TElement, TTemplateData> extends IRenderer<TElement, TTemplateData> {
|
||||
renderPlaceholder(index: number, templateData: TTemplateData): void;
|
||||
}
|
||||
|
||||
export interface ITemplateData<T> {
|
||||
data: T;
|
||||
disposable: IDisposable;
|
||||
}
|
||||
|
||||
class PagedRenderer<TElement, TTemplateData> implements IRenderer<number, ITemplateData<TTemplateData>> {
|
||||
|
||||
get templateId(): string { return this.renderer.templateId; }
|
||||
|
||||
constructor(
|
||||
private renderer: IPagedRenderer<TElement, TTemplateData>,
|
||||
private modelProvider: () => IPagedModel<TElement>
|
||||
) { }
|
||||
|
||||
renderTemplate(container: HTMLElement): ITemplateData<TTemplateData> {
|
||||
const data = this.renderer.renderTemplate(container);
|
||||
return { data, disposable: { dispose: () => { } } };
|
||||
}
|
||||
|
||||
renderElement(index: number, _: number, data: ITemplateData<TTemplateData>): void {
|
||||
data.disposable.dispose();
|
||||
|
||||
const model = this.modelProvider();
|
||||
|
||||
if (model.isResolved(index)) {
|
||||
return this.renderer.renderElement(model.get(index), index, data.data);
|
||||
}
|
||||
|
||||
const promise = model.resolve(index);
|
||||
data.disposable = { dispose: () => promise.cancel() };
|
||||
|
||||
this.renderer.renderPlaceholder(index, data.data);
|
||||
promise.done(entry => this.renderer.renderElement(entry, index, data.data));
|
||||
}
|
||||
|
||||
disposeTemplate(data: ITemplateData<TTemplateData>): void {
|
||||
data.disposable.dispose();
|
||||
data.disposable = null;
|
||||
this.renderer.disposeTemplate(data.data);
|
||||
data.data = null;
|
||||
}
|
||||
}
|
||||
|
||||
export class PagedList<T> {
|
||||
|
||||
private list: List<number>;
|
||||
private _model: IPagedModel<T>;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
delegate: IDelegate<number>,
|
||||
renderers: IPagedRenderer<T, any>[],
|
||||
options: IListOptions<any> = {} // TODO@Joao: should be IListOptions<T>
|
||||
) {
|
||||
const pagedRenderers = renderers.map(r => new PagedRenderer<T, ITemplateData<T>>(r, () => this.model));
|
||||
this.list = new List(container, delegate, pagedRenderers, options);
|
||||
}
|
||||
|
||||
get widget(): List<number> {
|
||||
return this.list;
|
||||
}
|
||||
|
||||
get onFocusChange(): Event<IListEvent<T>> {
|
||||
return mapEvent(this.list.onFocusChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
|
||||
}
|
||||
|
||||
get onSelectionChange(): Event<IListEvent<T>> {
|
||||
return mapEvent(this.list.onSelectionChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
|
||||
}
|
||||
|
||||
get onPin(): Event<IListEvent<T>> {
|
||||
return mapEvent(this.list.onPin, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
|
||||
}
|
||||
|
||||
get model(): IPagedModel<T> {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
set model(model: IPagedModel<T>) {
|
||||
this._model = model;
|
||||
this.list.splice(0, this.list.length, range(model.length));
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.list.length;
|
||||
}
|
||||
|
||||
get scrollTop(): number {
|
||||
return this.list.scrollTop;
|
||||
}
|
||||
|
||||
set scrollTop(scrollTop: number) {
|
||||
this.list.scrollTop = scrollTop;
|
||||
}
|
||||
|
||||
focusNext(n?: number, loop?: boolean): void {
|
||||
this.list.focusNext(n, loop);
|
||||
}
|
||||
|
||||
focusPrevious(n?: number, loop?: boolean): void {
|
||||
this.list.focusPrevious(n, loop);
|
||||
}
|
||||
|
||||
selectNext(n?: number, loop?: boolean): void {
|
||||
this.list.selectNext(n, loop);
|
||||
}
|
||||
|
||||
selectPrevious(n?: number, loop?: boolean): void {
|
||||
this.list.selectPrevious(n, loop);
|
||||
}
|
||||
|
||||
focusNextPage(): void {
|
||||
this.list.focusNextPage();
|
||||
}
|
||||
|
||||
focusPreviousPage(): void {
|
||||
this.list.focusPreviousPage();
|
||||
}
|
||||
|
||||
getFocus(): number[] {
|
||||
return this.list.getFocus();
|
||||
}
|
||||
|
||||
setSelection(indexes: number[]): void {
|
||||
this.list.setSelection(indexes);
|
||||
}
|
||||
|
||||
layout(height?: number): void {
|
||||
this.list.layout(height);
|
||||
}
|
||||
|
||||
reveal(index: number, relativeTop?: number): void {
|
||||
this.list.reveal(index, relativeTop);
|
||||
}
|
||||
}
|
||||
350
src/vs/base/browser/ui/list/listView.ts
Normal file
@@ -0,0 +1,350 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { toObject, assign, getOrDefault } from 'vs/base/common/objects';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { ScrollEvent, ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { RangeMap, IRange, relativeComplement, each } from './rangeMap';
|
||||
import { IDelegate, IRenderer } from './list';
|
||||
import { RowCache, IRow } from './rowCache';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
|
||||
function canUseTranslate3d(): boolean {
|
||||
if (browser.isFirefox) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (browser.getZoomLevel() !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// see https://github.com/Microsoft/vscode/issues/24483
|
||||
if (browser.isChromev56) {
|
||||
const pixelRatio = browser.getPixelRatio();
|
||||
if (Math.floor(pixelRatio) !== pixelRatio) {
|
||||
// Not an integer
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
interface IItem<T> {
|
||||
id: string;
|
||||
element: T;
|
||||
size: number;
|
||||
templateId: string;
|
||||
row: IRow;
|
||||
}
|
||||
|
||||
const MouseEventTypes = [
|
||||
'click',
|
||||
'dblclick',
|
||||
'mouseup',
|
||||
'mousedown',
|
||||
'mouseover',
|
||||
'mousemove',
|
||||
'mouseout',
|
||||
'contextmenu',
|
||||
'touchstart'
|
||||
];
|
||||
|
||||
export interface IListViewOptions {
|
||||
useShadows?: boolean;
|
||||
}
|
||||
|
||||
const DefaultOptions: IListViewOptions = {
|
||||
useShadows: true
|
||||
};
|
||||
|
||||
export class ListView<T> implements IDisposable {
|
||||
|
||||
private items: IItem<T>[];
|
||||
private itemId: number;
|
||||
private rangeMap: RangeMap;
|
||||
private cache: RowCache<T>;
|
||||
private renderers: { [templateId: string]: IRenderer<T, any>; };
|
||||
private lastRenderTop: number;
|
||||
private lastRenderHeight: number;
|
||||
private _domNode: HTMLElement;
|
||||
private gesture: Gesture;
|
||||
private rowsContainer: HTMLElement;
|
||||
private scrollableElement: ScrollableElement;
|
||||
private disposables: IDisposable[];
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
private delegate: IDelegate<T>,
|
||||
renderers: IRenderer<T, any>[],
|
||||
options: IListViewOptions = DefaultOptions
|
||||
) {
|
||||
this.items = [];
|
||||
this.itemId = 0;
|
||||
this.rangeMap = new RangeMap();
|
||||
this.renderers = toObject<IRenderer<T, any>>(renderers, r => r.templateId);
|
||||
this.cache = new RowCache(this.renderers);
|
||||
|
||||
this.lastRenderTop = 0;
|
||||
this.lastRenderHeight = 0;
|
||||
|
||||
this._domNode = document.createElement('div');
|
||||
this._domNode.className = 'monaco-list';
|
||||
|
||||
this.rowsContainer = document.createElement('div');
|
||||
this.rowsContainer.className = 'monaco-list-rows';
|
||||
this.gesture = new Gesture(this.rowsContainer);
|
||||
|
||||
this.scrollableElement = new ScrollableElement(this.rowsContainer, {
|
||||
alwaysConsumeMouseWheel: true,
|
||||
horizontal: ScrollbarVisibility.Hidden,
|
||||
vertical: ScrollbarVisibility.Auto,
|
||||
useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows)
|
||||
});
|
||||
|
||||
this._domNode.appendChild(this.scrollableElement.getDomNode());
|
||||
container.appendChild(this._domNode);
|
||||
|
||||
this.disposables = [this.rangeMap, this.gesture, this.scrollableElement];
|
||||
|
||||
this.scrollableElement.onScroll(this.onScroll, this, this.disposables);
|
||||
domEvent(this.rowsContainer, TouchEventType.Change)(this.onTouchChange, this, this.disposables);
|
||||
|
||||
this.layout();
|
||||
}
|
||||
|
||||
get domNode(): HTMLElement {
|
||||
return this._domNode;
|
||||
}
|
||||
|
||||
splice(start: number, deleteCount: number, elements: T[] = []): T[] {
|
||||
const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight);
|
||||
each(previousRenderRange, i => this.removeItemFromDOM(this.items[i]));
|
||||
|
||||
const inserted = elements.map<IItem<T>>(element => ({
|
||||
id: String(this.itemId++),
|
||||
element,
|
||||
size: this.delegate.getHeight(element),
|
||||
templateId: this.delegate.getTemplateId(element),
|
||||
row: null
|
||||
}));
|
||||
|
||||
this.rangeMap.splice(start, deleteCount, ...inserted);
|
||||
|
||||
const deleted = this.items.splice(start, deleteCount, ...inserted);
|
||||
|
||||
const renderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight);
|
||||
each(renderRange, i => this.insertItemInDOM(this.items[i], i));
|
||||
|
||||
const scrollHeight = this.getContentHeight();
|
||||
this.rowsContainer.style.height = `${scrollHeight}px`;
|
||||
this.scrollableElement.setScrollDimensions({ scrollHeight });
|
||||
|
||||
return deleted.map(i => i.element);
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.items.length;
|
||||
}
|
||||
|
||||
get renderHeight(): number {
|
||||
const scrollDimensions = this.scrollableElement.getScrollDimensions();
|
||||
return scrollDimensions.height;
|
||||
}
|
||||
|
||||
element(index: number): T {
|
||||
return this.items[index].element;
|
||||
}
|
||||
|
||||
domElement(index: number): HTMLElement {
|
||||
const row = this.items[index].row;
|
||||
return row && row.domNode;
|
||||
}
|
||||
|
||||
elementHeight(index: number): number {
|
||||
return this.items[index].size;
|
||||
}
|
||||
|
||||
elementTop(index: number): number {
|
||||
return this.rangeMap.positionAt(index);
|
||||
}
|
||||
|
||||
indexAt(position: number): number {
|
||||
return this.rangeMap.indexAt(position);
|
||||
}
|
||||
|
||||
indexAfter(position: number): number {
|
||||
return this.rangeMap.indexAfter(position);
|
||||
}
|
||||
|
||||
layout(height?: number): void {
|
||||
this.scrollableElement.setScrollDimensions({
|
||||
height: height || DOM.getContentHeight(this._domNode)
|
||||
});
|
||||
}
|
||||
|
||||
// Render
|
||||
|
||||
private render(renderTop: number, renderHeight: number): void {
|
||||
const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight);
|
||||
const renderRange = this.getRenderRange(renderTop, renderHeight);
|
||||
|
||||
const rangesToInsert = relativeComplement(renderRange, previousRenderRange);
|
||||
const rangesToRemove = relativeComplement(previousRenderRange, renderRange);
|
||||
|
||||
rangesToInsert.forEach(range => each(range, i => this.insertItemInDOM(this.items[i], i)));
|
||||
rangesToRemove.forEach(range => each(range, i => this.removeItemFromDOM(this.items[i])));
|
||||
|
||||
if (canUseTranslate3d() && !isWindows /* Windows: translate3d breaks subpixel-antialias (ClearType) unless a background is defined */) {
|
||||
const transform = `translate3d(0px, -${renderTop}px, 0px)`;
|
||||
this.rowsContainer.style.transform = transform;
|
||||
this.rowsContainer.style.webkitTransform = transform;
|
||||
} else {
|
||||
this.rowsContainer.style.top = `-${renderTop}px`;
|
||||
}
|
||||
|
||||
this.lastRenderTop = renderTop;
|
||||
this.lastRenderHeight = renderHeight;
|
||||
}
|
||||
|
||||
// DOM operations
|
||||
|
||||
private insertItemInDOM(item: IItem<T>, index: number): void {
|
||||
if (!item.row) {
|
||||
item.row = this.cache.alloc(item.templateId);
|
||||
}
|
||||
|
||||
if (!item.row.domNode.parentElement) {
|
||||
this.rowsContainer.appendChild(item.row.domNode);
|
||||
}
|
||||
|
||||
const renderer = this.renderers[item.templateId];
|
||||
item.row.domNode.style.top = `${this.elementTop(index)}px`;
|
||||
item.row.domNode.style.height = `${item.size}px`;
|
||||
item.row.domNode.setAttribute('data-index', `${index}`);
|
||||
renderer.renderElement(item.element, index, item.row.templateData);
|
||||
}
|
||||
|
||||
private removeItemFromDOM(item: IItem<T>): void {
|
||||
this.cache.release(item.row);
|
||||
item.row = null;
|
||||
}
|
||||
|
||||
getContentHeight(): number {
|
||||
return this.rangeMap.size;
|
||||
}
|
||||
|
||||
getScrollTop(): number {
|
||||
const scrollPosition = this.scrollableElement.getScrollPosition();
|
||||
return scrollPosition.scrollTop;
|
||||
}
|
||||
|
||||
setScrollTop(scrollTop: number): void {
|
||||
this.scrollableElement.setScrollPosition({ scrollTop });
|
||||
}
|
||||
|
||||
get scrollTop(): number {
|
||||
return this.getScrollTop();
|
||||
}
|
||||
|
||||
set scrollTop(scrollTop: number) {
|
||||
this.setScrollTop(scrollTop);
|
||||
}
|
||||
|
||||
// Events
|
||||
|
||||
addListener(type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable {
|
||||
const userHandler = handler;
|
||||
let domNode = this.domNode;
|
||||
|
||||
if (MouseEventTypes.indexOf(type) > -1) {
|
||||
handler = e => this.fireScopedEvent(e, userHandler, this.getItemIndexFromMouseEvent(e));
|
||||
} else if (type === TouchEventType.Tap) {
|
||||
domNode = this.rowsContainer;
|
||||
handler = e => this.fireScopedEvent(e, userHandler, this.getItemIndexFromGestureEvent(e));
|
||||
}
|
||||
|
||||
return DOM.addDisposableListener(domNode, type, handler, useCapture);
|
||||
}
|
||||
|
||||
private fireScopedEvent(
|
||||
event: any,
|
||||
handler: (event: any) => void,
|
||||
index: number
|
||||
) {
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const element = this.items[index].element;
|
||||
handler(assign(event, { element, index }));
|
||||
}
|
||||
|
||||
private onScroll(e: ScrollEvent): void {
|
||||
this.render(e.scrollTop, e.height);
|
||||
}
|
||||
|
||||
private onTouchChange(event: GestureEvent): void {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.scrollTop -= event.translationY;
|
||||
}
|
||||
|
||||
// Util
|
||||
|
||||
private getItemIndexFromMouseEvent(event: MouseEvent): number {
|
||||
return this.getItemIndexFromEventTarget(event.target);
|
||||
}
|
||||
|
||||
private getItemIndexFromGestureEvent(event: GestureEvent): number {
|
||||
return this.getItemIndexFromEventTarget(event.initialTarget);
|
||||
}
|
||||
|
||||
private getItemIndexFromEventTarget(target: EventTarget): number {
|
||||
while (target instanceof HTMLElement && target !== this.rowsContainer) {
|
||||
const element = target as HTMLElement;
|
||||
const rawIndex = element.getAttribute('data-index');
|
||||
|
||||
if (rawIndex) {
|
||||
const index = Number(rawIndex);
|
||||
|
||||
if (!isNaN(index)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
target = element.parentElement;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private getRenderRange(renderTop: number, renderHeight: number): IRange {
|
||||
return {
|
||||
start: this.rangeMap.indexAt(renderTop),
|
||||
end: this.rangeMap.indexAfter(renderTop + renderHeight - 1)
|
||||
};
|
||||
}
|
||||
|
||||
// Dispose
|
||||
|
||||
dispose() {
|
||||
this.items = null;
|
||||
|
||||
if (this._domNode && this._domNode.parentElement) {
|
||||
this._domNode.parentNode.removeChild(this._domNode);
|
||||
this._domNode = null;
|
||||
}
|
||||
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
965
src/vs/base/browser/ui/list/listWidget.ts
Normal file
@@ -0,0 +1,965 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./list';
|
||||
import { IDisposable, dispose, empty as EmptyDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isNumber } from 'vs/base/common/types';
|
||||
import { range } from 'vs/base/common/arrays';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
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 { domEvent } from 'vs/base/browser/event';
|
||||
import { IDelegate, IRenderer, IListEvent, IListMouseEvent, IListContextMenuEvent } from './list';
|
||||
import { ListView, IListViewOptions } from './listView';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
export interface IIdentityProvider<T> {
|
||||
(element: T): string;
|
||||
}
|
||||
|
||||
export interface ISpliceable<T> {
|
||||
splice(start: number, deleteCount: number, elements: T[]): void;
|
||||
}
|
||||
|
||||
class CombinedSpliceable<T> implements ISpliceable<T> {
|
||||
|
||||
constructor(private spliceables: ISpliceable<T>[]) { }
|
||||
|
||||
splice(start: number, deleteCount: number, elements: T[]): void {
|
||||
this.spliceables.forEach(s => s.splice(start, deleteCount, elements));
|
||||
}
|
||||
}
|
||||
|
||||
interface ITraitChangeEvent {
|
||||
indexes: number[];
|
||||
}
|
||||
|
||||
interface ITraitTemplateData {
|
||||
container: HTMLElement;
|
||||
elementDisposable: IDisposable;
|
||||
}
|
||||
|
||||
interface IRenderedElement {
|
||||
templateData: ITraitTemplateData;
|
||||
index: number;
|
||||
}
|
||||
|
||||
class TraitRenderer<T, D> implements IRenderer<T, ITraitTemplateData>
|
||||
{
|
||||
private rendered: IRenderedElement[] = [];
|
||||
|
||||
constructor(private trait: Trait<T>) { }
|
||||
|
||||
get templateId(): string {
|
||||
return `template:${this.trait.trait}`;
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): ITraitTemplateData {
|
||||
const elementDisposable = EmptyDisposable;
|
||||
return { container, elementDisposable };
|
||||
}
|
||||
|
||||
renderElement(element: T, index: number, templateData: ITraitTemplateData): void {
|
||||
templateData.elementDisposable.dispose();
|
||||
|
||||
const rendered = { index, templateData };
|
||||
this.rendered.push(rendered);
|
||||
templateData.elementDisposable = toDisposable(once(() => this.rendered.splice(this.rendered.indexOf(rendered), 1)));
|
||||
|
||||
this.trait.renderIndex(index, templateData.container);
|
||||
}
|
||||
|
||||
renderIndexes(indexes: number[]): void {
|
||||
this.rendered
|
||||
.filter(({ index }) => indexes.indexOf(index) > -1)
|
||||
.forEach(({ index, templateData }) => this.trait.renderIndex(index, templateData.container));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: ITraitTemplateData): void {
|
||||
templateData.elementDisposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class Trait<T> implements ISpliceable<boolean>, IDisposable {
|
||||
|
||||
/**
|
||||
* Sorted indexes which have this trait.
|
||||
*/
|
||||
private indexes: number[];
|
||||
|
||||
private _onChange = new Emitter<ITraitChangeEvent>();
|
||||
get onChange(): Event<ITraitChangeEvent> { return this._onChange.event; }
|
||||
|
||||
get trait(): string { return this._trait; }
|
||||
|
||||
@memoize
|
||||
get renderer(): TraitRenderer<T, any> {
|
||||
return new TraitRenderer<T, any>(this);
|
||||
}
|
||||
|
||||
constructor(private _trait: string) {
|
||||
this.indexes = [];
|
||||
}
|
||||
|
||||
splice(start: number, deleteCount: number, elements: boolean[]): void {
|
||||
const diff = elements.length - deleteCount;
|
||||
const end = start + deleteCount;
|
||||
const indexes = [
|
||||
...this.indexes.filter(i => i < start),
|
||||
...elements.reduce((r, hasTrait, i) => hasTrait ? [...r, i + start] : r, []),
|
||||
...this.indexes.filter(i => i >= end).map(i => i + diff)
|
||||
];
|
||||
|
||||
this.renderer.splice(start, deleteCount);
|
||||
this.set(indexes);
|
||||
}
|
||||
|
||||
renderIndex(index: number, container: HTMLElement): void {
|
||||
DOM.toggleClass(container, this._trait, this.contains(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the indexes which should have this trait.
|
||||
*
|
||||
* @param indexes Indexes which should have this trait.
|
||||
* @return The old indexes which had this trait.
|
||||
*/
|
||||
set(indexes: number[]): number[] {
|
||||
const result = this.indexes;
|
||||
this.indexes = indexes;
|
||||
|
||||
const toRender = disjunction(result, indexes);
|
||||
this.renderer.renderIndexes(toRender);
|
||||
|
||||
this._onChange.fire({ indexes });
|
||||
return result;
|
||||
}
|
||||
|
||||
get(): number[] {
|
||||
return this.indexes;
|
||||
}
|
||||
|
||||
contains(index: number): boolean {
|
||||
return this.indexes.some(i => i === index);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.indexes = null;
|
||||
this._onChange = dispose(this._onChange);
|
||||
}
|
||||
}
|
||||
|
||||
class FocusTrait<T> extends Trait<T> {
|
||||
|
||||
constructor(
|
||||
private getDomId: IIdentityProvider<number>
|
||||
) {
|
||||
super('focused');
|
||||
}
|
||||
|
||||
renderIndex(index: number, container: HTMLElement): void {
|
||||
super.renderIndex(index, container);
|
||||
container.setAttribute('role', 'treeitem');
|
||||
container.setAttribute('id', this.getDomId(index));
|
||||
}
|
||||
}
|
||||
|
||||
class Aria<T> implements IRenderer<T, HTMLElement>, ISpliceable<T> {
|
||||
|
||||
private length = 0;
|
||||
|
||||
get templateId(): string {
|
||||
return 'aria';
|
||||
}
|
||||
|
||||
splice(start: number, deleteCount: number, elements: T[]): void {
|
||||
this.length += elements.length - deleteCount;
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): HTMLElement {
|
||||
return container;
|
||||
}
|
||||
|
||||
renderElement(element: T, index: number, container: HTMLElement): void {
|
||||
container.setAttribute('aria-setsize', `${this.length}`);
|
||||
container.setAttribute('aria-posinset', `${index + 1}`);
|
||||
}
|
||||
|
||||
disposeTemplate(container: HTMLElement): void {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The TraitSpliceable is used as a util class to be able
|
||||
* to preserve traits across splice calls, given an identity
|
||||
* provider.
|
||||
*/
|
||||
class TraitSpliceable<T> implements ISpliceable<T> {
|
||||
|
||||
constructor(
|
||||
private trait: Trait<T>,
|
||||
private view: ListView<T>,
|
||||
private getId?: IIdentityProvider<T>
|
||||
) { }
|
||||
|
||||
splice(start: number, deleteCount: number, elements: T[]): void {
|
||||
if (!this.getId) {
|
||||
return this.trait.splice(start, deleteCount, elements.map(e => false));
|
||||
}
|
||||
|
||||
const pastElementsWithTrait = this.trait.get().map(i => this.getId(this.view.element(i)));
|
||||
const elementsWithTrait = elements.map(e => pastElementsWithTrait.indexOf(this.getId(e)) > -1);
|
||||
|
||||
this.trait.splice(start, deleteCount, elementsWithTrait);
|
||||
}
|
||||
}
|
||||
|
||||
class KeyboardController<T> implements IDisposable {
|
||||
|
||||
private disposables: IDisposable[];
|
||||
|
||||
constructor(
|
||||
private list: List<T>,
|
||||
private view: ListView<T>
|
||||
) {
|
||||
this.disposables = [];
|
||||
|
||||
const onKeyDown = chain(domEvent(view.domNode, 'keydown'))
|
||||
.map(e => new StandardKeyboardEvent(e));
|
||||
|
||||
onKeyDown.filter(e => e.keyCode === KeyCode.Enter).on(this.onEnter, this, this.disposables);
|
||||
onKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(this.onUpArrow, this, this.disposables);
|
||||
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);
|
||||
}
|
||||
|
||||
private onEnter(e: StandardKeyboardEvent): void {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.list.setSelection(this.list.getFocus());
|
||||
this.list.open(this.list.getFocus());
|
||||
}
|
||||
|
||||
private onUpArrow(e: StandardKeyboardEvent): void {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.list.focusPrevious();
|
||||
this.list.reveal(this.list.getFocus()[0]);
|
||||
this.view.domNode.focus();
|
||||
}
|
||||
|
||||
private onDownArrow(e: StandardKeyboardEvent): void {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.list.focusNext();
|
||||
this.list.reveal(this.list.getFocus()[0]);
|
||||
this.view.domNode.focus();
|
||||
}
|
||||
|
||||
private onPageUpArrow(e: StandardKeyboardEvent): void {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.list.focusPreviousPage();
|
||||
this.list.reveal(this.list.getFocus()[0]);
|
||||
this.view.domNode.focus();
|
||||
}
|
||||
|
||||
private onPageDownArrow(e: StandardKeyboardEvent): void {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.list.focusNextPage();
|
||||
this.list.reveal(this.list.getFocus()[0]);
|
||||
this.view.domNode.focus();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
|
||||
function isSelectionSingleChangeEvent(event: IListMouseEvent<any>): boolean {
|
||||
return platform.isMacintosh ? event.metaKey : event.ctrlKey;
|
||||
}
|
||||
|
||||
function isSelectionRangeChangeEvent(event: IListMouseEvent<any>): boolean {
|
||||
return event.shiftKey;
|
||||
}
|
||||
|
||||
function isSelectionChangeEvent(event: IListMouseEvent<any>): boolean {
|
||||
return isSelectionSingleChangeEvent(event) || isSelectionRangeChangeEvent(event);
|
||||
}
|
||||
|
||||
export interface IMouseControllerOptions {
|
||||
selectOnMouseDown?: boolean;
|
||||
}
|
||||
|
||||
class MouseController<T> implements IDisposable {
|
||||
|
||||
private disposables: IDisposable[];
|
||||
|
||||
@memoize get onContextMenu(): Event<IListContextMenuEvent<T>> {
|
||||
const fromKeyboard = chain(domEvent(this.view.domNode, 'keydown'))
|
||||
.map(e => new StandardKeyboardEvent(e))
|
||||
.filter(e => this.list.getFocus().length > 0)
|
||||
.filter(e => e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10))
|
||||
.map(e => {
|
||||
const index = this.list.getFocus()[0];
|
||||
const element = this.view.element(index);
|
||||
const anchor = this.view.domElement(index);
|
||||
return { index, element, anchor };
|
||||
})
|
||||
.filter(({ anchor }) => !!anchor)
|
||||
.event;
|
||||
|
||||
const fromMouse = chain(fromCallback(handler => this.view.addListener('contextmenu', handler)))
|
||||
.map(({ element, index, clientX, clientY }) => ({ element, index, anchor: { x: clientX + 1, y: clientY } }))
|
||||
.event;
|
||||
|
||||
return any<IListContextMenuEvent<T>>(fromKeyboard, fromMouse);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private list: List<T>,
|
||||
private view: ListView<T>,
|
||||
private options: IMouseControllerOptions = {}
|
||||
) {
|
||||
this.disposables = [];
|
||||
this.disposables.push(view.addListener('mousedown', e => this.onMouseDown(e)));
|
||||
this.disposables.push(view.addListener('click', e => this.onPointer(e)));
|
||||
this.disposables.push(view.addListener('dblclick', e => this.onDoubleClick(e)));
|
||||
this.disposables.push(view.addListener('touchstart', e => this.onMouseDown(e)));
|
||||
this.disposables.push(view.addListener(TouchEventType.Tap, e => this.onPointer(e)));
|
||||
}
|
||||
|
||||
private onMouseDown(e: IListMouseEvent<T>): void {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.view.domNode.focus();
|
||||
|
||||
let reference = this.list.getFocus()[0];
|
||||
reference = reference === undefined ? this.list.getSelection()[0] : reference;
|
||||
|
||||
if (isSelectionRangeChangeEvent(e)) {
|
||||
return this.changeSelection(e, reference);
|
||||
}
|
||||
|
||||
const focus = e.index;
|
||||
this.list.setFocus([focus]);
|
||||
|
||||
if (isSelectionChangeEvent(e)) {
|
||||
return this.changeSelection(e, reference);
|
||||
}
|
||||
|
||||
if (this.options.selectOnMouseDown) {
|
||||
this.list.setSelection([focus]);
|
||||
this.list.open([focus]);
|
||||
}
|
||||
}
|
||||
|
||||
private onPointer(e: IListMouseEvent<T>): void {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (isSelectionChangeEvent(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.options.selectOnMouseDown) {
|
||||
const focus = this.list.getFocus();
|
||||
this.list.setSelection(focus);
|
||||
this.list.open(focus);
|
||||
}
|
||||
}
|
||||
|
||||
private onDoubleClick(e: IListMouseEvent<T>): void {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (isSelectionChangeEvent(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const focus = this.list.getFocus();
|
||||
this.list.setSelection(focus);
|
||||
this.list.pin(focus);
|
||||
}
|
||||
|
||||
private changeSelection(e: IListMouseEvent<T>, reference: number | undefined): void {
|
||||
const focus = e.index;
|
||||
|
||||
if (isSelectionRangeChangeEvent(e) && reference !== undefined) {
|
||||
const min = Math.min(reference, focus);
|
||||
const max = Math.max(reference, focus);
|
||||
const rangeSelection = range(max + 1, min);
|
||||
const selection = this.list.getSelection();
|
||||
const contiguousRange = getContiguousRangeContaining(disjunction(selection, [reference]), reference);
|
||||
|
||||
if (contiguousRange.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newSelection = disjunction(rangeSelection, relativeComplement(selection, contiguousRange));
|
||||
this.list.setSelection(newSelection);
|
||||
|
||||
} else if (isSelectionSingleChangeEvent(e)) {
|
||||
const selection = this.list.getSelection();
|
||||
const newSelection = selection.filter(i => i !== focus);
|
||||
|
||||
if (selection.length === newSelection.length) {
|
||||
this.list.setSelection([...newSelection, focus]);
|
||||
} else {
|
||||
this.list.setSelection(newSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IListOptions<T> extends IListViewOptions, IMouseControllerOptions, IListStyles {
|
||||
identityProvider?: IIdentityProvider<T>;
|
||||
ariaLabel?: string;
|
||||
mouseSupport?: boolean;
|
||||
keyboardSupport?: boolean;
|
||||
}
|
||||
|
||||
export interface IListStyles {
|
||||
listFocusBackground?: Color;
|
||||
listFocusForeground?: Color;
|
||||
listActiveSelectionBackground?: Color;
|
||||
listActiveSelectionForeground?: Color;
|
||||
listFocusAndSelectionBackground?: Color;
|
||||
listFocusAndSelectionForeground?: Color;
|
||||
listInactiveSelectionBackground?: Color;
|
||||
listInactiveSelectionForeground?: Color;
|
||||
listInactiveFocusBackground?: Color;
|
||||
listHoverBackground?: Color;
|
||||
listHoverForeground?: Color;
|
||||
listDropBackground?: Color;
|
||||
listFocusOutline?: Color;
|
||||
listInactiveFocusOutline?: Color;
|
||||
listSelectionOutline?: Color;
|
||||
listHoverOutline?: Color;
|
||||
}
|
||||
|
||||
const defaultStyles: IListStyles = {
|
||||
listFocusBackground: Color.fromHex('#073655'),
|
||||
listActiveSelectionBackground: Color.fromHex('#0E639C'),
|
||||
listActiveSelectionForeground: Color.fromHex('#FFFFFF'),
|
||||
listFocusAndSelectionBackground: Color.fromHex('#094771'),
|
||||
listFocusAndSelectionForeground: Color.fromHex('#FFFFFF'),
|
||||
listInactiveSelectionBackground: Color.fromHex('#3F3F46'),
|
||||
listHoverBackground: Color.fromHex('#2A2D2E'),
|
||||
listDropBackground: Color.fromHex('#383B3D')
|
||||
};
|
||||
|
||||
const DefaultOptions: IListOptions<any> = {
|
||||
keyboardSupport: true,
|
||||
mouseSupport: true
|
||||
};
|
||||
|
||||
// TODO@Joao: move these utils into a SortedArray class
|
||||
|
||||
function getContiguousRangeContaining(range: number[], value: number): number[] {
|
||||
const index = range.indexOf(value);
|
||||
|
||||
if (index === -1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result = [];
|
||||
let i = index - 1;
|
||||
while (i >= 0 && range[i] === value - (index - i)) {
|
||||
result.push(range[i--]);
|
||||
}
|
||||
|
||||
result.reverse();
|
||||
i = index;
|
||||
while (i < range.length && range[i] === value + (i - index)) {
|
||||
result.push(range[i++]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two sorted collections of numbers, returns the intersection
|
||||
* betweem them (OR).
|
||||
*/
|
||||
function disjunction(one: number[], other: number[]): number[] {
|
||||
const result = [];
|
||||
let i = 0, j = 0;
|
||||
|
||||
while (i < one.length || j < other.length) {
|
||||
if (i >= one.length) {
|
||||
result.push(other[j++]);
|
||||
} else if (j >= other.length) {
|
||||
result.push(one[i++]);
|
||||
} else if (one[i] === other[j]) {
|
||||
result.push(one[i]);
|
||||
i++;
|
||||
j++;
|
||||
continue;
|
||||
} else if (one[i] < other[j]) {
|
||||
result.push(one[i++]);
|
||||
} else {
|
||||
result.push(other[j++]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two sorted collections of numbers, returns the relative
|
||||
* complement between them (XOR).
|
||||
*/
|
||||
function relativeComplement(one: number[], other: number[]): number[] {
|
||||
const result = [];
|
||||
let i = 0, j = 0;
|
||||
|
||||
while (i < one.length || j < other.length) {
|
||||
if (i >= one.length) {
|
||||
result.push(other[j++]);
|
||||
} else if (j >= other.length) {
|
||||
result.push(one[i++]);
|
||||
} else if (one[i] === other[j]) {
|
||||
i++;
|
||||
j++;
|
||||
continue;
|
||||
} else if (one[i] < other[j]) {
|
||||
result.push(one[i++]);
|
||||
} else {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const numericSort = (a: number, b: number) => a - b;
|
||||
|
||||
class PipelineRenderer<T> implements IRenderer<T, any> {
|
||||
|
||||
constructor(
|
||||
private _templateId: string,
|
||||
private renderers: IRenderer<T, any>[]
|
||||
) { }
|
||||
|
||||
get templateId(): string {
|
||||
return this._templateId;
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): any[] {
|
||||
return this.renderers.map(r => r.renderTemplate(container));
|
||||
}
|
||||
|
||||
renderElement(element: T, index: number, templateData: any[]): void {
|
||||
this.renderers.forEach((r, i) => r.renderElement(element, index, templateData[i]));
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: any[]): void {
|
||||
this.renderers.forEach((r, i) => r.disposeTemplate(templateData[i]));
|
||||
}
|
||||
}
|
||||
|
||||
export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
|
||||
private static InstanceCount = 0;
|
||||
private idPrefix = `list_id_${++List.InstanceCount}`;
|
||||
|
||||
private focus: Trait<T>;
|
||||
private selection: Trait<T>;
|
||||
private eventBufferer: EventBufferer;
|
||||
private view: ListView<T>;
|
||||
private spliceable: ISpliceable<T>;
|
||||
private disposables: IDisposable[];
|
||||
private styleElement: HTMLStyleElement;
|
||||
|
||||
@memoize get onFocusChange(): Event<IListEvent<T>> {
|
||||
return mapEvent(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e));
|
||||
}
|
||||
|
||||
@memoize get onSelectionChange(): Event<IListEvent<T>> {
|
||||
return mapEvent(this.eventBufferer.wrapEvent(this.selection.onChange), e => this.toListEvent(e));
|
||||
}
|
||||
|
||||
private _onContextMenu: Event<IListContextMenuEvent<T>> = Event.None;
|
||||
get onContextMenu(): Event<IListContextMenuEvent<T>> {
|
||||
return this._onContextMenu;
|
||||
}
|
||||
|
||||
private _onOpen = new Emitter<number[]>();
|
||||
@memoize get onOpen(): Event<IListEvent<T>> {
|
||||
return mapEvent(this._onOpen.event, indexes => this.toListEvent({ indexes }));
|
||||
}
|
||||
|
||||
private _onPin = new Emitter<number[]>();
|
||||
@memoize get onPin(): Event<IListEvent<T>> {
|
||||
return mapEvent(this._onPin.event, indexes => this.toListEvent({ indexes }));
|
||||
}
|
||||
|
||||
readonly onDOMFocus: Event<void>;
|
||||
readonly onDOMBlur: Event<void>;
|
||||
|
||||
private _onDispose = new Emitter<void>();
|
||||
get onDispose(): Event<void> { return this._onDispose.event; }
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
delegate: IDelegate<T>,
|
||||
renderers: IRenderer<T, any>[],
|
||||
options: IListOptions<T> = DefaultOptions
|
||||
) {
|
||||
const aria = new Aria();
|
||||
this.focus = new FocusTrait(i => this.getElementDomId(i));
|
||||
this.selection = new Trait('selected');
|
||||
|
||||
this.eventBufferer = new EventBufferer();
|
||||
mixin(options, defaultStyles, false);
|
||||
|
||||
renderers = renderers.map(r => new PipelineRenderer(r.templateId, [aria, this.focus.renderer, this.selection.renderer, r]));
|
||||
|
||||
this.view = new ListView(container, delegate, renderers, options);
|
||||
this.view.domNode.setAttribute('role', 'tree');
|
||||
DOM.addClass(this.view.domNode, this.idPrefix);
|
||||
this.view.domNode.tabIndex = 0;
|
||||
|
||||
this.styleElement = DOM.createStyleSheet(this.view.domNode);
|
||||
|
||||
this.spliceable = new CombinedSpliceable([
|
||||
aria,
|
||||
new TraitSpliceable(this.focus, this.view, options.identityProvider),
|
||||
new TraitSpliceable(this.selection, this.view, options.identityProvider),
|
||||
this.view
|
||||
]);
|
||||
|
||||
this.disposables = [this.focus, this.selection, this.view, this._onDispose];
|
||||
|
||||
this.onDOMFocus = mapEvent(domEvent(this.view.domNode, 'focus', true), () => null);
|
||||
this.onDOMBlur = mapEvent(domEvent(this.view.domNode, 'blur', true), () => null);
|
||||
|
||||
if (typeof options.keyboardSupport !== 'boolean' || options.keyboardSupport) {
|
||||
const controller = new KeyboardController(this, this.view);
|
||||
this.disposables.push(controller);
|
||||
}
|
||||
|
||||
if (typeof options.mouseSupport !== 'boolean' || options.mouseSupport) {
|
||||
const controller = new MouseController(this, this.view, options);
|
||||
this.disposables.push(controller);
|
||||
this._onContextMenu = controller.onContextMenu;
|
||||
}
|
||||
|
||||
this.onFocusChange(this._onFocusChange, this, this.disposables);
|
||||
this.onSelectionChange(this._onSelectionChange, this, this.disposables);
|
||||
|
||||
if (options.ariaLabel) {
|
||||
this.view.domNode.setAttribute('aria-label', options.ariaLabel);
|
||||
}
|
||||
|
||||
this.style(options);
|
||||
}
|
||||
|
||||
splice(start: number, deleteCount: number, elements: T[] = []): void {
|
||||
this.eventBufferer.bufferEvents(() => this.spliceable.splice(start, deleteCount, elements));
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.view.length;
|
||||
}
|
||||
|
||||
get contentHeight(): number {
|
||||
return this.view.getContentHeight();
|
||||
}
|
||||
|
||||
get scrollTop(): number {
|
||||
return this.view.getScrollTop();
|
||||
}
|
||||
|
||||
set scrollTop(scrollTop: number) {
|
||||
this.view.setScrollTop(scrollTop);
|
||||
}
|
||||
|
||||
layout(height?: number): void {
|
||||
this.view.layout(height);
|
||||
}
|
||||
|
||||
setSelection(indexes: number[]): void {
|
||||
indexes = indexes.sort(numericSort);
|
||||
this.selection.set(indexes);
|
||||
}
|
||||
|
||||
selectNext(n = 1, loop = false): void {
|
||||
if (this.length === 0) { return; }
|
||||
const selection = this.selection.get();
|
||||
let index = selection.length > 0 ? selection[0] + n : 0;
|
||||
this.setSelection(loop ? [index % this.length] : [Math.min(index, this.length - 1)]);
|
||||
}
|
||||
|
||||
selectPrevious(n = 1, loop = false): void {
|
||||
if (this.length === 0) { return; }
|
||||
const selection = this.selection.get();
|
||||
let index = selection.length > 0 ? selection[0] - n : 0;
|
||||
if (loop && index < 0) {
|
||||
index = this.length + (index % this.length);
|
||||
}
|
||||
this.setSelection([Math.max(index, 0)]);
|
||||
}
|
||||
|
||||
getSelection(): number[] {
|
||||
return this.selection.get();
|
||||
}
|
||||
|
||||
getSelectedElements(): T[] {
|
||||
return this.getSelection().map(i => this.view.element(i));
|
||||
}
|
||||
|
||||
setFocus(indexes: number[]): void {
|
||||
indexes = indexes.sort(numericSort);
|
||||
this.focus.set(indexes);
|
||||
}
|
||||
|
||||
focusNext(n = 1, loop = false): void {
|
||||
if (this.length === 0) { return; }
|
||||
const focus = this.focus.get();
|
||||
let index = focus.length > 0 ? focus[0] + n : 0;
|
||||
this.setFocus(loop ? [index % this.length] : [Math.min(index, this.length - 1)]);
|
||||
}
|
||||
|
||||
focusPrevious(n = 1, loop = false): void {
|
||||
if (this.length === 0) { return; }
|
||||
const focus = this.focus.get();
|
||||
let index = focus.length > 0 ? focus[0] - n : 0;
|
||||
if (loop && index < 0) { index = (this.length + (index % this.length)) % this.length; }
|
||||
this.setFocus([Math.max(index, 0)]);
|
||||
}
|
||||
|
||||
focusNextPage(): void {
|
||||
let lastPageIndex = this.view.indexAt(this.view.getScrollTop() + this.view.renderHeight);
|
||||
lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1;
|
||||
const lastPageElement = this.view.element(lastPageIndex);
|
||||
const currentlyFocusedElement = this.getFocusedElements()[0];
|
||||
|
||||
if (currentlyFocusedElement !== lastPageElement) {
|
||||
this.setFocus([lastPageIndex]);
|
||||
} else {
|
||||
const previousScrollTop = this.view.getScrollTop();
|
||||
this.view.setScrollTop(previousScrollTop + this.view.renderHeight - this.view.elementHeight(lastPageIndex));
|
||||
|
||||
if (this.view.getScrollTop() !== previousScrollTop) {
|
||||
// Let the scroll event listener run
|
||||
setTimeout(() => this.focusNextPage(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
focusPreviousPage(): void {
|
||||
let firstPageIndex: number;
|
||||
const scrollTop = this.view.getScrollTop();
|
||||
|
||||
if (scrollTop === 0) {
|
||||
firstPageIndex = this.view.indexAt(scrollTop);
|
||||
} else {
|
||||
firstPageIndex = this.view.indexAfter(scrollTop - 1);
|
||||
}
|
||||
|
||||
const firstPageElement = this.view.element(firstPageIndex);
|
||||
const currentlyFocusedElement = this.getFocusedElements()[0];
|
||||
|
||||
if (currentlyFocusedElement !== firstPageElement) {
|
||||
this.setFocus([firstPageIndex]);
|
||||
} else {
|
||||
const previousScrollTop = scrollTop;
|
||||
this.view.setScrollTop(scrollTop - this.view.renderHeight);
|
||||
|
||||
if (this.view.getScrollTop() !== previousScrollTop) {
|
||||
// Let the scroll event listener run
|
||||
setTimeout(() => this.focusPreviousPage(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
focusLast(): void {
|
||||
if (this.length === 0) { return; }
|
||||
this.setFocus([this.length - 1]);
|
||||
}
|
||||
|
||||
focusFirst(): void {
|
||||
if (this.length === 0) { return; }
|
||||
this.setFocus([0]);
|
||||
}
|
||||
|
||||
getFocus(): number[] {
|
||||
return this.focus.get();
|
||||
}
|
||||
|
||||
getFocusedElements(): T[] {
|
||||
return this.getFocus().map(i => this.view.element(i));
|
||||
}
|
||||
|
||||
reveal(index: number, relativeTop?: number): void {
|
||||
const scrollTop = this.view.getScrollTop();
|
||||
const elementTop = this.view.elementTop(index);
|
||||
const elementHeight = this.view.elementHeight(index);
|
||||
|
||||
if (isNumber(relativeTop)) {
|
||||
relativeTop = relativeTop < 0 ? 0 : relativeTop;
|
||||
relativeTop = relativeTop > 1 ? 1 : relativeTop;
|
||||
|
||||
// y = mx + b
|
||||
const m = elementHeight - this.view.renderHeight;
|
||||
this.view.setScrollTop(m * relativeTop + elementTop);
|
||||
} else {
|
||||
const viewItemBottom = elementTop + elementHeight;
|
||||
const wrapperBottom = scrollTop + this.view.renderHeight;
|
||||
|
||||
if (elementTop < scrollTop) {
|
||||
this.view.setScrollTop(elementTop);
|
||||
} else if (viewItemBottom >= wrapperBottom) {
|
||||
this.view.setScrollTop(viewItemBottom - this.view.renderHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getElementDomId(index: number): string {
|
||||
return `${this.idPrefix}_${index}`;
|
||||
}
|
||||
|
||||
isDOMFocused(): boolean {
|
||||
return this.view.domNode === document.activeElement;
|
||||
}
|
||||
|
||||
getHTMLElement(): HTMLElement {
|
||||
return this.view.domNode;
|
||||
}
|
||||
|
||||
open(indexes: number[]): void {
|
||||
this._onOpen.fire(indexes);
|
||||
}
|
||||
|
||||
pin(indexes: number[]): void {
|
||||
this._onPin.fire(indexes);
|
||||
}
|
||||
|
||||
style(styles: IListStyles): void {
|
||||
const content: string[] = [];
|
||||
|
||||
if (styles.listFocusBackground) {
|
||||
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.focused { background-color: ${styles.listFocusBackground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listFocusForeground) {
|
||||
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.focused { color: ${styles.listFocusForeground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listActiveSelectionBackground) {
|
||||
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.selected { background-color: ${styles.listActiveSelectionBackground}; }`);
|
||||
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.selected:hover { background-color: ${styles.listActiveSelectionBackground}; }`); // overwrite :hover style in this case!
|
||||
}
|
||||
|
||||
if (styles.listActiveSelectionForeground) {
|
||||
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.selected { color: ${styles.listActiveSelectionForeground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listFocusAndSelectionBackground) {
|
||||
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listFocusAndSelectionForeground) {
|
||||
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listInactiveFocusBackground) {
|
||||
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.focused { background-color: ${styles.listInactiveFocusBackground}; }`);
|
||||
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.focused:hover { background-color: ${styles.listInactiveFocusBackground}; }`); // overwrite :hover style in this case!
|
||||
}
|
||||
|
||||
if (styles.listInactiveSelectionBackground) {
|
||||
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.selected { background-color: ${styles.listInactiveSelectionBackground}; }`);
|
||||
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.selected:hover { background-color: ${styles.listInactiveSelectionBackground}; }`); // overwrite :hover style in this case!
|
||||
}
|
||||
|
||||
if (styles.listInactiveSelectionForeground) {
|
||||
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.selected { color: ${styles.listInactiveSelectionForeground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listHoverBackground) {
|
||||
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row:hover { background-color: ${styles.listHoverBackground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listHoverForeground) {
|
||||
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row:hover { color: ${styles.listHoverForeground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listSelectionOutline) {
|
||||
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.selected { outline: 1px dotted ${styles.listSelectionOutline}; outline-offset: -1px; }`);
|
||||
}
|
||||
|
||||
if (styles.listFocusOutline) {
|
||||
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; }`);
|
||||
}
|
||||
|
||||
if (styles.listInactiveFocusOutline) {
|
||||
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.focused { outline: 1px dotted ${styles.listInactiveFocusOutline}; outline-offset: -1px; }`);
|
||||
}
|
||||
|
||||
if (styles.listHoverOutline) {
|
||||
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`);
|
||||
}
|
||||
|
||||
this.styleElement.innerHTML = content.join('\n');
|
||||
}
|
||||
|
||||
private toListEvent({ indexes }: ITraitChangeEvent) {
|
||||
return { indexes, elements: indexes.map(i => this.view.element(i)) };
|
||||
}
|
||||
|
||||
private _onFocusChange(): void {
|
||||
const focus = this.focus.get();
|
||||
|
||||
if (focus.length > 0) {
|
||||
this.view.domNode.setAttribute('aria-activedescendant', this.getElementDomId(focus[0]));
|
||||
} else {
|
||||
this.view.domNode.removeAttribute('aria-activedescendant');
|
||||
}
|
||||
|
||||
this.view.domNode.setAttribute('role', 'tree');
|
||||
DOM.toggleClass(this.view.domNode, 'element-focused', focus.length > 0);
|
||||
}
|
||||
|
||||
private _onSelectionChange(): void {
|
||||
const selection = this.selection.get();
|
||||
|
||||
DOM.toggleClass(this.view.domNode, 'selection-none', selection.length === 0);
|
||||
DOM.toggleClass(this.view.domNode, 'selection-single', selection.length === 1);
|
||||
DOM.toggleClass(this.view.domNode, 'selection-multiple', selection.length > 1);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._onDispose.fire();
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
241
src/vs/base/browser/ui/list/rangeMap.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export interface IItem {
|
||||
size: number;
|
||||
}
|
||||
|
||||
export interface IRange {
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
export interface IRangedGroup {
|
||||
range: IRange;
|
||||
size: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the intersection between two ranges as a range itself.
|
||||
* Returns `null` if the intersection is empty.
|
||||
*/
|
||||
export function intersect(one: IRange, other: IRange): IRange {
|
||||
if (one.start >= other.end || other.start >= one.end) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const start = Math.max(one.start, other.start);
|
||||
const end = Math.min(one.end, other.end);
|
||||
|
||||
if (end - start <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { start, end };
|
||||
}
|
||||
|
||||
export function isEmpty(range: IRange): boolean {
|
||||
return range.end - range.start <= 0;
|
||||
}
|
||||
|
||||
export function relativeComplement(one: IRange, other: IRange): IRange[] {
|
||||
const result: IRange[] = [];
|
||||
const first = { start: one.start, end: Math.min(other.start, one.end) };
|
||||
const second = { start: Math.max(other.end, one.start), end: one.end };
|
||||
|
||||
if (!isEmpty(first)) {
|
||||
result.push(first);
|
||||
}
|
||||
|
||||
if (!isEmpty(second)) {
|
||||
result.push(second);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function each(range: IRange, fn: (index: number) => void): void {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
fn(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the intersection between a ranged group and a range.
|
||||
* Returns `[]` if the intersection is empty.
|
||||
*/
|
||||
export function groupIntersect(range: IRange, groups: IRangedGroup[]): IRangedGroup[] {
|
||||
const result: IRangedGroup[] = [];
|
||||
|
||||
for (let r of groups) {
|
||||
if (range.start >= r.range.end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (range.end < r.range.start) {
|
||||
break;
|
||||
}
|
||||
|
||||
const intersection = intersect(range, r.range);
|
||||
|
||||
if (!intersection) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push({
|
||||
range: intersection,
|
||||
size: r.size
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts a range by that `much`.
|
||||
*/
|
||||
function shift({ start, end }: IRange, much: number): IRange {
|
||||
return { start: start + much, end: end + much };
|
||||
}
|
||||
|
||||
/**
|
||||
* Consolidates a collection of ranged groups.
|
||||
*
|
||||
* Consolidation is the process of merging consecutive ranged groups
|
||||
* that share the same `size`.
|
||||
*/
|
||||
export function consolidate(groups: IRangedGroup[]): IRangedGroup[] {
|
||||
const result: IRangedGroup[] = [];
|
||||
let previousGroup: IRangedGroup = null;
|
||||
|
||||
for (let group of groups) {
|
||||
const start = group.range.start;
|
||||
const end = group.range.end;
|
||||
const size = group.size;
|
||||
|
||||
if (previousGroup && size === previousGroup.size) {
|
||||
previousGroup.range.end = end;
|
||||
continue;
|
||||
}
|
||||
|
||||
previousGroup = { range: { start, end }, size };
|
||||
result.push(previousGroup);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates several collections of ranged groups into a single
|
||||
* collection.
|
||||
*/
|
||||
function concat(...groups: IRangedGroup[][]): IRangedGroup[] {
|
||||
return consolidate(groups.reduce((r, g) => r.concat(g), [] as IRangedGroup[]));
|
||||
}
|
||||
|
||||
export class RangeMap {
|
||||
|
||||
private groups: IRangedGroup[] = [];
|
||||
private _size = 0;
|
||||
|
||||
splice(index: number, deleteCount: number, ...items: IItem[]): void {
|
||||
const diff = items.length - deleteCount;
|
||||
const before = groupIntersect({ start: 0, end: index }, this.groups);
|
||||
const after = groupIntersect({ start: index + deleteCount, end: Number.POSITIVE_INFINITY }, this.groups)
|
||||
.map<IRangedGroup>(g => ({ range: shift(g.range, diff), size: g.size }));
|
||||
|
||||
const middle = items.map<IRangedGroup>((item, i) => ({
|
||||
range: { start: index + i, end: index + i + 1 },
|
||||
size: item.size
|
||||
}));
|
||||
|
||||
this.groups = concat(before, middle, after);
|
||||
this._size = this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of items in the range map.
|
||||
*/
|
||||
get count(): number {
|
||||
const len = this.groups.length;
|
||||
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.groups[len - 1].range.end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of the sizes of all items in the range map.
|
||||
*/
|
||||
get size(): number {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the item at the given position.
|
||||
*/
|
||||
indexAt(position: number): number {
|
||||
if (position < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
let size = 0;
|
||||
|
||||
for (let group of this.groups) {
|
||||
const count = group.range.end - group.range.start;
|
||||
const newSize = size + (count * group.size);
|
||||
|
||||
if (position < newSize) {
|
||||
return index + Math.floor((position - size) / group.size);
|
||||
}
|
||||
|
||||
index += count;
|
||||
size = newSize;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the item right after the item at the
|
||||
* index of the given position.
|
||||
*/
|
||||
indexAfter(position: number): number {
|
||||
return Math.min(this.indexAt(position) + 1, this.count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start position of the item at the given index.
|
||||
*/
|
||||
positionAt(index: number): number {
|
||||
if (index < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let position = 0;
|
||||
let count = 0;
|
||||
|
||||
for (let group of this.groups) {
|
||||
const groupCount = group.range.end - group.range.start;
|
||||
const newCount = count + groupCount;
|
||||
|
||||
if (index < newCount) {
|
||||
return position + ((index - count) * group.size);
|
||||
}
|
||||
|
||||
position += groupCount * group.size;
|
||||
count = newCount;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.groups = null;
|
||||
}
|
||||
}
|
||||
93
src/vs/base/browser/ui/list/rowCache.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IRenderer } from './list';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { $, removeClass } from 'vs/base/browser/dom';
|
||||
|
||||
export interface IRow {
|
||||
domNode: HTMLElement;
|
||||
templateId: string;
|
||||
templateData: any;
|
||||
}
|
||||
|
||||
function removeFromParent(element: HTMLElement): void {
|
||||
try {
|
||||
element.parentElement.removeChild(element);
|
||||
} catch (e) {
|
||||
// this will throw if this happens due to a blur event, nasty business
|
||||
}
|
||||
}
|
||||
|
||||
export class RowCache<T> implements IDisposable {
|
||||
|
||||
private cache: { [templateId: string]: IRow[]; };
|
||||
|
||||
constructor(private renderers: { [templateId: string]: IRenderer<T, any>; }) {
|
||||
this.cache = Object.create(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a row either by creating a new one or reusing
|
||||
* a previously released row which shares the same templateId.
|
||||
*/
|
||||
alloc(templateId: string): IRow {
|
||||
let result = this.getTemplateCache(templateId).pop();
|
||||
|
||||
if (!result) {
|
||||
const domNode = $('.monaco-list-row');
|
||||
const renderer = this.renderers[templateId];
|
||||
const templateData = renderer.renderTemplate(domNode);
|
||||
result = { domNode, templateId, templateData };
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the row for eventual reuse.
|
||||
*/
|
||||
release(row: IRow): void {
|
||||
if (!row) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.releaseRow(row);
|
||||
}
|
||||
|
||||
private releaseRow(row: IRow): void {
|
||||
const { domNode, templateId } = row;
|
||||
removeClass(domNode, 'scrolling');
|
||||
removeFromParent(domNode);
|
||||
|
||||
const cache = this.getTemplateCache(templateId);
|
||||
cache.push(row);
|
||||
}
|
||||
|
||||
private getTemplateCache(templateId: string): IRow[] {
|
||||
return this.cache[templateId] || (this.cache[templateId] = []);
|
||||
}
|
||||
|
||||
private garbageCollect(): void {
|
||||
if (this.cache) {
|
||||
Object.keys(this.cache).forEach(templateId => {
|
||||
this.cache[templateId].forEach(cachedRow => {
|
||||
const renderer = this.renderers[templateId];
|
||||
renderer.disposeTemplate(cachedRow.templateData);
|
||||
cachedRow.domNode = null;
|
||||
cachedRow.templateData = null;
|
||||
});
|
||||
|
||||
delete this.cache[templateId];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.garbageCollect();
|
||||
this.cache = null;
|
||||
this.renderers = null;
|
||||
}
|
||||
}
|
||||
141
src/vs/base/browser/ui/menu/menu.css
Normal file
@@ -0,0 +1,141 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical .actions-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical .action-item {
|
||||
padding: 0;
|
||||
-ms-transform: none;
|
||||
-webkit-transform: none;
|
||||
-moz-transform: none;
|
||||
-o-transform: none;
|
||||
transform: none;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical .action-item.active {
|
||||
-ms-transform: none;
|
||||
-webkit-transform: none;
|
||||
-moz-transform: none;
|
||||
-o-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical .action-item.focused {
|
||||
background-color: #E4E4E4;
|
||||
}
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical .action-item:hover:not(.disabled) {
|
||||
background-color: #EEE;
|
||||
}
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical .action-label {
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
text-decoration: none;
|
||||
padding: 0.8em 1em;
|
||||
line-height: 1.1em;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical .keybinding {
|
||||
display: inline-block;
|
||||
-ms-flex: 2 1 auto;
|
||||
flex: 2 1 auto;
|
||||
padding: 0.8em 1em;
|
||||
line-height: 1.1em;
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator) {
|
||||
display: inline-block;
|
||||
-webkit-box-sizing: border-box;
|
||||
-o-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical .action-label.separator {
|
||||
padding: 0.5em 0 0 0;
|
||||
margin-bottom: 0.5em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical .action-label.separator.text {
|
||||
padding: 0.7em 1em 0.1em 1em;
|
||||
font-weight: bold;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical .action-label:hover {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Context Menu */
|
||||
|
||||
.context-view.monaco-menu-container {
|
||||
font-family: "Segoe WPC", "Segoe UI", ".SFNSDisplay-Light", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback";
|
||||
outline: 0;
|
||||
box-shadow: 0 2px 8px #A8A8A8;
|
||||
border: none;
|
||||
color: #646465;
|
||||
background-color: white;
|
||||
-webkit-animation: fadeIn 0.083s linear;
|
||||
-o-animation: fadeIn 0.083s linear;
|
||||
-moz-animation: fadeIn 0.083s linear;
|
||||
-ms-animation: fadeIn 0.083s linear;
|
||||
animation: fadeIn 0.083s linear;
|
||||
}
|
||||
|
||||
.context-view.monaco-menu-container :focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
/* Dark theme */
|
||||
.vs-dark .monaco-menu .monaco-action-bar.vertical .action-item.focused {
|
||||
background-color: #4B4C4D;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-menu .monaco-action-bar.vertical .action-item:hover:not(.disabled) {
|
||||
background-color: #3A3A3A;
|
||||
}
|
||||
|
||||
.vs-dark .context-view.monaco-menu-container {
|
||||
box-shadow: 0 2px 8px #000;
|
||||
color: #BBB;
|
||||
background-color: #2D2F31;
|
||||
}
|
||||
|
||||
/* High Contrast Theming */
|
||||
.hc-black .context-view.monaco-menu-container {
|
||||
border: 2px solid #6FC3DF;
|
||||
color: white;
|
||||
background-color: #0C141F;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.hc-black .monaco-menu .monaco-action-bar.vertical .action-item.focused {
|
||||
background: none;
|
||||
border: 1px dotted #f38518;
|
||||
}
|
||||
|
||||
.hc-black .monaco-menu .monaco-action-bar.vertical .action-item:hover:not(.disabled) {
|
||||
background: none;
|
||||
border: 1px dashed #f38518;
|
||||
}
|
||||
65
src/vs/base/browser/ui/menu/menu.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./menu';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { $ } from 'vs/base/browser/builder';
|
||||
import { IActionRunner, IAction } from 'vs/base/common/actions';
|
||||
import { ActionBar, IActionItemProvider, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
|
||||
export interface IMenuOptions {
|
||||
context?: any;
|
||||
actionItemProvider?: IActionItemProvider;
|
||||
actionRunner?: IActionRunner;
|
||||
getKeyBinding?: (action: IAction) => ResolvedKeybinding;
|
||||
}
|
||||
|
||||
export class Menu extends EventEmitter {
|
||||
|
||||
private actionBar: ActionBar;
|
||||
private listener: IDisposable;
|
||||
|
||||
constructor(container: HTMLElement, actions: IAction[], options: IMenuOptions = {}) {
|
||||
super();
|
||||
|
||||
$(container).addClass('monaco-menu-container');
|
||||
|
||||
let $menu = $('.monaco-menu').appendTo(container);
|
||||
|
||||
this.actionBar = new ActionBar($menu, {
|
||||
orientation: ActionsOrientation.VERTICAL,
|
||||
actionItemProvider: options.actionItemProvider,
|
||||
context: options.context,
|
||||
actionRunner: options.actionRunner,
|
||||
isMenu: true
|
||||
});
|
||||
|
||||
this.listener = this.addEmitter(this.actionBar);
|
||||
|
||||
this.actionBar.push(actions, { icon: true, label: true });
|
||||
}
|
||||
|
||||
public focus() {
|
||||
this.actionBar.focus(true);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
super.dispose();
|
||||
|
||||
if (this.actionBar) {
|
||||
this.actionBar.dispose();
|
||||
this.actionBar = null;
|
||||
}
|
||||
|
||||
if (this.listener) {
|
||||
this.listener.dispose();
|
||||
this.listener = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/vs/base/browser/ui/octiconLabel/octiconLabel.mock.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import octiconLabel = require('vs/base/browser/ui/octiconLabel/octiconLabel');
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
|
||||
function expand(text: string): string {
|
||||
return text;
|
||||
}
|
||||
|
||||
class MockOcticonLabel {
|
||||
|
||||
private _container: HTMLElement;
|
||||
|
||||
constructor(container: HTMLElement) {
|
||||
this._container = container;
|
||||
}
|
||||
|
||||
set text(text: string) {
|
||||
let innerHTML = text || '';
|
||||
innerHTML = escape(innerHTML);
|
||||
innerHTML = expand(innerHTML);
|
||||
this._container.innerHTML = innerHTML;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var mock: typeof octiconLabel = {
|
||||
expand: expand,
|
||||
OcticonLabel: <any>MockOcticonLabel
|
||||
};
|
||||
export = mock;
|
||||
35
src/vs/base/browser/ui/octiconLabel/octiconLabel.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./octicons/octicons';
|
||||
import 'vs/css!./octicons/octicons-animations';
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
|
||||
export 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 class OcticonLabel {
|
||||
|
||||
private _container: HTMLElement;
|
||||
|
||||
constructor(container: HTMLElement) {
|
||||
this._container = container;
|
||||
}
|
||||
|
||||
set text(text: string) {
|
||||
let innerHTML = text || '';
|
||||
innerHTML = escape(innerHTML);
|
||||
innerHTML = expand(innerHTML);
|
||||
this._container.innerHTML = innerHTML;
|
||||
}
|
||||
|
||||
set title(title: string) {
|
||||
this._container.title = title;
|
||||
}
|
||||
}
|
||||
124
src/vs/base/browser/ui/octiconLabel/octicons/OSSREADME.json
Normal file
@@ -0,0 +1,124 @@
|
||||
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
|
||||
|
||||
[{
|
||||
"name": "octicons-code",
|
||||
"repositoryURL": "https://octicons.github.com",
|
||||
"version": "3.1.0",
|
||||
"license": "MIT",
|
||||
"licenseDetail": [
|
||||
"The MIT License",
|
||||
"",
|
||||
"(c) 2012-2015 GitHub",
|
||||
"",
|
||||
"Permission is hereby granted, free of charge, to any person obtaining a copy",
|
||||
"of this software and associated documentation files (the \"Software\"), to deal",
|
||||
"in the Software without restriction, including without limitation the rights",
|
||||
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
|
||||
"copies of the Software, and to permit persons to whom the Software is",
|
||||
"furnished to do so, subject to the following conditions:",
|
||||
"",
|
||||
"The above copyright notice and this permission notice shall be included in",
|
||||
"all copies or substantial portions of the Software.",
|
||||
"",
|
||||
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
|
||||
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
|
||||
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
|
||||
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
|
||||
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
|
||||
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN",
|
||||
"THE SOFTWARE."
|
||||
]
|
||||
},{
|
||||
"name": "octicons-font",
|
||||
"repositoryURL": "https://octicons.github.com",
|
||||
"version": "3.1.0",
|
||||
"license": "SIL OFL 1.1",
|
||||
"licenseDetail": [
|
||||
"(c) 2012-2015 GitHub",
|
||||
"",
|
||||
"SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007",
|
||||
"",
|
||||
"PREAMBLE",
|
||||
"The goals of the Open Font License (OFL) are to stimulate worldwide",
|
||||
"development of collaborative font projects, to support the font creation",
|
||||
"efforts of academic and linguistic communities, and to provide a free and",
|
||||
"open framework in which fonts may be shared and improved in partnership",
|
||||
"with others.",
|
||||
"",
|
||||
"The OFL allows the licensed fonts to be used, studied, modified and",
|
||||
"redistributed freely as long as they are not sold by themselves. The",
|
||||
"fonts, including any derivative works, can be bundled, embedded,",
|
||||
"redistributed and/or sold with any software provided that any reserved",
|
||||
"names are not used by derivative works. The fonts and derivatives,",
|
||||
"however, cannot be released under any other type of license. The",
|
||||
"requirement for fonts to remain under this license does not apply",
|
||||
"to any document created using the fonts or their derivatives.",
|
||||
"",
|
||||
"DEFINITIONS",
|
||||
"\"Font Software\" refers to the set of files released by the Copyright",
|
||||
"Holder(s) under this license and clearly marked as such. This may",
|
||||
"include source files, build scripts and documentation.",
|
||||
"",
|
||||
"\"Reserved Font Name\" refers to any names specified as such after the",
|
||||
"copyright statement(s).",
|
||||
"",
|
||||
"\"Original Version\" refers to the collection of Font Software components as",
|
||||
"distributed by the Copyright Holder(s).",
|
||||
"",
|
||||
"\"Modified Version\" refers to any derivative made by adding to, deleting,",
|
||||
"or substituting -- in part or in whole -- any of the components of the",
|
||||
"Original Version, by changing formats or by porting the Font Software to a",
|
||||
"new environment.",
|
||||
"",
|
||||
"\"Author\" refers to any designer, engineer, programmer, technical",
|
||||
"writer or other person who contributed to the Font Software.",
|
||||
"",
|
||||
"PERMISSION & CONDITIONS",
|
||||
"Permission is hereby granted, free of charge, to any person obtaining",
|
||||
"a copy of the Font Software, to use, study, copy, merge, embed, modify,",
|
||||
"redistribute, and sell modified and unmodified copies of the Font",
|
||||
"Software, subject to the following conditions:",
|
||||
"",
|
||||
"1) Neither the Font Software nor any of its individual components,",
|
||||
"in Original or Modified Versions, may be sold by itself.",
|
||||
"",
|
||||
"2) Original or Modified Versions of the Font Software may be bundled,",
|
||||
"redistributed and/or sold with any software, provided that each copy",
|
||||
"contains the above copyright notice and this license. These can be",
|
||||
"included either as stand-alone text files, human-readable headers or",
|
||||
"in the appropriate machine-readable metadata fields within text or",
|
||||
"binary files as long as those fields can be easily viewed by the user.",
|
||||
"",
|
||||
"3) No Modified Version of the Font Software may use the Reserved Font",
|
||||
"Name(s) unless explicit written permission is granted by the corresponding",
|
||||
"Copyright Holder. This restriction only applies to the primary font name as",
|
||||
"presented to the users.",
|
||||
"",
|
||||
"4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font",
|
||||
"Software shall not be used to promote, endorse or advertise any",
|
||||
"Modified Version, except to acknowledge the contribution(s) of the",
|
||||
"Copyright Holder(s) and the Author(s) or with their explicit written",
|
||||
"permission.",
|
||||
"",
|
||||
"5) The Font Software, modified or unmodified, in part or in whole,",
|
||||
"must be distributed entirely under this license, and must not be",
|
||||
"distributed under any other license. The requirement for fonts to",
|
||||
"remain under this license does not apply to any document created",
|
||||
"using the Font Software.",
|
||||
"",
|
||||
"TERMINATION",
|
||||
"This license becomes null and void if any of the above conditions are",
|
||||
"not met.",
|
||||
"",
|
||||
"DISCLAIMER",
|
||||
"THE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,",
|
||||
"EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF",
|
||||
"MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT",
|
||||
"OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE",
|
||||
"COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,",
|
||||
"INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL",
|
||||
"DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING",
|
||||
"FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM",
|
||||
"OTHER DEALINGS IN THE FONT SOFTWARE."
|
||||
]
|
||||
}]
|
||||
1
src/vs/base/browser/ui/octiconLabel/octicons/README.md
Normal file
@@ -0,0 +1 @@
|
||||
If you intend to install Octicons locally, install `octicons-local.ttf`. It should appear as “github-octicons” in your font list. It is specially designed not to conflict with GitHub's web fonts.
|
||||
@@ -0,0 +1,14 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
@keyframes octicon-spin {
|
||||
100% {
|
||||
transform:rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.octicon-animation-spin {
|
||||
animation: octicon-spin 2s linear infinite;
|
||||
}
|
||||
BIN
src/vs/base/browser/ui/octiconLabel/octicons/octicons-local.ttf
Normal file
233
src/vs/base/browser/ui/octiconLabel/octicons/octicons.css
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
/*! *****************************************************************************
|
||||
(c) 2012-2015 GitHub
|
||||
|
||||
When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos)
|
||||
|
||||
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/)
|
||||
Applies to all other files
|
||||
***************************************************************************** */
|
||||
|
||||
@font-face {
|
||||
font-family: 'octicons';
|
||||
src: url('octicons.eot?#iefix') format('embedded-opentype'),
|
||||
url('octicons.woff') format('woff'),
|
||||
url('octicons.ttf') format('truetype'),
|
||||
url('octicons.svg#octicons') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
.octicon is optimized for 16px.
|
||||
.mega-octicon is optimized for 32px but can be used larger.
|
||||
|
||||
*/
|
||||
.octicon, .mega-octicon {
|
||||
font: normal normal normal 16px/1 octicons;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.mega-octicon { font-size: 32px; }
|
||||
|
||||
.octicon-alert:before { content: '\f02d'} /* */
|
||||
.octicon-arrow-down:before { content: '\f03f'} /* */
|
||||
.octicon-arrow-left:before { content: '\f040'} /* */
|
||||
.octicon-arrow-right:before { content: '\f03e'} /* */
|
||||
.octicon-arrow-small-down:before { content: '\f0a0'} /* */
|
||||
.octicon-arrow-small-left:before { content: '\f0a1'} /* */
|
||||
.octicon-arrow-small-right:before { content: '\f071'} /* */
|
||||
.octicon-arrow-small-up:before { content: '\f09f'} /* */
|
||||
.octicon-arrow-up:before { content: '\f03d'} /* */
|
||||
.octicon-microscope:before,
|
||||
.octicon-beaker:before { content: '\f0dd'} /* */
|
||||
.octicon-bell:before { content: '\f0de'} /* */
|
||||
.octicon-book:before { content: '\f007'} /* */
|
||||
.octicon-bookmark:before { content: '\f07b'} /* */
|
||||
.octicon-briefcase:before { content: '\f0d3'} /* */
|
||||
.octicon-broadcast:before { content: '\f048'} /* */
|
||||
.octicon-browser:before { content: '\f0c5'} /* */
|
||||
.octicon-bug:before { content: '\f091'} /* */
|
||||
.octicon-calendar:before { content: '\f068'} /* */
|
||||
.octicon-check:before { content: '\f03a'} /* */
|
||||
.octicon-checklist:before { content: '\f076'} /* */
|
||||
.octicon-chevron-down:before { content: '\f0a3'} /* */
|
||||
.octicon-chevron-left:before { content: '\f0a4'} /* */
|
||||
.octicon-chevron-right:before { content: '\f078'} /* */
|
||||
.octicon-chevron-up:before { content: '\f0a2'} /* */
|
||||
.octicon-circle-slash:before { content: '\f084'} /* */
|
||||
.octicon-circuit-board:before { content: '\f0d6'} /* */
|
||||
.octicon-clippy:before { content: '\f035'} /* */
|
||||
.octicon-clock:before { content: '\f046'} /* */
|
||||
.octicon-cloud-download:before { content: '\f00b'} /* */
|
||||
.octicon-cloud-upload:before { content: '\f00c'} /* */
|
||||
.octicon-code:before { content: '\f05f'} /* */
|
||||
.octicon-color-mode:before { content: '\f065'} /* */
|
||||
.octicon-comment-add:before,
|
||||
.octicon-comment:before { content: '\f02b'} /* */
|
||||
.octicon-comment-discussion:before { content: '\f04f'} /* */
|
||||
.octicon-credit-card:before { content: '\f045'} /* */
|
||||
.octicon-dash:before { content: '\f0ca'} /* */
|
||||
.octicon-dashboard:before { content: '\f07d'} /* */
|
||||
.octicon-database:before { content: '\f096'} /* */
|
||||
.octicon-clone:before,
|
||||
.octicon-desktop-download:before { content: '\f0dc'} /* */
|
||||
.octicon-device-camera:before { content: '\f056'} /* */
|
||||
.octicon-device-camera-video:before { content: '\f057'} /* */
|
||||
.octicon-device-desktop:before { content: '\f27c'} /* */
|
||||
.octicon-device-mobile:before { content: '\f038'} /* */
|
||||
.octicon-diff:before { content: '\f04d'} /* */
|
||||
.octicon-diff-added:before { content: '\f06b'} /* */
|
||||
.octicon-diff-ignored:before { content: '\f099'} /* */
|
||||
.octicon-diff-modified:before { content: '\f06d'} /* */
|
||||
.octicon-diff-removed:before { content: '\f06c'} /* */
|
||||
.octicon-diff-renamed:before { content: '\f06e'} /* */
|
||||
.octicon-ellipsis:before { content: '\f09a'} /* */
|
||||
.octicon-eye-unwatch:before,
|
||||
.octicon-eye-watch:before,
|
||||
.octicon-eye:before { content: '\f04e'} /* */
|
||||
.octicon-file-binary:before { content: '\f094'} /* */
|
||||
.octicon-file-code:before { content: '\f010'} /* */
|
||||
.octicon-file-directory:before { content: '\f016'} /* */
|
||||
.octicon-file-media:before { content: '\f012'} /* */
|
||||
.octicon-file-pdf:before { content: '\f014'} /* */
|
||||
.octicon-file-submodule:before { content: '\f017'} /* */
|
||||
.octicon-file-symlink-directory:before { content: '\f0b1'} /* */
|
||||
.octicon-file-symlink-file:before { content: '\f0b0'} /* */
|
||||
.octicon-file-text:before { content: '\f011'} /* */
|
||||
.octicon-file-zip:before { content: '\f013'} /* */
|
||||
.octicon-flame:before { content: '\f0d2'} /* */
|
||||
.octicon-fold:before { content: '\f0cc'} /* */
|
||||
.octicon-gear:before { content: '\f02f'} /* */
|
||||
.octicon-gift:before { content: '\f042'} /* */
|
||||
.octicon-gist:before { content: '\f00e'} /* */
|
||||
.octicon-gist-secret:before { content: '\f08c'} /* */
|
||||
.octicon-git-branch-create:before,
|
||||
.octicon-git-branch-delete:before,
|
||||
.octicon-git-branch:before { content: '\f020'} /* */
|
||||
.octicon-git-commit:before { content: '\f01f'} /* */
|
||||
.octicon-git-compare:before { content: '\f0ac'} /* */
|
||||
.octicon-git-merge:before { content: '\f023'} /* */
|
||||
.octicon-git-pull-request-abandoned:before,
|
||||
.octicon-git-pull-request:before { content: '\f009'} /* */
|
||||
.octicon-globe:before { content: '\f0b6'} /* */
|
||||
.octicon-graph:before { content: '\f043'} /* */
|
||||
.octicon-heart:before { content: '\2665'} /* ♥ */
|
||||
.octicon-history:before { content: '\f07e'} /* */
|
||||
.octicon-home:before { content: '\f08d'} /* */
|
||||
.octicon-horizontal-rule:before { content: '\f070'} /* */
|
||||
.octicon-hubot:before { content: '\f09d'} /* */
|
||||
.octicon-inbox:before { content: '\f0cf'} /* */
|
||||
.octicon-info:before { content: '\f059'} /* */
|
||||
.octicon-issue-closed:before { content: '\f028'} /* */
|
||||
.octicon-issue-opened:before { content: '\f026'} /* */
|
||||
.octicon-issue-reopened:before { content: '\f027'} /* */
|
||||
.octicon-jersey:before { content: '\f019'} /* */
|
||||
.octicon-key:before { content: '\f049'} /* */
|
||||
.octicon-keyboard:before { content: '\f00d'} /* */
|
||||
.octicon-law:before { content: '\f0d8'} /* */
|
||||
.octicon-light-bulb:before { content: '\f000'} /* */
|
||||
.octicon-link:before { content: '\f05c'} /* */
|
||||
.octicon-link-external:before { content: '\f07f'} /* */
|
||||
.octicon-list-ordered:before { content: '\f062'} /* */
|
||||
.octicon-list-unordered:before { content: '\f061'} /* */
|
||||
.octicon-location:before { content: '\f060'} /* */
|
||||
.octicon-gist-private:before,
|
||||
.octicon-mirror-private:before,
|
||||
.octicon-git-fork-private:before,
|
||||
.octicon-lock:before { content: '\f06a'} /* */
|
||||
.octicon-logo-github:before { content: '\f092'} /* */
|
||||
.octicon-mail:before { content: '\f03b'} /* */
|
||||
.octicon-mail-read:before { content: '\f03c'} /* */
|
||||
.octicon-mail-reply:before { content: '\f051'} /* */
|
||||
.octicon-mark-github:before { content: '\f00a'} /* */
|
||||
.octicon-markdown:before { content: '\f0c9'} /* */
|
||||
.octicon-megaphone:before { content: '\f077'} /* */
|
||||
.octicon-mention:before { content: '\f0be'} /* */
|
||||
.octicon-milestone:before { content: '\f075'} /* */
|
||||
.octicon-mirror-public:before,
|
||||
.octicon-mirror:before { content: '\f024'} /* */
|
||||
.octicon-mortar-board:before { content: '\f0d7'} /* */
|
||||
.octicon-mute:before { content: '\f080'} /* */
|
||||
.octicon-no-newline:before { content: '\f09c'} /* */
|
||||
.octicon-octoface:before { content: '\f008'} /* */
|
||||
.octicon-organization:before { content: '\f037'} /* */
|
||||
.octicon-package:before { content: '\f0c4'} /* */
|
||||
.octicon-paintcan:before { content: '\f0d1'} /* */
|
||||
.octicon-pencil:before { content: '\f058'} /* */
|
||||
.octicon-person-add:before,
|
||||
.octicon-person-follow:before,
|
||||
.octicon-person:before { content: '\f018'} /* */
|
||||
.octicon-pin:before { content: '\f041'} /* */
|
||||
.octicon-plug:before { content: '\f0d4'} /* */
|
||||
.octicon-repo-create:before,
|
||||
.octicon-gist-new:before,
|
||||
.octicon-file-directory-create:before,
|
||||
.octicon-file-add:before,
|
||||
.octicon-plus:before { content: '\f05d'} /* */
|
||||
.octicon-primitive-dot:before { content: '\f052'} /* */
|
||||
.octicon-primitive-square:before { content: '\f053'} /* */
|
||||
.octicon-pulse:before { content: '\f085'} /* */
|
||||
.octicon-question:before { content: '\f02c'} /* */
|
||||
.octicon-quote:before { content: '\f063'} /* */
|
||||
.octicon-radio-tower:before { content: '\f030'} /* */
|
||||
.octicon-repo-delete:before,
|
||||
.octicon-repo:before { content: '\f001'} /* */
|
||||
.octicon-repo-clone:before { content: '\f04c'} /* */
|
||||
.octicon-repo-force-push:before { content: '\f04a'} /* */
|
||||
.octicon-gist-fork:before,
|
||||
.octicon-repo-forked:before { content: '\f002'} /* */
|
||||
.octicon-repo-pull:before { content: '\f006'} /* */
|
||||
.octicon-repo-push:before { content: '\f005'} /* */
|
||||
.octicon-rocket:before { content: '\f033'} /* */
|
||||
.octicon-rss:before { content: '\f034'} /* */
|
||||
.octicon-ruby:before { content: '\f047'} /* */
|
||||
.octicon-screen-full:before { content: '\f066'} /* */
|
||||
.octicon-screen-normal:before { content: '\f067'} /* */
|
||||
.octicon-search-save:before,
|
||||
.octicon-search:before { content: '\f02e'} /* */
|
||||
.octicon-server:before { content: '\f097'} /* */
|
||||
.octicon-settings:before { content: '\f07c'} /* */
|
||||
.octicon-shield:before { content: '\f0e1'} /* */
|
||||
.octicon-log-in:before,
|
||||
.octicon-sign-in:before { content: '\f036'} /* */
|
||||
.octicon-log-out:before,
|
||||
.octicon-sign-out:before { content: '\f032'} /* */
|
||||
.octicon-squirrel:before { content: '\f0b2'} /* */
|
||||
.octicon-star-add:before,
|
||||
.octicon-star-delete:before,
|
||||
.octicon-star:before { content: '\f02a'} /* */
|
||||
.octicon-stop:before { content: '\f08f'} /* */
|
||||
.octicon-repo-sync:before,
|
||||
.octicon-sync:before { content: '\f087'} /* */
|
||||
.octicon-tag-remove:before,
|
||||
.octicon-tag-add:before,
|
||||
.octicon-tag:before { content: '\f015'} /* */
|
||||
.octicon-telescope:before { content: '\f088'} /* */
|
||||
.octicon-terminal:before { content: '\f0c8'} /* */
|
||||
.octicon-three-bars:before { content: '\f05e'} /* */
|
||||
.octicon-thumbsdown:before { content: '\f0db'} /* */
|
||||
.octicon-thumbsup:before { content: '\f0da'} /* */
|
||||
.octicon-tools:before { content: '\f031'} /* */
|
||||
.octicon-trashcan:before { content: '\f0d0'} /* */
|
||||
.octicon-triangle-down:before { content: '\f05b'} /* */
|
||||
.octicon-triangle-left:before { content: '\f044'} /* */
|
||||
.octicon-triangle-right:before { content: '\f05a'} /* */
|
||||
.octicon-triangle-up:before { content: '\f0aa'} /* */
|
||||
.octicon-unfold:before { content: '\f039'} /* */
|
||||
.octicon-unmute:before { content: '\f0ba'} /* */
|
||||
.octicon-versions:before { content: '\f064'} /* */
|
||||
.octicon-watch:before { content: '\f0e0'} /* */
|
||||
.octicon-remove-close:before,
|
||||
.octicon-x:before { content: '\f081'} /* */
|
||||
.octicon-zap:before { content: '\26A1'} /* ⚡ */
|
||||
BIN
src/vs/base/browser/ui/octiconLabel/octicons/octicons.eot
Normal file
220
src/vs/base/browser/ui/octiconLabel/octicons/octicons.less
Normal file
@@ -0,0 +1,220 @@
|
||||
@octicons-font-path: ".";
|
||||
@octicons-version: "396334ee3da78f4302d25c758ae3e3ce5dc3c97d";
|
||||
|
||||
@font-face {
|
||||
font-family: 'octicons';
|
||||
src: ~"url('@{octicons-font-path}/octicons.eot?#iefix&v=@{octicons-version}') format('embedded-opentype')",
|
||||
~"url('@{octicons-font-path}/octicons.woff?v=@{octicons-version}') format('woff')",
|
||||
~"url('@{octicons-font-path}/octicons.ttf?v=@{octicons-version}') format('truetype')",
|
||||
~"url('@{octicons-font-path}/octicons.svg?v=@{octicons-version}#octicons') format('svg')";
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
// .octicon is optimized for 16px.
|
||||
// .mega-octicon is optimized for 32px but can be used larger.
|
||||
.octicon, .mega-octicon {
|
||||
font: normal normal normal 16px/1 octicons;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.mega-octicon { font-size: 32px; }
|
||||
|
||||
.octicon-alert:before { content: '\f02d'} /* */
|
||||
.octicon-arrow-down:before { content: '\f03f'} /* */
|
||||
.octicon-arrow-left:before { content: '\f040'} /* */
|
||||
.octicon-arrow-right:before { content: '\f03e'} /* */
|
||||
.octicon-arrow-small-down:before { content: '\f0a0'} /* */
|
||||
.octicon-arrow-small-left:before { content: '\f0a1'} /* */
|
||||
.octicon-arrow-small-right:before { content: '\f071'} /* */
|
||||
.octicon-arrow-small-up:before { content: '\f09f'} /* */
|
||||
.octicon-arrow-up:before { content: '\f03d'} /* */
|
||||
.octicon-microscope:before,
|
||||
.octicon-beaker:before { content: '\f0dd'} /* */
|
||||
.octicon-bell:before { content: '\f0de'} /* */
|
||||
.octicon-book:before { content: '\f007'} /* */
|
||||
.octicon-bookmark:before { content: '\f07b'} /* */
|
||||
.octicon-briefcase:before { content: '\f0d3'} /* */
|
||||
.octicon-broadcast:before { content: '\f048'} /* */
|
||||
.octicon-browser:before { content: '\f0c5'} /* */
|
||||
.octicon-bug:before { content: '\f091'} /* */
|
||||
.octicon-calendar:before { content: '\f068'} /* */
|
||||
.octicon-check:before { content: '\f03a'} /* */
|
||||
.octicon-checklist:before { content: '\f076'} /* */
|
||||
.octicon-chevron-down:before { content: '\f0a3'} /* */
|
||||
.octicon-chevron-left:before { content: '\f0a4'} /* */
|
||||
.octicon-chevron-right:before { content: '\f078'} /* */
|
||||
.octicon-chevron-up:before { content: '\f0a2'} /* */
|
||||
.octicon-circle-slash:before { content: '\f084'} /* */
|
||||
.octicon-circuit-board:before { content: '\f0d6'} /* */
|
||||
.octicon-clippy:before { content: '\f035'} /* */
|
||||
.octicon-clock:before { content: '\f046'} /* */
|
||||
.octicon-cloud-download:before { content: '\f00b'} /* */
|
||||
.octicon-cloud-upload:before { content: '\f00c'} /* */
|
||||
.octicon-code:before { content: '\f05f'} /* */
|
||||
.octicon-color-mode:before { content: '\f065'} /* */
|
||||
.octicon-comment-add:before,
|
||||
.octicon-comment:before { content: '\f02b'} /* */
|
||||
.octicon-comment-discussion:before { content: '\f04f'} /* */
|
||||
.octicon-credit-card:before { content: '\f045'} /* */
|
||||
.octicon-dash:before { content: '\f0ca'} /* */
|
||||
.octicon-dashboard:before { content: '\f07d'} /* */
|
||||
.octicon-database:before { content: '\f096'} /* */
|
||||
.octicon-clone:before,
|
||||
.octicon-desktop-download:before { content: '\f0dc'} /* */
|
||||
.octicon-device-camera:before { content: '\f056'} /* */
|
||||
.octicon-device-camera-video:before { content: '\f057'} /* */
|
||||
.octicon-device-desktop:before { content: '\f27c'} /* */
|
||||
.octicon-device-mobile:before { content: '\f038'} /* */
|
||||
.octicon-diff:before { content: '\f04d'} /* */
|
||||
.octicon-diff-added:before { content: '\f06b'} /* */
|
||||
.octicon-diff-ignored:before { content: '\f099'} /* */
|
||||
.octicon-diff-modified:before { content: '\f06d'} /* */
|
||||
.octicon-diff-removed:before { content: '\f06c'} /* */
|
||||
.octicon-diff-renamed:before { content: '\f06e'} /* */
|
||||
.octicon-ellipsis:before { content: '\f09a'} /* */
|
||||
.octicon-eye-unwatch:before,
|
||||
.octicon-eye-watch:before,
|
||||
.octicon-eye:before { content: '\f04e'} /* */
|
||||
.octicon-file-binary:before { content: '\f094'} /* */
|
||||
.octicon-file-code:before { content: '\f010'} /* */
|
||||
.octicon-file-directory:before { content: '\f016'} /* */
|
||||
.octicon-file-media:before { content: '\f012'} /* */
|
||||
.octicon-file-pdf:before { content: '\f014'} /* */
|
||||
.octicon-file-submodule:before { content: '\f017'} /* */
|
||||
.octicon-file-symlink-directory:before { content: '\f0b1'} /* */
|
||||
.octicon-file-symlink-file:before { content: '\f0b0'} /* */
|
||||
.octicon-file-text:before { content: '\f011'} /* */
|
||||
.octicon-file-zip:before { content: '\f013'} /* */
|
||||
.octicon-flame:before { content: '\f0d2'} /* */
|
||||
.octicon-fold:before { content: '\f0cc'} /* */
|
||||
.octicon-gear:before { content: '\f02f'} /* */
|
||||
.octicon-gift:before { content: '\f042'} /* */
|
||||
.octicon-gist:before { content: '\f00e'} /* */
|
||||
.octicon-gist-secret:before { content: '\f08c'} /* */
|
||||
.octicon-git-branch-create:before,
|
||||
.octicon-git-branch-delete:before,
|
||||
.octicon-git-branch:before { content: '\f020'} /* */
|
||||
.octicon-git-commit:before { content: '\f01f'} /* */
|
||||
.octicon-git-compare:before { content: '\f0ac'} /* */
|
||||
.octicon-git-merge:before { content: '\f023'} /* */
|
||||
.octicon-git-pull-request-abandoned:before,
|
||||
.octicon-git-pull-request:before { content: '\f009'} /* */
|
||||
.octicon-globe:before { content: '\f0b6'} /* */
|
||||
.octicon-graph:before { content: '\f043'} /* */
|
||||
.octicon-heart:before { content: '\2665'} /* ♥ */
|
||||
.octicon-history:before { content: '\f07e'} /* */
|
||||
.octicon-home:before { content: '\f08d'} /* */
|
||||
.octicon-horizontal-rule:before { content: '\f070'} /* */
|
||||
.octicon-hubot:before { content: '\f09d'} /* */
|
||||
.octicon-inbox:before { content: '\f0cf'} /* */
|
||||
.octicon-info:before { content: '\f059'} /* */
|
||||
.octicon-issue-closed:before { content: '\f028'} /* */
|
||||
.octicon-issue-opened:before { content: '\f026'} /* */
|
||||
.octicon-issue-reopened:before { content: '\f027'} /* */
|
||||
.octicon-jersey:before { content: '\f019'} /* */
|
||||
.octicon-key:before { content: '\f049'} /* */
|
||||
.octicon-keyboard:before { content: '\f00d'} /* */
|
||||
.octicon-law:before { content: '\f0d8'} /* */
|
||||
.octicon-light-bulb:before { content: '\f000'} /* */
|
||||
.octicon-link:before { content: '\f05c'} /* */
|
||||
.octicon-link-external:before { content: '\f07f'} /* */
|
||||
.octicon-list-ordered:before { content: '\f062'} /* */
|
||||
.octicon-list-unordered:before { content: '\f061'} /* */
|
||||
.octicon-location:before { content: '\f060'} /* */
|
||||
.octicon-gist-private:before,
|
||||
.octicon-mirror-private:before,
|
||||
.octicon-git-fork-private:before,
|
||||
.octicon-lock:before { content: '\f06a'} /* */
|
||||
.octicon-logo-github:before { content: '\f092'} /* */
|
||||
.octicon-mail:before { content: '\f03b'} /* */
|
||||
.octicon-mail-read:before { content: '\f03c'} /* */
|
||||
.octicon-mail-reply:before { content: '\f051'} /* */
|
||||
.octicon-mark-github:before { content: '\f00a'} /* */
|
||||
.octicon-markdown:before { content: '\f0c9'} /* */
|
||||
.octicon-megaphone:before { content: '\f077'} /* */
|
||||
.octicon-mention:before { content: '\f0be'} /* */
|
||||
.octicon-milestone:before { content: '\f075'} /* */
|
||||
.octicon-mirror-public:before,
|
||||
.octicon-mirror:before { content: '\f024'} /* */
|
||||
.octicon-mortar-board:before { content: '\f0d7'} /* */
|
||||
.octicon-mute:before { content: '\f080'} /* */
|
||||
.octicon-no-newline:before { content: '\f09c'} /* */
|
||||
.octicon-octoface:before { content: '\f008'} /* */
|
||||
.octicon-organization:before { content: '\f037'} /* */
|
||||
.octicon-package:before { content: '\f0c4'} /* */
|
||||
.octicon-paintcan:before { content: '\f0d1'} /* */
|
||||
.octicon-pencil:before { content: '\f058'} /* */
|
||||
.octicon-person-add:before,
|
||||
.octicon-person-follow:before,
|
||||
.octicon-person:before { content: '\f018'} /* */
|
||||
.octicon-pin:before { content: '\f041'} /* */
|
||||
.octicon-plug:before { content: '\f0d4'} /* */
|
||||
.octicon-repo-create:before,
|
||||
.octicon-gist-new:before,
|
||||
.octicon-file-directory-create:before,
|
||||
.octicon-file-add:before,
|
||||
.octicon-plus:before { content: '\f05d'} /* */
|
||||
.octicon-primitive-dot:before { content: '\f052'} /* */
|
||||
.octicon-primitive-square:before { content: '\f053'} /* */
|
||||
.octicon-pulse:before { content: '\f085'} /* */
|
||||
.octicon-question:before { content: '\f02c'} /* */
|
||||
.octicon-quote:before { content: '\f063'} /* */
|
||||
.octicon-radio-tower:before { content: '\f030'} /* */
|
||||
.octicon-repo-delete:before,
|
||||
.octicon-repo:before { content: '\f001'} /* */
|
||||
.octicon-repo-clone:before { content: '\f04c'} /* */
|
||||
.octicon-repo-force-push:before { content: '\f04a'} /* */
|
||||
.octicon-gist-fork:before,
|
||||
.octicon-repo-forked:before { content: '\f002'} /* */
|
||||
.octicon-repo-pull:before { content: '\f006'} /* */
|
||||
.octicon-repo-push:before { content: '\f005'} /* */
|
||||
.octicon-rocket:before { content: '\f033'} /* */
|
||||
.octicon-rss:before { content: '\f034'} /* */
|
||||
.octicon-ruby:before { content: '\f047'} /* */
|
||||
.octicon-screen-full:before { content: '\f066'} /* */
|
||||
.octicon-screen-normal:before { content: '\f067'} /* */
|
||||
.octicon-search-save:before,
|
||||
.octicon-search:before { content: '\f02e'} /* */
|
||||
.octicon-server:before { content: '\f097'} /* */
|
||||
.octicon-settings:before { content: '\f07c'} /* */
|
||||
.octicon-shield:before { content: '\f0e1'} /* */
|
||||
.octicon-log-in:before,
|
||||
.octicon-sign-in:before { content: '\f036'} /* */
|
||||
.octicon-log-out:before,
|
||||
.octicon-sign-out:before { content: '\f032'} /* */
|
||||
.octicon-squirrel:before { content: '\f0b2'} /* */
|
||||
.octicon-star-add:before,
|
||||
.octicon-star-delete:before,
|
||||
.octicon-star:before { content: '\f02a'} /* */
|
||||
.octicon-stop:before { content: '\f08f'} /* */
|
||||
.octicon-repo-sync:before,
|
||||
.octicon-sync:before { content: '\f087'} /* */
|
||||
.octicon-tag-remove:before,
|
||||
.octicon-tag-add:before,
|
||||
.octicon-tag:before { content: '\f015'} /* */
|
||||
.octicon-telescope:before { content: '\f088'} /* */
|
||||
.octicon-terminal:before { content: '\f0c8'} /* */
|
||||
.octicon-three-bars:before { content: '\f05e'} /* */
|
||||
.octicon-thumbsdown:before { content: '\f0db'} /* */
|
||||
.octicon-thumbsup:before { content: '\f0da'} /* */
|
||||
.octicon-tools:before { content: '\f031'} /* */
|
||||
.octicon-trashcan:before { content: '\f0d0'} /* */
|
||||
.octicon-triangle-down:before { content: '\f05b'} /* */
|
||||
.octicon-triangle-left:before { content: '\f044'} /* */
|
||||
.octicon-triangle-right:before { content: '\f05a'} /* */
|
||||
.octicon-triangle-up:before { content: '\f0aa'} /* */
|
||||
.octicon-unfold:before { content: '\f039'} /* */
|
||||
.octicon-unmute:before { content: '\f0ba'} /* */
|
||||
.octicon-versions:before { content: '\f064'} /* */
|
||||
.octicon-watch:before { content: '\f0e0'} /* */
|
||||
.octicon-remove-close:before,
|
||||
.octicon-x:before { content: '\f081'} /* */
|
||||
.octicon-zap:before { content: '\26A1'} /* ⚡ */
|
||||
220
src/vs/base/browser/ui/octiconLabel/octicons/octicons.scss
Normal file
@@ -0,0 +1,220 @@
|
||||
$octicons-font-path: "." !default;
|
||||
$octicons-version: "396334ee3da78f4302d25c758ae3e3ce5dc3c97d";
|
||||
|
||||
@font-face {
|
||||
font-family: 'octicons';
|
||||
src: url('#{$octicons-font-path}/octicons.eot?#iefix&v=#{$octicons-version}') format('embedded-opentype'),
|
||||
url('#{$octicons-font-path}/octicons.woff?v=#{$octicons-version}') format('woff'),
|
||||
url('#{$octicons-font-path}/octicons.ttf?v=#{$octicons-version}') format('truetype'),
|
||||
url('#{$octicons-font-path}/octicons.svg?v=#{$octicons-version}#octicons') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
// .octicon is optimized for 16px.
|
||||
// .mega-octicon is optimized for 32px but can be used larger.
|
||||
.octicon, .mega-octicon {
|
||||
font: normal normal normal 16px/1 octicons;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.mega-octicon { font-size: 32px; }
|
||||
|
||||
.octicon-alert:before { content: '\f02d'} /* */
|
||||
.octicon-arrow-down:before { content: '\f03f'} /* */
|
||||
.octicon-arrow-left:before { content: '\f040'} /* */
|
||||
.octicon-arrow-right:before { content: '\f03e'} /* */
|
||||
.octicon-arrow-small-down:before { content: '\f0a0'} /* */
|
||||
.octicon-arrow-small-left:before { content: '\f0a1'} /* */
|
||||
.octicon-arrow-small-right:before { content: '\f071'} /* */
|
||||
.octicon-arrow-small-up:before { content: '\f09f'} /* */
|
||||
.octicon-arrow-up:before { content: '\f03d'} /* */
|
||||
.octicon-microscope:before,
|
||||
.octicon-beaker:before { content: '\f0dd'} /* */
|
||||
.octicon-bell:before { content: '\f0de'} /* */
|
||||
.octicon-book:before { content: '\f007'} /* */
|
||||
.octicon-bookmark:before { content: '\f07b'} /* */
|
||||
.octicon-briefcase:before { content: '\f0d3'} /* */
|
||||
.octicon-broadcast:before { content: '\f048'} /* */
|
||||
.octicon-browser:before { content: '\f0c5'} /* */
|
||||
.octicon-bug:before { content: '\f091'} /* */
|
||||
.octicon-calendar:before { content: '\f068'} /* */
|
||||
.octicon-check:before { content: '\f03a'} /* */
|
||||
.octicon-checklist:before { content: '\f076'} /* */
|
||||
.octicon-chevron-down:before { content: '\f0a3'} /* */
|
||||
.octicon-chevron-left:before { content: '\f0a4'} /* */
|
||||
.octicon-chevron-right:before { content: '\f078'} /* */
|
||||
.octicon-chevron-up:before { content: '\f0a2'} /* */
|
||||
.octicon-circle-slash:before { content: '\f084'} /* */
|
||||
.octicon-circuit-board:before { content: '\f0d6'} /* */
|
||||
.octicon-clippy:before { content: '\f035'} /* */
|
||||
.octicon-clock:before { content: '\f046'} /* */
|
||||
.octicon-cloud-download:before { content: '\f00b'} /* */
|
||||
.octicon-cloud-upload:before { content: '\f00c'} /* */
|
||||
.octicon-code:before { content: '\f05f'} /* */
|
||||
.octicon-color-mode:before { content: '\f065'} /* */
|
||||
.octicon-comment-add:before,
|
||||
.octicon-comment:before { content: '\f02b'} /* */
|
||||
.octicon-comment-discussion:before { content: '\f04f'} /* */
|
||||
.octicon-credit-card:before { content: '\f045'} /* */
|
||||
.octicon-dash:before { content: '\f0ca'} /* */
|
||||
.octicon-dashboard:before { content: '\f07d'} /* */
|
||||
.octicon-database:before { content: '\f096'} /* */
|
||||
.octicon-clone:before,
|
||||
.octicon-desktop-download:before { content: '\f0dc'} /* */
|
||||
.octicon-device-camera:before { content: '\f056'} /* */
|
||||
.octicon-device-camera-video:before { content: '\f057'} /* */
|
||||
.octicon-device-desktop:before { content: '\f27c'} /* */
|
||||
.octicon-device-mobile:before { content: '\f038'} /* */
|
||||
.octicon-diff:before { content: '\f04d'} /* */
|
||||
.octicon-diff-added:before { content: '\f06b'} /* */
|
||||
.octicon-diff-ignored:before { content: '\f099'} /* */
|
||||
.octicon-diff-modified:before { content: '\f06d'} /* */
|
||||
.octicon-diff-removed:before { content: '\f06c'} /* */
|
||||
.octicon-diff-renamed:before { content: '\f06e'} /* */
|
||||
.octicon-ellipsis:before { content: '\f09a'} /* */
|
||||
.octicon-eye-unwatch:before,
|
||||
.octicon-eye-watch:before,
|
||||
.octicon-eye:before { content: '\f04e'} /* */
|
||||
.octicon-file-binary:before { content: '\f094'} /* */
|
||||
.octicon-file-code:before { content: '\f010'} /* */
|
||||
.octicon-file-directory:before { content: '\f016'} /* */
|
||||
.octicon-file-media:before { content: '\f012'} /* */
|
||||
.octicon-file-pdf:before { content: '\f014'} /* */
|
||||
.octicon-file-submodule:before { content: '\f017'} /* */
|
||||
.octicon-file-symlink-directory:before { content: '\f0b1'} /* */
|
||||
.octicon-file-symlink-file:before { content: '\f0b0'} /* */
|
||||
.octicon-file-text:before { content: '\f011'} /* */
|
||||
.octicon-file-zip:before { content: '\f013'} /* */
|
||||
.octicon-flame:before { content: '\f0d2'} /* */
|
||||
.octicon-fold:before { content: '\f0cc'} /* */
|
||||
.octicon-gear:before { content: '\f02f'} /* */
|
||||
.octicon-gift:before { content: '\f042'} /* */
|
||||
.octicon-gist:before { content: '\f00e'} /* */
|
||||
.octicon-gist-secret:before { content: '\f08c'} /* */
|
||||
.octicon-git-branch-create:before,
|
||||
.octicon-git-branch-delete:before,
|
||||
.octicon-git-branch:before { content: '\f020'} /* */
|
||||
.octicon-git-commit:before { content: '\f01f'} /* */
|
||||
.octicon-git-compare:before { content: '\f0ac'} /* */
|
||||
.octicon-git-merge:before { content: '\f023'} /* */
|
||||
.octicon-git-pull-request-abandoned:before,
|
||||
.octicon-git-pull-request:before { content: '\f009'} /* */
|
||||
.octicon-globe:before { content: '\f0b6'} /* */
|
||||
.octicon-graph:before { content: '\f043'} /* */
|
||||
.octicon-heart:before { content: '\2665'} /* ♥ */
|
||||
.octicon-history:before { content: '\f07e'} /* */
|
||||
.octicon-home:before { content: '\f08d'} /* */
|
||||
.octicon-horizontal-rule:before { content: '\f070'} /* */
|
||||
.octicon-hubot:before { content: '\f09d'} /* */
|
||||
.octicon-inbox:before { content: '\f0cf'} /* */
|
||||
.octicon-info:before { content: '\f059'} /* */
|
||||
.octicon-issue-closed:before { content: '\f028'} /* */
|
||||
.octicon-issue-opened:before { content: '\f026'} /* */
|
||||
.octicon-issue-reopened:before { content: '\f027'} /* */
|
||||
.octicon-jersey:before { content: '\f019'} /* */
|
||||
.octicon-key:before { content: '\f049'} /* */
|
||||
.octicon-keyboard:before { content: '\f00d'} /* */
|
||||
.octicon-law:before { content: '\f0d8'} /* */
|
||||
.octicon-light-bulb:before { content: '\f000'} /* */
|
||||
.octicon-link:before { content: '\f05c'} /* */
|
||||
.octicon-link-external:before { content: '\f07f'} /* */
|
||||
.octicon-list-ordered:before { content: '\f062'} /* */
|
||||
.octicon-list-unordered:before { content: '\f061'} /* */
|
||||
.octicon-location:before { content: '\f060'} /* */
|
||||
.octicon-gist-private:before,
|
||||
.octicon-mirror-private:before,
|
||||
.octicon-git-fork-private:before,
|
||||
.octicon-lock:before { content: '\f06a'} /* */
|
||||
.octicon-logo-github:before { content: '\f092'} /* */
|
||||
.octicon-mail:before { content: '\f03b'} /* */
|
||||
.octicon-mail-read:before { content: '\f03c'} /* */
|
||||
.octicon-mail-reply:before { content: '\f051'} /* */
|
||||
.octicon-mark-github:before { content: '\f00a'} /* */
|
||||
.octicon-markdown:before { content: '\f0c9'} /* */
|
||||
.octicon-megaphone:before { content: '\f077'} /* */
|
||||
.octicon-mention:before { content: '\f0be'} /* */
|
||||
.octicon-milestone:before { content: '\f075'} /* */
|
||||
.octicon-mirror-public:before,
|
||||
.octicon-mirror:before { content: '\f024'} /* */
|
||||
.octicon-mortar-board:before { content: '\f0d7'} /* */
|
||||
.octicon-mute:before { content: '\f080'} /* */
|
||||
.octicon-no-newline:before { content: '\f09c'} /* */
|
||||
.octicon-octoface:before { content: '\f008'} /* */
|
||||
.octicon-organization:before { content: '\f037'} /* */
|
||||
.octicon-package:before { content: '\f0c4'} /* */
|
||||
.octicon-paintcan:before { content: '\f0d1'} /* */
|
||||
.octicon-pencil:before { content: '\f058'} /* */
|
||||
.octicon-person-add:before,
|
||||
.octicon-person-follow:before,
|
||||
.octicon-person:before { content: '\f018'} /* */
|
||||
.octicon-pin:before { content: '\f041'} /* */
|
||||
.octicon-plug:before { content: '\f0d4'} /* */
|
||||
.octicon-repo-create:before,
|
||||
.octicon-gist-new:before,
|
||||
.octicon-file-directory-create:before,
|
||||
.octicon-file-add:before,
|
||||
.octicon-plus:before { content: '\f05d'} /* */
|
||||
.octicon-primitive-dot:before { content: '\f052'} /* */
|
||||
.octicon-primitive-square:before { content: '\f053'} /* */
|
||||
.octicon-pulse:before { content: '\f085'} /* */
|
||||
.octicon-question:before { content: '\f02c'} /* */
|
||||
.octicon-quote:before { content: '\f063'} /* */
|
||||
.octicon-radio-tower:before { content: '\f030'} /* */
|
||||
.octicon-repo-delete:before,
|
||||
.octicon-repo:before { content: '\f001'} /* */
|
||||
.octicon-repo-clone:before { content: '\f04c'} /* */
|
||||
.octicon-repo-force-push:before { content: '\f04a'} /* */
|
||||
.octicon-gist-fork:before,
|
||||
.octicon-repo-forked:before { content: '\f002'} /* */
|
||||
.octicon-repo-pull:before { content: '\f006'} /* */
|
||||
.octicon-repo-push:before { content: '\f005'} /* */
|
||||
.octicon-rocket:before { content: '\f033'} /* */
|
||||
.octicon-rss:before { content: '\f034'} /* */
|
||||
.octicon-ruby:before { content: '\f047'} /* */
|
||||
.octicon-screen-full:before { content: '\f066'} /* */
|
||||
.octicon-screen-normal:before { content: '\f067'} /* */
|
||||
.octicon-search-save:before,
|
||||
.octicon-search:before { content: '\f02e'} /* */
|
||||
.octicon-server:before { content: '\f097'} /* */
|
||||
.octicon-settings:before { content: '\f07c'} /* */
|
||||
.octicon-shield:before { content: '\f0e1'} /* */
|
||||
.octicon-log-in:before,
|
||||
.octicon-sign-in:before { content: '\f036'} /* */
|
||||
.octicon-log-out:before,
|
||||
.octicon-sign-out:before { content: '\f032'} /* */
|
||||
.octicon-squirrel:before { content: '\f0b2'} /* */
|
||||
.octicon-star-add:before,
|
||||
.octicon-star-delete:before,
|
||||
.octicon-star:before { content: '\f02a'} /* */
|
||||
.octicon-stop:before { content: '\f08f'} /* */
|
||||
.octicon-repo-sync:before,
|
||||
.octicon-sync:before { content: '\f087'} /* */
|
||||
.octicon-tag-remove:before,
|
||||
.octicon-tag-add:before,
|
||||
.octicon-tag:before { content: '\f015'} /* */
|
||||
.octicon-telescope:before { content: '\f088'} /* */
|
||||
.octicon-terminal:before { content: '\f0c8'} /* */
|
||||
.octicon-three-bars:before { content: '\f05e'} /* */
|
||||
.octicon-thumbsdown:before { content: '\f0db'} /* */
|
||||
.octicon-thumbsup:before { content: '\f0da'} /* */
|
||||
.octicon-tools:before { content: '\f031'} /* */
|
||||
.octicon-trashcan:before { content: '\f0d0'} /* */
|
||||
.octicon-triangle-down:before { content: '\f05b'} /* */
|
||||
.octicon-triangle-left:before { content: '\f044'} /* */
|
||||
.octicon-triangle-right:before { content: '\f05a'} /* */
|
||||
.octicon-triangle-up:before { content: '\f0aa'} /* */
|
||||
.octicon-unfold:before { content: '\f039'} /* */
|
||||
.octicon-unmute:before { content: '\f0ba'} /* */
|
||||
.octicon-versions:before { content: '\f064'} /* */
|
||||
.octicon-watch:before { content: '\f0e0'} /* */
|
||||
.octicon-remove-close:before,
|
||||
.octicon-x:before { content: '\f081'} /* */
|
||||
.octicon-zap:before { content: '\26A1'} /* ⚡ */
|
||||
183
src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg
Normal file
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>
|
||||
(c) 2012-2015 GitHub
|
||||
|
||||
When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos)
|
||||
|
||||
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/)
|
||||
Applies to all other files
|
||||
</metadata>
|
||||
<defs>
|
||||
<font id="octicons" horiz-adv-x="1024" >
|
||||
<font-face font-family="octicons" font-weight="400" font-stretch="normal" units-per-em="1024" ascent="832" descent="-192" />
|
||||
<missing-glyph d="M512 832C229.25 832 0 602.75 0 320c0-226.25 146.688-418.125 350.156-485.812 25.594-4.688 34.938 11.125 34.938 24.625 0 12.188-0.469 52.562-0.719 95.312C242-76.81200000000001 211.906 14.5 211.906 14.5c-23.312 59.125-56.844 74.875-56.844 74.875-46.531 31.75 3.53 31.125 3.53 31.125 51.406-3.562 78.47-52.75 78.47-52.75 45.688-78.25 119.875-55.625 149-42.5 4.654 33 17.904 55.625 32.5 68.375C304.906 106.56200000000001 185.344 150.5 185.344 346.688c0 55.938 19.969 101.562 52.656 137.406-5.219 13-22.844 65.094 5.062 135.562 0 0 42.938 13.75 140.812-52.5 40.812 11.406 84.594 17.031 128.125 17.219 43.5-0.188 87.312-5.875 128.188-17.281 97.688 66.312 140.688 52.5 140.688 52.5 28-70.531 10.375-122.562 5.125-135.5 32.812-35.844 52.625-81.469 52.625-137.406 0-196.688-119.75-240-233.812-252.688 18.438-15.875 34.75-47 34.75-94.75 0-68.438-0.688-123.625-0.688-140.5 0-13.625 9.312-29.562 35.25-24.562C877.438-98 1024 93.875 1024 320 1024 602.75 794.75 832 512 832z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="alert" unicode="" d="M1005.854 31.753000000000043l-438.286 767C556.173 818.694 534.967 831 512 831s-44.173-12.306-55.567-32.247l-438.286-767c-11.319-19.809-11.238-44.144 0.213-63.876C29.811-51.85500000000002 50.899-64 73.714-64h876.572c22.814 0 43.903 12.145 55.354 31.877S1017.173 11.94399999999996 1005.854 31.753000000000043zM576 64H448V192h128V64zM576 256H448V512h128V256z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="arrow-down" unicode="" d="M448 384V640H192v-256H0l320-384 320 384H448z" horiz-adv-x="640" />
|
||||
<glyph glyph-name="arrow-left" unicode="" d="M384 448V640L0 320l384-320V192h256V448H384z" horiz-adv-x="640" />
|
||||
<glyph glyph-name="arrow-right" unicode="" d="M640 320L256 640v-192H0v-256h256v-192L640 320z" horiz-adv-x="640" />
|
||||
<glyph glyph-name="arrow-small-down" unicode="" d="M256 384V512H128v-128H0l192-256 192 256H256z" horiz-adv-x="384" />
|
||||
<glyph glyph-name="arrow-small-left" unicode="" d="M256 384V512L0 320l256-192V256h128V384H256z" horiz-adv-x="384" />
|
||||
<glyph glyph-name="arrow-small-right" unicode="" d="M384 320L128 512v-128H0v-128h128v-128L384 320z" horiz-adv-x="384" />
|
||||
<glyph glyph-name="arrow-small-up" unicode="" d="M192 512L0 256h128v-128h128V256h128L192 512z" horiz-adv-x="384" />
|
||||
<glyph glyph-name="arrow-up" unicode="" d="M320 640L0 256h192v-256h256V256h192L320 640z" horiz-adv-x="640" />
|
||||
<glyph glyph-name="beaker" unicode="" d="M920-102L704 384V640h64v64H192v-64h64v-256L40-102c-19-42 12-90 58-90h764c46 0 77 48 58 90zM240 192l80 192V640h320v-256l80-192H240z m272 128h64v-64h-64v64z m-64 64h-64v64h64v-64z m0 192h64v-64h-64v64z m0 192h-64V832h64v-64z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="bell" unicode="" d="M896 64v-64H0v64l47 37c49 49 52 163 76 283 49 241 261 320 261 320 0 35 29 64 64 64s64-29 64-64c0 0 217-79 266-320 24-120 27-234 76-283l42-37z m-448-256c71 0 128 57 128 128H320c0-71 57-128 128-128z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="book" unicode="" d="M128 512h256v-64H128v64z m0-192h256v64H128v-64z m0-128h256v64H128v-64z m704 320H576v-64h256v64z m0-128H576v-64h256v64z m0-128H576v-64h256v64z m128 384v-576c0-35-29-64-64-64H544l-64-64-64 64H64c-35 0-64 29-64 64V640c0 35 29 64 64 64h352l64-64 64 64h352c35 0 64-29 64-64z m-512-32l-32 32H64v-576h384V608z m448 32H544l-32-32v-544h384V640z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="bookmark" unicode="" d="M0-64l192 128 192-128V640c0 35-29 64-64 64H64c-35 0-64-29-64-64v-704z m72 580l88 1 27 83c3 9 7 9 10 0l27-83 88-1c9 0 11-3 4-9l-72-52 28-83c2-9-1-11-8-7l-72 52-72-52c-7-4-10-2-8 7l28 83-72 52c-7 6-5 9 4 9z" horiz-adv-x="384" />
|
||||
<glyph glyph-name="briefcase" unicode="" d="M576 576v64c0 35-29 64-64 64H384c-35 0-64-29-64-64v-64H64c-35 0-64-29-64-64v-512c0-35 29-64 64-64h768c35 0 64 29 64 64V512c0 35-29 64-64 64H576z m-192 64h128v-64H384v64z m448-384H512v-64H384v64H64V512h64v-192h640V512h64v-256z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="broadcast" unicode="" d="M576 256h-64c35 0 64 29 64 64v64c0 35-29 64-64 64h-64c-35 0-64-29-64-64v-64c0-35 29-64 64-64h-64c-35 0-64-29-64-64v-128h64v-192c0-35 29-64 64-64h64c35 0 64 29 64 64V64h64V192c0 35-29 64-64 64zM448 384h64v-64h-64v64z m128-256h-64v-256h-64V128h-64v64h192v-64z m134 224c0 127-103 230-230 230S250 479 250 352c0-18 2-35 6-52v-127c-39 49-64 111-64 179 0 159 129 288 288 288s288-129 288-288c0-68-25-130-64-179V300c4 17 6 34 6 52z m250 0c0-184-104-344-256-424v67c119 74 198 206 198 357 0 233-189 422-422 422S58 585 58 352c0-151 79-283 198-357v-67C104 8 0 168 0 352 0 617 215 832 480 832s480-215 480-480z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="browser" unicode="" d="M320 640h64v-64h-64V640zM192 640h64v-64h-64V640zM64 640h64v-64H64V640zM832 0H64V512h768V0zM832 576H448v64h384V576zM896 640c0 35.35-28.65 64-64 64H64c-35.35 0-64-28.65-64-64v-640c0-35.35 28.65-64 64-64h768c35.35 0 64 28.65 64 64V640z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="bug" unicode="" d="M704 192h192v64H704v64l203 66-22 60-181-62v64c0 35-29 64-64 64v64c0 31-23 56-53 62l66 66h115V768H627L499 640h-38L333 768H192v-64h115l66-66c-30-6-53-31-53-62v-64c-35 0-64-29-64-64v-64L75 446l-22-60 203-66v-64H64v-64h192v-64L53 62l22-60 181 62v-64c0-35 29-64 64-64h64l64 64V448h64v-448l64-64h64c35 0 64 29 64 64v64l181-62 22 60-203 66v64zM576 512H384v64h192v-64z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="calendar" unicode="" d="M768 704h-64v-96c0-18-14-32-32-32H544c-18 0-32 14-32 32v96H320v-96c0-18-14-32-32-32H160c-18 0-32 14-32 32v96H64c-35 0-64-29-64-64v-704c0-35 29-64 64-64h704c35 0 64 29 64 64V640c0 35-29 64-64 64z m0-768H64V512h704v-576zM256 640h-64V768h64v-128z m384 0h-64V768h64v-128zM320 384h-64v64h64v-64z m128 0h-64v64h64v-64z m128 0h-64v64h64v-64z m128 0h-64v64h64v-64zM192 256h-64v64h64v-64z m128 0h-64v64h64v-64z m128 0h-64v64h64v-64z m128 0h-64v64h64v-64z m128 0h-64v64h64v-64zM192 128h-64v64h64v-64z m128 0h-64v64h64v-64z m128 0h-64v64h64v-64z m128 0h-64v64h64v-64z m128 0h-64v64h64v-64zM192 0h-64v64h64v-64z m128 0h-64v64h64v-64z m128 0h-64v64h64v-64z m128 0h-64v64h64v-64z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="check" unicode="" d="M768 512L256 0 0 256l96 96 160-160 416 416 96-96z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="checklist" unicode="" d="M208 166c1 21 10 42 25 56l64 60c16 15 35 22 55 22s41-8 57-23l39-40 128 128V640c0 35-29 64-64 64H64c-35 0-64-29-64-64v-576c0-35 29-64 64-64h272L231 108c-15 16-23 36-23 58z m-16 474h320v-64H192v64z m-64-192H64v64h64v-64z m0 128H64v64h64v-64z m64-64h320v-64H192v64z m544-224L448 0 288 164l64 60 96-96 224 224 64-64z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="chevron-down" unicode="" d="M320 128L0 448l96 96 224-240 224 240 96-96-320-320z" horiz-adv-x="640" />
|
||||
<glyph glyph-name="chevron-left" unicode="" d="M352 640l96-96-240-224 240-224-96-96L32 320l320 320z" horiz-adv-x="512" />
|
||||
<glyph glyph-name="chevron-right" unicode="" d="M480 320L160 0l-96 96 240 224L64 544l96 96 320-320z" horiz-adv-x="512" />
|
||||
<glyph glyph-name="chevron-up" unicode="" d="M640 256l-96-96-224 240L96 160 0 256l320 320 320-320z" horiz-adv-x="640" />
|
||||
<glyph glyph-name="circle-slash" unicode="" d="M448 768C201 768 0 567 0 320s201-448 448-448 448 201 448 448S695 768 448 768z m0-83c83 0 160-28 222-75L158 98c-47 62-75 139-75 222 0 201 164 365 365 365z m0-730c-83 0-160 28-222 75l512 512c47-62 75-139 75-222 0-201-164-365-365-365z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="circuit-board" unicode="" d="M192 512c0 35 29 64 64 64s64-29 64-64-29-64-64-64-64 29-64 64z m512 0c0 35-29 64-64 64s-64-29-64-64 29-64 64-64 64 29 64 64z m0-384c0 35-29 64-64 64s-64-29-64-64 29-64 64-64 64 29 64 64zM832 768H320v-139c23-12 41-30 53-53h150c27 50 85 82 150 67 48-12 87-51 98-99 20-88-46-166-131-166-51 0-95 28-117 70H373c-27-51-85-82-150-66-47 11-86 50-97 97-16 65 15 123 66 150V768H64C29 768 0 739 0 704v-768c0-35 29-64 64-64l320 320h139c27 50 85 82 150 67 48-12 87-51 98-99 20-88-46-166-131-166-51 0-95 28-117 70h-75L256-128h576c35 0 64 29 64 64V704c0 35-29 64-64 64z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="clippy" unicode="" d="M128 64h256v-64H128v64z m320 384H128v-64h320v64z m128-192V384L384 192l192-192V128h320V256H576z m-288 64H128v-64h160v64zM128 128h160v64H128v-64z m576-64h64v-128c-1-18-7-33-19-45s-27-18-45-19H64c-35 0-64 29-64 64V640c0 35 29 64 64 64h192C256 775 313 832 384 832s128-57 128-128h192c35 0 64-29 64-64v-320h-64V512H64v-576h640V64zM128 576h512c0 35-29 64-64 64h-64c-35 0-64 29-64 64s-29 64-64 64-64-29-64-64-29-64-64-64h-64c-35 0-64-29-64-64z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="clock" unicode="" d="M512 320h192v-128H448c-35 0-64 29-64 64V576h128v-256z m-64 365c201 0 365-164 365-365S649-45 448-45 83 119 83 320s164 365 365 365m0 83C201 768 0 567 0 320s201-448 448-448 448 201 448 448S695 768 448 768z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="cloud-download" unicode="" d="M576 0h128l-192-192-192 192h128V320h128v-320z m192 512c0 28-58 192-288 192-155 0-288-123-288-256C65 448 0 351 0 256c0-98 64-192 192-192 28 0 170 0 192 0v83H192C88 147 83 238 83 256c0 11 3 109 109 109h83v83c0 89 100 173 205 173 163 0 200-99 205-115v-77h83c52 0 173-14 173-141 0-134-144-141-173-141H640v-83c24 0 127 0 128 0 133 0 256 74 256 224 0 156-123 224-256 224z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="cloud-upload" unicode="" d="M448 256H320l192 192 192-192H576v-320H448V256z m320 256c0 28-58 192-288 192-155 0-288-123-288-256C65 448 0 351 0 256c0-98 64-192 192-192 28 0 170 0 192 0v83H192C88 147 83 238 83 256c0 11 3 109 109 109h83v83c0 89 100 173 205 173 163 0 200-99 205-115v-77h83c52 0 173-14 173-141 0-134-144-141-173-141H640v-83c24 0 127 0 128 0 133 0 256 74 256 224 0 156-123 224-256 224z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="code" unicode="" d="M608 640l-96-96 224-224L512 96l96-96 288 320L608 640zM288 640L0 320l288-320 96 96L160 320l224 224L288 640z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="color-mode" unicode="" d="M704 704H64c-35 0-64-29-64-64v-640c0-35 29-64 64-64h640c35 0 64 29 64 64V640c0 35-29 64-64 64zM64 0V640h640L64 0z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="comment" unicode="" d="M832 704H64c-35 0-64-29-64-64v-512c0-35 29-64 64-64h128v-224l224 224h416c35 0 64 29 64 64V640c0 35-29 64-64 64z m0-576H384L256 0V128H64V640h768v-512z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="comment-discussion" unicode="" d="M256 256c0-35 29-64 64-64h256v-64c0-35-29-64-64-64H320L128-128V64H64c-35 0-64 29-64 64V448c0 35 29 64 64 64h192v-256z m640 448H384c-35 0-64-29-64-64v-320c0-35 29-64 64-64h256l192-192V256h64c35 0 64 29 64 64V640c0 35-29 64-64 64z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="credit-card" unicode="" d="M768 256H128v64h640v-64z m256 384v-576c0-35-29-64-64-64H64c-35 0-64 29-64 64V640c0 35 29 64 64 64h896c35 0 64-29 64-64z m-64-192H64v-384h896V448z m0 192H64v-64h896v64zM384 192H128v-64h256v64z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="dash" unicode="" d="M0 384v-128h512V384H0z" horiz-adv-x="512" />
|
||||
<glyph glyph-name="dashboard" unicode="" d="M512 512h-64v64h64v-64z m256-192h-64v-64h64v64zM320 512h-64v-64h64v64z m-64-192h-64v-64h64v64z m704 352l-32 32-416-320c-4 1-64 0-64 0-35 0-64-29-64-64v-64c0-35 29-64 64-64h64c35 0 64 29 64 64v59l384 357zM858 410c12-39 19-80 19-122 0-219-178-397-397-397S83 69 83 288s178 397 397 397c77 0 148-22 209-60l60 60c-76 52-169 83-269 83C215 768 0 553 0 288s215-480 480-480 480 215 480 480c0 66-13 129-38 186l-64-64z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="database" unicode="" d="M384-128C171.969-128 0-70.625 0 0c0 38.625 0 80.875 0 128 0 11.125 5.562 21.688 13.562 32C56.375 104.875 205.25 64 384 64s327.625 40.875 370.438 96c8-10.312 13.562-20.875 13.562-32 0-37.062 0-76.375 0-128C768-70.625 596-128 384-128zM384 128C171.969 128 0 185.375 0 256c0 38.656 0 80.844 0 128 0 6.781 2.562 13.375 6 19.906l0 0C7.938 408 10.5 412.031 13.562 416 56.375 360.906 205.25 320 384 320s327.625 40.906 370.438 96c3.062-3.969 5.625-8 7.562-12.094l0 0c3.438-6.531 6-13.125 6-19.906 0-37.062 0-76.344 0-128C768 185.375 596 128 384 128zM384 384C171.969 384 0 441.344 0 512c0 20.219 0 41.594 0 64 0 20.344 0 41.469 0 64C0 710.656 171.969 768 384 768c212 0 384-57.344 384-128 0-19.969 0-41.156 0-64 0-19.594 0-40.25 0-64C768 441.344 596 384 384 384zM384 704c-141.375 0-256-28.594-256-64s114.625-64 256-64 256 28.594 256 64S525.375 704 384 704z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="desktop-download" unicode="" d="M256 448h192V832h128v-384h192L512 192 256 448z m704 256H704v-64h256v-512H64V640h256v64H64c-35 0-64-29-64-64v-576c0-35 29-64 64-64h342c-16-39-55-89-150-128h512c-95 39-134 89-150 128h342c35 0 64 29 64 64V640c0 35-29 64-64 64z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="device-camera" unicode="" d="M960 640H448c0 35-29 64-64 64H128c-35 0-64-29-64-64-35 0-64-29-64-64v-576c0-35 29-64 64-64h896c35 0 64 29 64 64V576c0 35-29 64-64 64zM384 512H128v64h256v-64z m288-448c-124 0-224 100-224 224s100 224 224 224 224-100 224-224-100-224-224-224z m160 224c0-88-72-160-160-160s-160 72-160 160 72 160 160 160 160-72 160-160z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="device-camera-video" unicode="" d="M973 634L640 402V576c0 35-29 64-64 64H64c-35 0-64-29-64-64v-576c0-35 29-64 64-64h512c35 0 64 29 64 64V174l333-232c21-15 51 0 51 26V608c0 26-30 41-51 26z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="device-desktop" unicode="" d="M960 704H64c-35 0-64-29-64-64v-576c0-35 29-64 64-64h342c-16-39-55-89-150-128h512c-95 39-134 89-150 128h342c35 0 64 29 64 64V640c0 35-29 64-64 64z m0-576H64V640h896v-512z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="device-mobile" unicode="" d="M576 832H64C29 832 0 803 0 768v-896c0-35 29-64 64-64h512c35 0 64 29 64 64V768c0 35-29 64-64 64zM320-147c-46 0-83 37-83 83s37 83 83 83 83-37 83-83-37-83-83-83z m256 211H64V704h512v-640z" horiz-adv-x="640" />
|
||||
<glyph glyph-name="diff" unicode="" d="M384 384h128v-64H384v-128h-64V320H192v64h128V512h64v-128zM192 0h320v64H192v-64z m288 704l224-224v-608c0-35-29-64-64-64H64c-35 0-64 29-64 64V640c0 35 29 64 64 64h416z m160-256L448 640H64v-768h576V448zM544 832S192 832 192 832v-64h320l256-256v-512h64V544L544 832z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="diff-added" unicode="" d="M832 768H64C29 768 0 739 0 704v-768c0-35 29-64 64-64h768c35 0 64 29 64 64V704c0 35-29 64-64 64z m0-832H64V704h768v-768zM384 256H192V384h192V576h128v-192h192v-128H512v-192H384V256z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="diff-ignored" unicode="" d="M832 768H64C29 768 0 739 0 704v-768c0-35 29-64 64-64h768c35 0 64 29 64 64V704c0 35-29 64-64 64z m0-832H64V704h768v-768zM288 64h-96v96l416 416h96v-96L288 64z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="diff-modified" unicode="" d="M832 768H64C29 768 0 739 0 704v-768c0-35 29-64 64-64h768c35 0 64 29 64 64V704c0 35-29 64-64 64z m0-832H64V704h768v-768zM256 320c0 106 86 192 192 192s192-86 192-192-86-192-192-192-192 86-192 192z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="diff-removed" unicode="" d="M832 768H64C29 768 0 739 0 704v-768c0-35 29-64 64-64h768c35 0 64 29 64 64V704c0 35-29 64-64 64z m0-832H64V704h768v-768zM704 256H192V384h512v-128z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="diff-renamed" unicode="" d="M384 256H192V384h192V576l320-256-320-256V256z m512 448v-768c0-35-29-64-64-64H64c-35 0-64 29-64 64V704c0 35 29 64 64 64h768c35 0 64-29 64-64z m-64 0H64v-768h768V704z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="ellipsis" unicode="" d="M704 512H64c-35 0-64-29-64-64v-256c0-35 29-64 64-64h640c35 0 64 29 64 64V448c0 35-29 64-64 64zM256 256H128V384h128v-128z m192 0H320V384h128v-128z m192 0H512V384h128v-128z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="eye" unicode="" d="M516 704C192 704 0 320 0 320s192-384 516-384c316 0 508 384 508 384S832 704 516 704z m-4-640c-141 0-256 114-256 256 0 141 115 256 256 256 142 0 256-115 256-256 0-142-114-256-256-256z m128 256c0-71-57-128-128-128s-128 57-128 128 57 128 128 128 128-57 128-128z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="file-binary" unicode="" d="M256 64h64v-64H128v64h64V192h-64v64h128v-192z m512 480v-608c0-35-29-64-64-64H64c-35 0-64 29-64 64V704c0 35 29 64 64 64h480l224-224z m-64-32L512 704H64v-768h640V512z m-192 64H384v-64h64v-128h-64v-64h192v64h-64V576z m-384 0h192v-256H128V576z m64-192h64V512h-64v-128z m192-128h192v-256H384V256z m64-192h64V192h-64v-128z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="file-code" unicode="" d="M544 768H64C29 768 0 739 0 704v-768c0-35 29-64 64-64h640c35 0 64 29 64 64V544L544 768z m160-832H64V704h448l192-192v-576zM320 385l-96-97 96-96-32-64-160 160 160 160 32-63z m160 63l160-160-160-160-32 63 96 97-96 96 32 64z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="file-directory" unicode="" d="M832 576H448v64c0 42-20 64-64 64H64c-35 0-64-29-64-64v-640c0-35 29-64 64-64h768c35 0 64 29 64 64V512c0 35-29 64-64 64z m-448 0H64v64h320v-64z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="file-media" unicode="" d="M384 512h128v-128H384V512z m384 32v-608c0-35-29-64-64-64H64c-35 0-64 29-64 64V704c0 35 29 64 64 64h480l224-224z m-64-32L512 704H64v-704l192 320 128-256 128 128 192-192V512z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="file-pdf" unicode="" d="M544 768H64C29 768 0 739 0 704v-768c0-35 29-64 64-64h640c35 0 64 29 64 64V544L544 768zM64 704h256c-7-2-13-6-20-13-6-6-11-16-15-30-7-25-9-57-6-94s11-75 22-115c-15-47-39-103-71-170s-51-106-58-118c-9-3-23-9-44-19-21-9-42-23-64-41V704z m283-307c29-72 54-117 75-134s41-29 60-34c-41-6-79-13-116-21-36-8-75-21-116-38 1 1 14 28 39 80s45 101 58 147z m357-461H96c-4 0-8 1-11 2 13 4 29 13 47 28 29 24 67 74 114 152 20 8 37 15 52 20l27 9c29 8 60 15 92 21 32 5 64 10 95 13 29-14 58-25 89-34 31-8 58-13 79-15 9 0 17 1 24 2v-198z m0 311c-12 7-26 13-41 18-15 4-31 6-48 7-25 0-51-2-79-5-15 4-36 18-63 41s-55 73-84 149c8 53 12 95 13 126s1 47 0 48c3 26-2 45-13 56s-24 17-39 17h162l192-192v-265z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="file-submodule" unicode="" d="M640 384H256v-448h576c35 0 64 29 64 64V320H640v64z m-64-128H320v64h256v-64z m256 320H448v64c0 42-20 64-64 64H64c-35 0-64-29-64-64v-640c0-35 29-64 64-64h128V384c0 35 29 64 64 64h384c35 0 64-29 64-64h192V512c0 35-29 64-64 64z m-448 0H64v64h320v-64z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="file-symlink-directory" unicode="" d="M832 576H448v64c0 42-20 64-64 64H64c-35 0-64-29-64-64v-640c0-35 29-64 64-64h768c35 0 64 29 64 64V512c0 35-29 64-64 64zM64 640h320v-64H64v64z m384-576V192c-63 1-118-14-163-45s-76-80-93-147c1 105 25 184 72 239 47 54 108 81 184 81V448l256-192-256-192z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="file-symlink-file" unicode="" d="M544 768H64C29 768 0 739 0 704v-768c0-35 29-64 64-64h640c35 0 64 29 64 64V544L544 768z m160-832H64V704h448l192-192v-576zM384 544l256-192-256-192V288c-63 1-118-14-163-45s-76-80-93-147c1 105 25 184 72 239 47 54 108 81 184 81V544z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="file-text" unicode="" d="M384 512H128v64h256v-64zM128 320h448v64H128v-64z m0-128h448v64H128v-64z m0-128h448v64H128v-64z m640 480v-608c0-35-29-64-64-64H64c-35 0-64 29-64 64V704c0 35 29 64 64 64h480l224-224z m-64-32L512 704H64v-768h640V512z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="file-zip" unicode="" d="M544 768H64C29 768 0 739 0 704v-768c0-35 29-64 64-64h640c35 0 64 29 64 64V544L544 768z m160-832H64V704h192v-64h64v64h192l192-192v-576zM320 576v64h64v-64h-64z m-64 0h64v-64h-64v64z m64-128v64h64v-64h-64z m-64 0h64v-64h-64v64z m64-128v64h64v-64h-64z m-64-82c-38-22-64-63-64-110v-64h256v64c0 71-57 128-128 128v64h-64v-82z m128-46v-64H256v64h128z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="flame" unicode="" d="M433 787c50-134 24-207-32-265-61-64-156-112-223-206-89-125-104-400 217-472-135 71-164 277-18 406-38-125 32-205 119-176 85 29 141-32 139-102-1-48-20-89-69-112 209 37 293 210 293 342 0 174-155 198-77 344-93-8-125-69-116-169 6-66-63-111-114-81-41 25-40 73-4 109 77 76 107 251-115 382z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="fold" unicode="" d="M448 256l192-192H512v-192H384V64H256l192 192z m192 384H512V832H384v-192H256l192-192 192 192z m256-128c0 35-29 64-64 64H672l-64-64h192L672 384H224L96 512h192l-64 64H64c-35 0-64-29-64-64l160-160L0 192c0-35 29-64 64-64h160l64 64H96l128 128h448l128-128H608l64-64h160c35 0 64 29 64 64L736 352l160 160z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="gear" unicode="" d="M896 271V373l-124 41-29 70 56 118-72 72-116-58-70 29-44 123H395l-40-124-71-29-118 56-72-72 58-116-29-70L0 369v-102l124-41 29-70-56-118 72-72 116 58 70-29 44-123h102l40 124 71 29 118-56 72 72-59 116 30 70 123 44zM448 128c-106 0-192 86-192 192s86 192 192 192 192-86 192-192-86-192-192-192z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="gift" unicode="" d="M832 576h-88c12 21 21 43 23 58 4 43-7 78-33 103-23 24-52 31-87 31-3 0-5 0-7 0-34-1-71-16-98-37s-47-46-62-77c-15 31-35 56-62 77s-64 37-98 37c-1 0-2 0-2 0-36 0-68-6-92-31-26-25-37-60-33-103 2-15 11-37 23-58h-88c-35 0-64-29-64-64v-192h64v-320c0-35 29-64 64-64h576c35 0 64 29 64 64V320h64V512c0 35-29 64-64 64z m-306 56c11 23 27 43 48 59 19 15 46 25 67 26h6c29 0 42-7 51-16s21-25 19-61c-3-12-16-39-32-64H500l26 56z m-264 69c8 8 20 16 58 16 20 0 46-11 66-26 21-16 37-35 48-59l27-56H275c-16 25-29 52-32 64-2 36 10 52 19 61z m186-701H192V320h256v-320z m0 384H128V512h320v-128z m320-384H512V320h256v-320z m64 384H512V512h320v-128z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="gist" unicode="" d="M480 512l160-160-160-160-48 48 112 112-112 112 48 48z m-192 0L128 352l160-160 48 48-112 112 112 112-48 48zM0 0V704c0 35 29 64 64 64h640c35 0 64-29 64-64v-704c0-35-29-64-64-64H64c-35 0-64 29-64 64z m64 0h640V704H64v-704z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="gist-secret" unicode="" d="M512 160l64-224H320l64 224-48 96h224l-48-96z m128 288H256l-128-64h640l-128 64z m-64 256l-128-64-128 64-64-192h384l-64 192z m258-496l-194 48 64-128-128-192h206c29 0 55 20 62 48l36 146c9 34-12 69-46 78z m-578 48L62 208c-34-9-55-44-46-78l36-146c7-28 33-48 62-48h206L192 128l64 128z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="git-branch" unicode="" d="M640 512c0 71-57 128-128 128s-128-57-128-128c0-47 26-88 64-110v-19c-1-33-15-63-40-88s-55-39-88-40c-53-1-95-10-128-29V530c38 22 64 63 64 110 0 71-57 128-128 128S0 711 0 640c0-47 26-88 64-110v-420C26 88 0 47 0 0c0-71 57-128 128-128s128 57 128 128c0 34-13 64-34 87 6 4 31 26 38 30 16 7 36 11 60 11 67 3 125 29 176 80s77 127 80 193h-1c39 23 65 64 65 111zM128 717c42 0 77-35 77-77s-35-77-77-77-77 35-77 77 35 77 77 77z m0-794c-42 0-77 35-77 77s35 77 77 77 77-35 77-77-35-77-77-77z m384 512c-42 0-77 35-77 77s35 77 77 77 77-35 77-77-35-77-77-77z" horiz-adv-x="640" />
|
||||
<glyph glyph-name="git-commit" unicode="" d="M695 384c-29 110-128 192-247 192s-218-82-247-192H0v-128h201c29-110 128-192 247-192s218 82 247 192h201V384H695zM448 179c-78 0-141 63-141 141s63 141 141 141 141-63 141-141-63-141-141-141z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="git-compare" unicode="" d="M320 64h-64c-17 1-31 7-44 20s-19 27-20 44V530c38 22 64 63 64 110 0 71-57 128-128 128S0 711 0 640c0-47 26-88 64-110 0-111 0-402 0-402 2-50 22-94 60-132s82-58 132-60c0 0 65 0 64 0v-128l192 192-192 192v-128zM128 717c42 0 77-35 77-77s-35-77-77-77-77 35-77 77 35 77 77 77z m704-607c0 111 0 402 0 402-2 50-22 94-60 132s-82 58-132 60c0 0-65 0-64 0V832L384 640l192-192V576h64c17-1 31-7 44-20s19-27 20-44v-402c-38-22-64-63-64-110 0-71 57-128 128-128s128 57 128 128c0 47-26 88-64 110z m-64-187c-42 0-77 35-77 77s35 77 77 77 77-35 77-77-35-77-77-77z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="git-merge" unicode="" d="M640 384c-47 0-88-26-111-65v1c-67 1-145 23-200 65-48 37-96 103-121 156 29 23 48 59 48 99 0 71-57 128-128 128S0 711 0 640c0-47 26-88 64-110v-420C26 88 0 47 0 0c0-71 57-128 128-128s128 57 128 128c0 47-26 88-64 110V341c43-45 92-81 147-108s130-40 190-41v1c23-39 64-65 111-65 71 0 128 57 128 128s-57 128-128 128zM205 0c0-42-35-77-77-77s-77 35-77 77 35 77 77 77 77-35 77-77z m-77 563c-42 0-77 35-77 77s35 77 77 77 77-35 77-77-35-77-77-77z m512-384c-42 0-77 35-77 77s35 77 77 77 77-35 77-77-35-77-77-77z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="git-pull-request" unicode="" d="M704 110c0 111 0 402 0 402-2 50-22 94-60 132s-82 58-132 60c0 0-65 0-64 0V832L256 640l192-192V576h64c17-1 31-7 44-20s19-27 20-44v-402c-38-22-64-63-64-110 0-71 57-128 128-128s128 57 128 128c0 47-26 88-64 110z m-64-187c-42 0-77 35-77 77s35 77 77 77 77-35 77-77-35-77-77-77zM256 640c0 71-57 128-128 128S0 711 0 640c0-47 26-88 64-110 0-99 0-356 0-420-38-22-64-63-64-110 0-71 57-128 128-128s128 57 128 128c0 47-26 88-64 110V530c38 22 64 63 64 110z m-51-640c0-42-35-77-77-77s-77 35-77 77 35 77 77 77 77-35 77-77z m-77 563c-42 0-77 35-77 77s35 77 77 77 77-35 77-77-35-77-77-77z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="globe" unicode="" d="M512 704c-212.077 0-384-171.923-384-384s171.923-384 384-384c25.953 0 51.303 2.582 75.812 7.49-9.879 4.725-10.957 40.174-1.188 60.385 10.875 22.5 45 79.5 11.25 98.625s-24.375 27.75-45 49.875-12.19 25.451-13.5 31.125c-4.5 19.5 19.875 48.75 21 51.75s1.125 14.25 0.75 17.625S545.75 265.25 542 265.625s-5.625-6-10.875-6.375-28.125 13.875-33 17.625-7.125 12.75-13.875 19.5-7.5 1.5-18 5.625-44.25 16.5-70.125 27-28.125 25.219-28.5 35.625-15.75 25.5-22.961 36.375c-7.209 10.875-8.539 25.875-11.164 22.5s13.5-42.75 10.875-43.875-8.25 10.875-15.75 20.625 7.875 4.5-16.125 51.75 7.5 71.344 9 96 20.25-9 10.5 6.75 0.75 48.75-6.75 60.75S275 602 275 602c1.125 11.625 37.5 31.5 63.75 49.875s42.281 4.125 63.375-2.625 22.5-4.5 15.375 2.25 3 10.125 19.5 7.5 21-22.5 46.125-20.625 2.625-4.875 6-11.25-3.75-5.625-20.25-16.875S469.25 599 498.5 577.625s20.25 14.25 17.25 30S537.125 611 537.125 611c18-12 14.674-0.66 27.799-4.785S613.625 572 613.625 572c-44.625-24.375-16.5-27-9-32.625s-15.375-16.5-15.375-16.5c-9.375 9.375-10.875-0.375-16.875-3.75s-0.375-12-0.375-12c-31.031-4.875-24-37.5-23.625-45.375s-19.875-19.875-25.125-31.125S536.75 395 527 393.5s-19.5 36.75-72 22.5c-15.828-4.297-51-22.5-32.25-59.625s49.875 10.5 60.375 5.25-3-28.875-0.75-29.25 29.625-1.031 31.125-33 41.625-29.25 50.25-30 37.5 23.625 41.625 24.75S626 309.125 662 288.5s54.375-17.625 66.75-26.25 3.75-25.875 15.375-31.5 58.125 1.875 69.75-17.25-48-115.125-66.75-125.625S719.75 53.375 701 38s-45-34.406-69.75-49.125c-21.908-13.027-25.85-36.365-35.609-43.732C767.496-16.67999999999995 896 136.64999999999998 896 320 896 532.077 724.077 704 512 704zM602 343.625c-5.25-1.5-16.125-11.25-42.75 4.5s-45 12.75-47.25 15.375c0 0-2.25 6.375 9.375 7.5 23.871 2.311 54-22.125 60.75-22.5s10.125 6.75 22.125 2.883C616.25 347.52 607.25 345.125 602 343.625zM476.375 665.75c-2.615 1.902 2.166 4.092 5.016 7.875 1.645 2.186 0.425 5.815 2.484 7.875 5.625 5.625 33.375 13.5 27.949-1.875C506.4 664.25 480.5 662.75 476.375 665.75zM543.5 617c-9.375 0.375-31.443 2.707-27.375 6.75 15.844 15.75-6 20.25-19.5 21.375S477.5 653.75 484.25 654.5s33.75-0.375 38.25-4.125 28.875-13.5 30.375-20.625S552.875 616.625 543.5 617zM624.875 619.625c-7.5-6-45.24 21.529-52.5 27.75-31.5 27-48.375 18-54.99 22.5-6.617 4.5-4.26 10.5 5.865 19.5s38.625-3 55.125-4.875 35.625-14.625 36-29.781C614.75 639.564 632.375 625.625 624.875 619.625z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="graph" unicode="" d="M1024-64v-64H0V832h64v-896h960z m-704 64H192V320h128v-320z m256 0H448V640h128v-640z m256 0H704V448h128v-448z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="heart" unicode="♥" d="M717 576c-33 40-80 61-141 64-62 0-108-27-141-64s-50-59-51-64c-1 5-18 27-51 64s-75 64-141 64c-61-3-108-24-141-64-33-39-50-82-51-128 0-33 6-97 43-171s150-188 341-341c191 153 305 267 342 341s42 139 42 171c-1 46-18 89-51 129z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="history" unicode="" d="M512 0H384V448h320v-128H512v-320zM448 768c-140 0-264-65-346-166L0 704v-256h256l-96 96c67 85 171 141 288 141 201 0 365-164 365-365S649-45 448-45 83 119 83 320c0 22 2 43 6 64H5c-3-21-5-42-5-64 0-247 201-448 448-448s448 201 448 448S695 768 448 768z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="home" unicode="" d="M1024 256L832 448V704H704v-128L512 768 0 256h128l64-320c0-35 29-64 64-64h512c35 0 64 29 64 64l64 320h128zM768-64H576V192H448v-256H256l-76 404 332 332 332-332-76-404z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="horizontal-rule" unicode="" d="M63.938 384h128v-128h64V639.938h-64V448h-128V639.938H0V256h63.938V384zM639.875 256V384h-63.938v-128H639.875zM639.875 448V575.938h-63.938V448H639.875zM447.938 448V575.938h128v64h-192V256h64V384h128v64H447.938zM0 0h639.875V128H0V0z" horiz-adv-x="639.875" />
|
||||
<glyph glyph-name="hubot" unicode="" d="M192 448c-35 0-64-29-64-64v-128c0-35 29-64 64-64h512c35 0 64 29 64 64V384c0 35-29 64-64 64H192z m512-112l-80-80h-96l-80 80-80-80h-96l-80 80v48h48l80-80 80 80h96l80-80 80 80h48v-48zM320 128h256v-64H320v64z m128 576C201 704 0 518 0 288v-288c0-35 29-64 64-64h768c35 0 64 29 64 64V288c0 230-201 416-448 416z m384-704H64V288c0 198 169 358 384 358s384-160 384-358v-288z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="inbox" unicode="" d="M896 256l-72 457c-5 31-32 55-64 55H136c-32 0-59-24-64-55L0 256v-320c0-35 29-64 64-64h768c35 0 64 29 64 64V256z m-210-35l-28-57c-11-22-33-36-58-36H295c-24 0-46 14-57 35l-28 58c-11 21-33 35-57 35H64l64 448h640l64-448h-88c-25 0-47-14-58-35z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="info" unicode="" d="M403 468c-12 12-18 27-18 45s6 33 18 45 27 18 45 18 33-6 45-18 18-27 18-45-6-33-18-45-27-19-45-19-33 7-45 19z m109-147c-1 16-7 31-20 44-13 12-27 19-44 20h-64c-17-1-31-8-44-20-13-13-19-28-20-44h64v-192c1-17 7-32 20-44 13-13 27-20 44-20h64c17 0 31 7 44 20 13 12 19 27 20 44h-64V321z m-64 364C247 685 83 522 83 321s164-365 365-365 365 163 365 365-164 364-365 364m0 84c247 0 448-201 448-448S695-127 448-127 0 73 0 321 201 769 448 769z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="issue-closed" unicode="" d="M448 192h128v-128H448V192z m128 384H448v-320h128V576z m96-96l-64-64 160-160 256 288-64 64-192-224-96 96zM512-45c-201 0-365 164-365 365s164 365 365 365c117 0 221-56 288-141l59 59C777 704 652 768 512 768 265 768 64 567 64 320s201-448 448-448 448 201 448 448l-97-97c-42-154-183-268-351-268z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="issue-opened" unicode="" d="M448 685c201 0 365-164 365-365S649-45 448-45 83 119 83 320s164 365 365 365m0 83C201 768 0 567 0 320s201-448 448-448 448 201 448 448S695 768 448 768z m64-192H384v-320h128V576z m0-384H384v-128h128V192z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="issue-reopened" unicode="" d="M512 256H384V576h128v-320zM384 64h128V192H384v-128z m405 128H640l96-96c-67-85-171-141-288-141-201 0-365 164-365 365 0 22 2 43 6 64H5c-3-21-5-42-5-64 0-247 201-448 448-448 140 0 264 65 346 166l102-102V192H789zM107 448h149l-96 96c67 85 171 141 288 141 201 0 365-164 365-365 0-22-2-43-6-64h84c3 21 5 42 5 64 0 247-201 448-448 448-140 0-264-65-346-166L0 704v-256h107z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="jersey" unicode="" d="M224 448l-32-32v-320l32-32h128l32 32V416l-32 32H224z m96-320h-64V384h64v-256z m401 464c-14 88-20 168-17 240H513c0-17-8-31-25-44-16-13-40-19-72-19s-56 6-72 19c-15 13-23 27-23 44H128c3-72-2-152-16-240-13-88-51-136-112-144v-576c1-17 7-31 20-44s27-19 44-20h704c17 1 31 7 44 20s19 27 20 44V448c-61 8-98 56-112 144z m47-720H64V384c57 32 95 80 110 144s20 144 18 240h64c-1-50 10-94 33-132 23-37 65-57 128-60 63 1 105 21 128 60 23 38 32 82 31 132h64c1-91 8-163 21-216 13-52 44-128 107-168v-512zM480 448l-32-32v-320l32-32h128l32 32V416l-32 32H480z m96-320h-64V384h64v-256z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="key" unicode="" d="M821 693c-48 48-108 73-181 75-72-2-133-27-181-75s-72-108-74-181c0-19 2-38 6-57L0 64v-64l64-64h128l64 64v64h64v64h64v64h128l70 71c19-5 38-7 58-7 73 2 133 27 181 75s73 108 75 181c-2 73-27 133-75 181zM704 488c-49 0-88 39-88 88s39 88 88 88 88-39 88-88-39-88-88-88z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="keyboard" unicode="" d="M640 512h-64v64h64v-64z m-448-64h-64v-64h64v64z m320 128h-64v-64h64v64z m-256 0H128v-64h128v64z m512-448h128v64H768v-64zM512 384h64v64h-64v-64zM256 192H128v-64h128v64z m512 384h-64v-64h64v64z m128 0h-64v-64h64v64zM768 256h128V448H768v-192z m256 384v-576c0-35-29-64-64-64H64c-35 0-64 29-64 64V640c0 35 29 64 64 64h896c35 0 64-29 64-64z m-64 0H64v-576h896V640zM384 384h64v64h-64v-64z m0 192h-64v-64h64v64zM256 384h64v64h-64v-64z m64-256h384v64H320v-64z m320 256h64v64h-64v-64z m-448-64h-64v-64h64v64z m320 0v-64h64v64h-64z m-128 0v-64h64v64h-64z m-64 0h-64v-64h64v64z m320-64h64v64h-64v-64z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="law" unicode="" d="M448 576c-53 0-96 43-96 96s43 96 96 96 96-43 96-96-43-96-96-96z m448-384c0-71-57-128-128-128h-64c-71 0-128 57-128 128l128 256h-64c-35 0-64 29-64 64h-64v-512c27 0 64-29 64-64h64c27 0 64-29 64-64H192c0 35 37 64 64 64h64c0 35 37 64 64 64h2l-2 512h-64c0-35-29-64-64-64h-64l128-256c0-71-57-128-128-128h-64C57 64 0 121 0 192l128 256H64v64h192c0 35 29 64 64 64h256c35 0 64-29 64-64h192v-64h-64l128-256zM160 384L64 192h192l-96 192z m672-192l-96 192-96-192h192z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="light-bulb" unicode="" d="M352 832C159 832 0 692 0 512c0-59 35-144 64-192 86-144 114-178 128-256v-64h320v64c14 78 42 112 128 256 29 48 64 133 64 192C704 692 545 832 352 832z m233-479c-16-28-30-51-43-71-55-90-80-132-93-207-1-3-1-7-1-11H256c0 4 0 8-1 11-13 75-38 117-93 207-13 20-27 43-43 71-27 45-55 117-55 159C64 653 193 768 352 768c78 0 151-27 206-76 53-48 82-112 82-180 0-42-28-114-55-159zM192-64h320c-15-73-83-128-160-128s-145 55-160 128z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="link" unicode="" d="M256 256h64v-64h-64c-96 0-192 108-192 224s99 224 192 224h256c93 0 192-108 192-224 0-90-58-174-128-208v74c37 29 64 81 64 134 0 82-65 160-128 160H256c-63 0-128-78-128-160s64-160 128-160z m576 192h-64v-64h64c64 0 128-78 128-160s-65-160-128-160H576c-63 0-128 78-128 160 0 53 27 105 64 134v74c-70-34-128-118-128-208 0-116 99-224 192-224h256c93 0 192 108 192 224s-96 224-192 224z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="link-external" unicode="" d="M704 192h64v-192c0-35-29-64-64-64H64c-35 0-64 29-64 64V640c0 35 29 64 64 64h192v-64H64v-640h640V192zM384 704l144-144-208-208 96-96 208 208 144-144V704H384z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="list-ordered" unicode="" d="M320 256h448v128h-448v-128z m0-256h448v128h-448v-128z m0 640v-128h448v128h-448z m-241-256h78v256h-36l-85-23v-50l43 2v-185z m110-206c0 36-12 78-96 78-33 0-64-6-83-16l1-66c21 10 42 15 67 15s32-11 32-28c0-26-30-58-110-112v-50h192v67l-91-2c49 30 87 66 87 113l1 1z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="list-unordered" unicode="" d="M0 256h128v128h-128v-128z m0 256h128v128h-128v-128z m0-512h128v128h-128v-128z m256 256h512v128h-512v-128z m0 256h512v128h-512v-128z m0-512h512v128h-512v-128z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="location" unicode="" d="M384 832C172 832 0 672 0 480c0-289 384-672 384-672s384 383 384 672C768 672 596 832 384 832z m0-931C265 31 64 292 64 480 64 639 208 768 384 768c86 0 167-31 228-87 59-55 92-126 92-201 0-188-201-449-320-579z m128 579c0-71-57-128-128-128s-128 57-128 128 57 128 128 128 128-57 128-128z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="lock" unicode="" d="M256 0h-64v64h64v-64z m512 384v-448c0-35-29-64-64-64H64c-35 0-64 29-64 64V384c0 35 29 64 64 64h64V576C128 717 243 832 384 832s256-115 256-256v-128h64c35 0 64-29 64-64z m-525 64h282V576c0 78-63 141-141 141s-141-63-141-141v-128z m461-64H128v-448h576V384z m-448-64h-64v-64h64v64z m0-128h-64v-64h64v64z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="logo-github" unicode="" d="M552.73 499.865H311.557c-6.205 0-11.25-5.045-11.25-11.297v-117.887c0-6.252 5.045-11.272 11.25-11.272h94.109v-146.542c0 0-21.145-7.057-79.496-7.057-68.914 0-165.156 25.244-165.156 236.795 0 211.642 100.197 239.491 194.307 239.491 81.465 0 116.514-14.304 138.869-21.241 7.01-2.203 13.404 4.831 13.404 11.105L534.543 785.87c0 2.912-1.041 6.417-4.262 8.785C521.186 801.048 465.865 832 326.168 832 165.133 832 0 763.513 0 434.243 0 105.02099999999996 189.051 56 348.381 56c131.883 0 212.021 56.314 212.021 56.314 3.268 1.801 3.6 6.395 3.6 8.479V488.568C563.955 494.773 558.887 499.865 552.73 499.865zM1772.381 803.866h-135.695c-6.252 0-11.271-5.044-11.271-11.296v-262.393h-211.619V792.57c0 6.252-5.068 11.296-11.178 11.296h-135.838c-6.111 0-11.084-5.044-11.084-11.296v-710.473c0-6.299 5.021-11.32 11.084-11.32h135.838c6.203 0 11.178 5.068 11.178 11.32V385.933h211.619l-0.475-303.883c0-6.3 5.021-11.272 11.084-11.272h135.885c6.252 0 11.131 5.068 11.131 11.272l0.473 710.521C1783.607 798.822 1778.539 803.866 1772.381 803.866zM714.949 787.763c-48.357 0-87.574-39.572-87.574-88.403 0-48.855 39.217-88.428 87.574-88.428s87.527 39.572 87.527 88.428C802.477 748.19 763.307 787.763 714.949 787.763zM792.861 559.874c0 6.205-5.02 11.344-11.131 11.344H646.32c-6.348 0-11.746-6.394-11.746-12.67 0 0 0-394.654 0-469.867 0-13.735 8.572-17.903 19.703-17.903 0 0 57.688 0 121.959 0 13.311 0 16.814 6.536 16.814 18.188-0.094 25.197-0.094 123.808-0.094 142.942C792.861 250.09500000000003 792.861 559.874 792.861 559.874zM2297.973 570.152h-134.701c-6.158 0-11.084-5.092-11.084-11.344v-348.31c0 0-34.244-25.197-82.934-25.197-48.547 0-61.525 22.024-61.525 69.719 0 47.553 0 303.835 0 303.835 0 6.252-5.068 11.345-11.131 11.345h-136.643c-6.252 0-11.178-5.093-11.178-11.345 0 0 0-185.521 0-326.807 0-141.284 78.766-175.906 186.99-175.906 88.854 0 160.609 49.115 160.609 49.115s3.363-25.766 5.068-28.844c1.422-3.078 5.447-6.158 9.852-6.158h86.58c6.158 0 11.178 5.069 11.178 11.321l0.379 477.278C2309.15 565.0609999999999 2304.129 570.152 2297.973 570.152zM2666.932 586.1610000000001c-76.539 0-128.592-34.148-128.592-34.148V792.57c0 6.252-5.068 11.296-11.131 11.296h-136.264c-6.109 0-11.131-5.044-11.131-11.296l-0.379-710.521c0-6.3 5.068-11.272 11.225-11.272 0 0 94.773 0 94.869 0 4.215 0 7.389 2.179 9.805 5.968 2.369 3.837 5.73 32.775 5.73 32.775s55.557-52.763 161.035-52.763c123.807 0 194.758 62.804 194.758 281.906C2856.859 557.482 2743.471 586.1610000000001 2666.932 586.1610000000001zM2613.791 185.77499999999998c-46.701 1.421-78.34 22.64-78.34 22.64v225.07c0 0 31.307 19.206 69.672 22.593 48.547 4.31 95.438-10.326 95.438-126.13C2700.322 207.94100000000003 2679.199 183.83399999999995 2613.791 185.77499999999998zM1185.125 188.33299999999997c-5.969 0-21.219-2.368-36.85-2.368-49.92 0-66.971 23.256-66.971 53.331 0 30.218 0 199.85 0 199.85h101.926c6.252 0 11.178 5.044 11.178 11.343v109.48c0.094 6.299-4.926 11.344-11.178 11.344h-101.926l-0.143 134.535c0 5.092-2.699 7.625-8.572 7.625H933.861c-5.352 0-8.336-2.391-8.336-7.578v-139.035c0 0-69.576-16.79-74.266-18.188-4.641-1.326-8.051-5.684-8.051-10.822v-87.408c0-6.252 5.068-11.344 11.178-11.344h71.139c0 0 0-91.34 0-210.222 0-156.109 109.553-171.455 183.439-171.455 33.723 0 74.076 10.988 80.848 13.356 4.074 1.421 6.395 5.637 6.395 10.136l0.047 96.101C1196.254 183.312 1190.998 188.428 1185.125 188.33299999999997z" horiz-adv-x="2856.857" />
|
||||
<glyph glyph-name="mail" unicode="" d="M0 576v-512c0-35 29-64 64-64h768c35 0 64 29 64 64V576c0 35-29 64-64 64H64c-35 0-64-29-64-64z m832 0L448 256 64 576h768zM64 480l256-192L64 96V480z m64-416l224 192 96-96 96 96 224-192H128z m704 32L576 288l256 192v-384z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="mail-read" unicode="" d="M384 512H256v64h128v-64z m192-64H256v-64h320v64z m320 31v-543c0-35-29-64-64-64H64c-35 0-64 29-64 64V479c0 21 10 40 27 52l101 72v37c0 35 29 64 64 64h77L448 832l179-128h77c35 0 64-29 64-64v-37l101-72c17-12 27-31 27-52zM192 352l256-160 256 160V640H192v-288zM64-32l288 192L64 352v-384z m704-32L448 128 128-64h640z m64 416L544 160l288-192V352z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="mail-reply" unicode="" d="M384 672l-384-288 384-288v192c111 0 329-61 384-280 0 291-196 451-384 472v192z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="mark-github" unicode="" d="M512 832C229.252 832 0 602.748 0 320c0-226.251 146.688-418.126 350.155-485.813 25.593-4.686 34.937 11.125 34.937 24.626 0 12.188-0.469 52.562-0.718 95.314-128.708-23.46-161.707 31.541-172.469 60.373-5.525 14.809-30.407 60.249-52.398 72.263-17.988 9.828-43.26 33.237-0.917 33.735 40.434 0.476 69.348-37.308 78.471-52.75 45.938-77.749 119.876-55.627 148.999-42.5 4.654 32.999 17.902 55.627 32.501 68.373-113.657 12.939-233.22 56.875-233.22 253.063 0 55.94 19.968 101.561 52.658 137.404-5.22 12.999-22.844 65.095 5.063 135.563 0 0 42.937 13.749 140.811-52.501 40.811 11.406 84.594 17.031 128.124 17.22 43.499-0.188 87.314-5.874 128.188-17.28 97.689 66.311 140.686 52.501 140.686 52.501 28-70.532 10.375-122.564 5.124-135.499 32.811-35.844 52.626-81.468 52.626-137.404 0-196.686-119.751-240-233.813-252.686 18.439-15.876 34.748-47.001 34.748-94.748 0-68.437-0.686-123.627-0.686-140.501 0-13.625 9.312-29.561 35.25-24.562C877.436-97.99800000000005 1024 93.87400000000002 1024 320 1024 602.748 794.748 832 512 832z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="markdown" unicode="" d="M950.154 640H73.846C33.127 640 0 606.873 0 566.154v-492.308C0 33.125 33.127 0 73.846 0h876.308c40.721 0 73.846 33.125 73.846 73.846V566.154C1024 606.873 990.875 640 950.154 640zM576 128.125L448 128V320l-96-123.077L256 320v-192H128V512h128l96-128 96 128 128 0.125V128.125zM767.091 96.125L608 320h96V512h128v-192h96L767.091 96.125z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="megaphone" unicode="" d="M640 768c-11 0-23-3-33-9-92-56-319-220-415-247-88 0-192-43-192-160s104-160 192-160c19-5 41-15 64-26v-294h128V93c86-55 172-117 223-148 10-6 22-9 33-9 33 0 64 27 64 64V704c0 37-31 64-64 64z m0-768c-24 15-57 37-96 64-10 7-21 14-32 22V620c10 7 20 13 30 20 39 26 74 49 98 64v-704z m128 384h256v-64H768v64z m0-128l256-128v-64L768 192v64z m256 384v-64L768 448v64l256 128z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="mention" unicode="" d="M466.697 732.899C238.66 760.898 31.1 598.735 3.102 370.698c-28-228.038 134.163-435.598 362.2-463.597 71.429-8.756 145.115 0.913 213.325 29.946l-0.016 0.032c24.404 10.357 35.788 38.538 25.431 62.939-10.359 24.403-38.538 35.787-62.94 25.43l-0.001 0.004c-52.472-22.339-109.15-29.799-164.1-23.067-175.413 21.538-300.153 181.2-278.616 356.613 21.538 175.413 181.199 300.154 356.613 278.616 175.412-21.538 300.154-181.199 278.617-356.612-4.309-35.083-21.542-55.725-61.6-55.725-42.5 0-64 45.889-64 81.222V432c0 26.51-21.49 48-48 48-9.699 0-18.72-2.887-26.269-7.833-25.684 20.259-57.437 33.87-94.349 38.402-105.246 12.923-201.045-61.924-213.967-167.17C212.508 238.15200000000004 287.354 142.35400000000004 392.6 129.43200000000002c57.379-7.045 116.216 14.707 157.871 53.13 24.959-28.124 59.866-47.624 100.121-52.567 87.707-10.769 167.537 51.602 178.307 139.309C856.898 497.34 694.734 704.899 466.697 732.899zM511.285 308.30100000000004c-6.462-52.623-54.361-90.047-106.985-83.585-52.623 6.461-90.046 54.36-83.585 106.984 6.461 52.623 54.361 90.046 106.984 83.585C480.322 408.823 517.746 360.924 511.285 308.30100000000004z" horiz-adv-x="832" />
|
||||
<glyph glyph-name="milestone" unicode="" d="M512 704H384V832h128v-128z m256-320H128c-35 0-64 29-64 64V576c0 35 29 64 64 64h640l128-128-128-128zM512 576H384v-128h128V576z m-128-768h128V320H384v-512z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="mirror" unicode="" d="M992 531L544 832 96 531c-19-12-32-29-32-51v-672l480 256 480-256V480c0 22-13 39-32 51z m-32-627L576 112v80h-64v-80L128-96V480L512 736v-288h64V736l384-256v-576zM384 384h320V512l192-192-192-192V256H384v-128L192 320l192 192v-128z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="mortar-board" unicode="" d="M501 244l-245 76s0-96 0-160 115-96 256-96 256 32 256 96 0 160 0 160l-245-76c-7-2-15-2-23 0h1z m18 409c-4 1-9 1-13 0l-489-152c-21-7-21-36 0-43l111-35v-113c-19-11-32-32-32-55 0-12 3-23 9-32-5-9-9-20-9-32v-165c0-35 128-35 128 0v165c0 12-3 23-9 32 5 9 9 20 9 32 0 24-13 44-32 55v93l313-98c4-1 9-1 13 0l489 152c21 7 21 36 0 43l-488 153z m-6-205c-35 0-64 14-64 32s29 32 64 32 64-14 64-32-29-32-64-32z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="mute" unicode="" d="M512 652v-664c0-43-52-64-82-34L192 192H64c-35 0-64 29-64 64V384c0 35 29 64 64 64h128l238 238c30 30 82 9 82-34z m482-206l-68 68-126-126-126 126-68-68 126-126-126-126 68-68 126 126 126-126 68 68-126 126 126 126z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="no-newline" unicode="" d="M1024 512v-192c0-35-29-64-64-64H768v-128L576 320l192 192v-128h128V512h128zM512 320c0-141-115-256-256-256S0 179 0 320s115 256 256 256 256-115 256-256zM96 214l266 266c-31 20-67 32-106 32-106 0-192-86-192-192 0-39 12-75 32-106z m352 106c0 39-12 75-32 106L150 160c31-20 67-32 106-32 106 0 192 86 192 192z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="octoface" unicode="" d="M940.812 554.312c8.25 20.219 35.375 101.75-8.562 211.906 0 0-67.375 21.312-219.875-82.906C648.5 700.875 579.875 703.5 512 703.5c-67.906 0-136.438-2.625-200.5-20.25C159.031 787.531 91.719 766.219 91.719 766.219 47.812 656 74.938 574.531 83.188 554.312 31.5 498.438 0 427.125 0 339.656 0 10.437999999999988 213.25-64 510.844-64 808.562-64 1024 10.437999999999988 1024 339.656 1024 427.125 992.5 498.438 940.812 554.312zM512-1c-211.406 0-382.781 9.875-382.781 214.688 0 48.938 24.062 94.595 65.344 132.312 68.75 62.969 185.281 29.688 317.438 29.688 132.25 0 248.625 33.281 317.438-29.625 41.312-37.78 65.438-83.312 65.438-132.312C894.875 8.875 723.375-1 512-1zM351.156 319.562c-42.469 0-76.906-51.062-76.906-114.188s34.438-114.312 76.906-114.312c42.375 0 76.812 51.188 76.812 114.312S393.531 319.562 351.156 319.562zM672.875 319.562C630.5 319.562 596 268.5 596 205.375s34.5-114.312 76.875-114.312 76.812 51.188 76.812 114.312C749.75 268.5 715.312 319.562 672.875 319.562z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="organization" unicode="" d="M304 515c35-41 86-67 144-67s109 26 144 67c22-40 64-67 112-67 71 0 128 57 128 128s-57 128-128 128c-26 0-49-8-69-21C615 768 539 832 448 832S281 768 261 683c-20 13-43 21-69 21-71 0-128-57-128-128s57-128 128-128c48 0 90 27 112 67z m333 97c13 24 38 41 67 41 42 0 77-35 77-77s-35-77-77-77-75 34-76 75c4 12 7 25 9 38zM448 769c71 0 129-58 129-129s-58-129-129-129-129 58-129 129S377 769 448 769zM192 499c-42 0-77 35-77 77s35 77 77 77c29 0 54-17 67-41 2-13 5-26 9-38-1-41-34-75-76-75z m640-51H64c-35 0-64-29-64-64v-192c0-35 29-64 64-64v-128c0-35 29-64 64-64h64c35 0 64 29 64 64v64h64v-192c0-35 29-64 64-64h128c35 0 64 29 64 64V64h64v-64c0-35 29-64 64-64h64c35 0 64 29 64 64V128c35 0 64 29 64 64V384c0 35-29 64-64 64zM192 0h-64V192H64V384h128v-384z m448 128h-64V256h-64v-384H384V256h-64v-128h-64V384h384v-256z m192 64h-64v-192h-64V384h128v-192z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="package" unicode="" d="M0 559v-478c0-29 19-54 48-62l416-111c10-3 22-3 32 0l416 111c29 8 48 33 48 62V559c0 29-19 54-48 62L496 732c-10 2-22 2-32 0L48 621c-29-8-48-33-48-62z m448-582L64 79V512l384-103v-432zM64 576l160 43 416-111-160-43L64 576z m832-497L512-23V409l128 35v-156l128 34V478l128 34v-433zM768 542L352 653l128 34 416-111-128-34z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="paintcan" unicode="" d="M384 832C171.923 832 0 660.077 0 448v-64c0-35.346 28.654-64 64-64v-320c0-70.692 143.269-128 320-128s320 57.308 320 128V320c35.346 0 64 28.654 64 64v64C768 660.077 596.077 832 384 832zM576 192v-32c0-17.673-14.327-32-32-32s-32 14.327-32 32v32c0 17.673-14.327 32-32 32s-32-14.327-32-32v-160c0-17.673-14.327-32-32-32s-32 14.327-32 32V160c0 17.673-14.327 32-32 32s-32-14.327-32-32v-32c0-35.346-28.654-64-64-64s-64 28.654-64 64v64c-35.346 0-64 28.654-64 64V371.193C186.382 340.108 279.318 320 384 320s197.618 20.108 256 51.193V256C640 220.654 611.346 192 576 192zM384 384c-107.433 0-199.393 26.474-237.372 64 37.979 37.526 129.939 64 237.372 64s199.393-26.474 237.372-64C583.393 410.474 491.433 384 384 384zM384 576c-176.62 0-319.816-57.236-319.996-127.867-0.001 0.001-0.002 0.001-0.003 0.002C64.075 624.804 207.314 768 384 768c176.731 0 320-143.269 320-320C704 518.692 560.731 576 384 576z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="pencil" unicode="" d="M0 64v-192h192l512 512-192 192L0 64z m192-128H64V64h64v-64h64v-64z m659 595l-83-83-192 192 83 83c25 25 65 25 90 0l102-102c25-25 25-65 0-90z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="person" unicode="" d="M448 448H64c-35 0-64-29-64-64v-320h128v-192c0-35 29-64 64-64h128c35 0 64 29 64 64V64h128V384c0 35-29 64-64 64z m0-320h-64V256h-64v-384H192V256h-64v-128H64V384h384v-256z m0 512C448 746 362 832 256 832S64 746 64 640s86-192 192-192 192 86 192 192zM256 512c-71 0-128 57-128 128S185 768 256 768s128-57 128-128-57-128-128-128z" horiz-adv-x="512" />
|
||||
<glyph glyph-name="pin" unicode="" d="M640 755v-51l32-64-288-192H141c-28 0-43-34-22-55l201-201L64-128l320 256 201-201c21-21 55-6 55 22V192l192 288 64-32h51c28 0 43 34 22 55L695 777c-21 21-55 6-55-22z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="plug" unicode="" d="M960 448v64H704V640H576v-64H448c-66 0-113-52-128-128l-64-64c-106 0-192-86-192-192v-128h64V192c0 71 57 128 128 128l64-64c16-74 63-128 128-128h128v-64h128V192h256v64H704V448h256z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="plus" unicode="" d="M768 256H448v-320H320V256H0V384h320V704h128v-320h320v-128z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="primitive-dot" unicode="" d="M-0.088 320c0 141.5 114.5 256 256 256 141.438 0 256-114.5 256-256s-114.562-256-256-256C114.413 64-0.088 178.5-0.088 320z" horiz-adv-x="511.825" />
|
||||
<glyph glyph-name="primitive-square" unicode="" d="M512 64H0V576h512V64z" horiz-adv-x="512" />
|
||||
<glyph glyph-name="pulse" unicode="" d="M736 320.062L563.188 486.406 422.406 288 352 729.594 152.438 320.062H0V192h230.406L288 307.188l57.594-345.562L576 288l102.375-96H896V320.062H736z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="question" unicode="" d="M384 192h128v-128H384V192z m256 224c0-137-128-160-128-160H384c0 35 29 64 64 64h32c18 0 32 14 32 32v64c0 18-14 32-32 32h-64c-18 0-32-14-32-32v-32H256c0 96 96 192 192 192s192-64 192-160zM448 685c201 0 365-164 365-365S649-45 448-45 83 119 83 320s164 365 365 365m0 83C201 768 0 567 0 320s201-448 448-448 448 201 448 448S695 768 448 768z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="quote" unicode="" d="M0 320v-256h256V320H128c0 0 0 128 128 128V576C256 576 0 576 0 320zM640 448V576c0 0-256 0-256-256v-256h256V320H512C512 320 512 448 640 448z" horiz-adv-x="640" />
|
||||
<glyph glyph-name="radio-tower" unicode="" d="M306.838 441.261c15.868 16.306 15.868 42.731 0 59.037-20.521 21.116-30.643 48.417-30.705 76.124 0.062 27.77 10.183 55.039 30.705 76.186 15.868 16.337 15.868 42.764 0 59.069-7.934 8.184-18.272 12.275-28.706 12.275-10.371 0-20.804-4.029-28.738-12.213-36.266-37.297-54.633-86.433-54.57-135.317-0.062-48.792 18.305-97.927 54.57-135.161C265.262 424.955 290.97 424.955 306.838 441.261zM149.093 798.858c-8.121 8.309-18.68 12.463-29.3 12.463-10.558 0-21.179-4.154-29.237-12.463C30.8 737.509 0.751 656.856 0.813 576.422 0.751 496.081 30.8 415.272 90.494 353.985c16.181-16.618 42.356-16.618 58.537 0 16.118 16.587 16.118 43.513 0 60.067-43.7 44.98-65.44 103.456-65.44 162.368s21.74 117.449 65.44 162.368C165.149 755.439 165.149 782.365 149.093 798.858zM513.031 472.153c57.351 0 103.956 46.574 103.956 103.956 0 57.382-46.605 103.955-103.956 103.955-57.381 0-103.956-46.573-103.956-103.955C409.076 518.727 455.65 472.153 513.031 472.153zM933.539 798.233c-16.181 16.618-42.355 16.618-58.475 0-16.181-16.587-16.181-43.513 0-60.068 43.668-44.918 65.409-103.456 65.409-162.368 0-58.85-21.805-117.387-65.473-162.306-16.117-16.618-16.117-43.575 0.062-60.068 8.059-8.309 18.616-12.463 29.237-12.463 10.558 0 21.178 4.154 29.236 12.463 59.726 61.287 89.774 142.096 89.649 222.437C1023.313 656.138 993.264 736.947 933.539 798.233zM513.281 389.127L513.281 389.127c-26.489-0.062-53.04 6.466-77.091 19.429L235.057-127.59000000000003h95.209l54.819 63.973h255.891l53.977-63.973h95.272L589.124 408.431C565.384 395.655 539.395 389.127 513.281 389.127zM512.656 358.483L577.004 128.29999999999995H449.059L512.656 358.483zM385.086 0.3550000000000182l63.974 63.973h127.944l63.974-63.973H385.086zM717.194 710.958c-15.868-16.306-15.868-42.731 0-59.037 20.491-21.116 30.611-48.511 30.674-76.124-0.062-27.77-10.183-55.102-30.674-76.187-15.868-16.336-15.868-42.763 0-59.068 7.871-8.184 18.242-12.213 28.737-12.213 10.309 0 20.741 4.029 28.675 12.213 36.298 37.234 54.665 86.433 54.54 135.255 0.125 48.792-18.181 97.927-54.54 135.161C758.801 727.264 733.062 727.264 717.194 710.958z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="repo" unicode="" d="M256 256h-64v64h64v-64z m0 192h-64v-64h64v64z m0 128h-64v-64h64v64z m0 128h-64v-64h64v64z m512 64v-768c0-35-29-64-64-64H384v-128l-96 96-96-96V-64H64c-35 0-64 29-64 64V768C0 803 29 832 64 832h640c35 0 64-29 64-64z m-64-640H64v-128h128v64h192v-64h320V128z m0 640H128v-576h576V768z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="repo-clone" unicode="" d="M960 832H576v-448c0-35 29-64 64-64h64v-64h64v64h192c35 0 64 29 64 64V768c0 35-29 64-64 64zM704 384h-64v64h64v-64z m256 0H768v64h192v-64z m0 128H704V768h256v-256z m-704 0h-64v64h64v-64z m0 128h-64v64h64v-64zM128 768h384V832H64C29 832 0 803 0 768v-768c0-35 29-64 64-64h128v-128l96 96 96-96V-64h320c35 0 64 29 64 64V192H128V768z m576-640v-128H384v64H192v-64H64V128h640zM192 320h64v-64h-64v64z m64 64h-64v64h64v-64z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="repo-force-push" unicode="" d="M640 256H512v-448H384V256H256l144 192H256l192 256 192-256H496l144-192zM704 832H64C29 832 0 803 0 768v-768c0-35 29-64 64-64h256v64H64V128h256v64H128V768h576v-576H576v-64h128v-128H576v-64h128c35 0 64 29 64 64V768c0 35-29 64-64 64z" horiz-adv-x="767.896" />
|
||||
<glyph glyph-name="repo-forked" unicode="" d="M512 768c-71 0-128-57-128-128 0-47 26-88 64-110v-82L320 320 192 448v82c38 22 64 63 64 110 0 71-57 128-128 128S0 711 0 640c0-47 26-88 64-110v-114l192-192v-114c-38-22-64-63-64-110 0-71 57-128 128-128s128 57 128 128c0 47-26 88-64 110V224l192 192V530c38 22 64 63 64 110 0 71-57 128-128 128zM128 563c-42 0-77 35-77 77s35 77 77 77 77-35 77-77-35-77-77-77z m192-640c-42 0-77 35-77 77s35 77 77 77 77-35 77-77-35-77-77-77z m192 640c-42 0-77 35-77 77s35 77 77 77 77-35 77-77-35-77-77-77z" horiz-adv-x="640" />
|
||||
<glyph glyph-name="repo-pull" unicode="" d="M832 320V448H448V576h384V704l192-192-192-192zM256 704h-64v-64h64v64z m448-320h64v-384c0-35-29-64-64-64H384v-128l-96 96-96-96V-64H64c-35 0-64 29-64 64V768C0 803 29 832 64 832h640c35 0 64-29 64-64v-128h-64V768H128v-576h576V384z m0-256H64v-128h128v64h192v-64h320V128zM256 448h-64v-64h64v64z m0 128h-64v-64h64v64z m-64-320h64v64h-64v-64z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="repo-push" unicode="" d="M256 640h-64v64h64v-64z m-64-128h64v64h-64v-64z m256 0L256 256h128v-448h128V256h128L448 512zM704 832H64C29 832 0 803 0 768v-768c0-35 29-64 64-64h256v64H64V128h256v64H128V768h577l-1-576H576v-64h128v-128H576v-64h128c35 0 64 29 64 64V768c0 35-29 64-64 64z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="rocket" unicode="" d="M1024 832s-6-24-19-68c-13-45-35-101-68-170-45 5-81 21-106 46s-40 60-45 105c69 33 125 56 169 69 45 13 69 18 69 18zM779 587c-17 17-30 35-40 56-10 20-17 42-22 65-37-21-74-45-111-72-37-28-73-60-108-95-45-45-85-116-114-157H192L0 192h192l128 128c-22-49-65-191-64-192l64-64c1-1 143 41 192 64L384 0v-192l192 192V192c41 29 112 70 157 114 35 35 67 72 94 109 28 37 52 74 73 110-23 5-45 12-66 22-20 10-38 23-55 40z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="rss" unicode="" d="M128 0H0V128c71 0 128-57 128-128zM0 640v-64c318 0 576-258 576-576h64c0 353-287 640-640 640z m0-256v-64c176 0 320-144 320-320h64c0 212-172 384-384 384z" horiz-adv-x="640" />
|
||||
<glyph glyph-name="ruby" unicode="" d="M832 448L512 128V576h192l128-128z m192 0L512-64 0 448l256 256h512l256-256zM512 32l416 416-192 192H288L96 448l416-416z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="screen-full" unicode="" d="M832 192h64v-192c0-35-29-64-64-64H640v64h192V192z m-768 0H0v-192c0-35 29-64 64-64h192v64H64V192z m0 448h192v64H64c-35 0-64-29-64-64v-192h64V640z m64-64h640v-512H128V576z m128-384h384V448H256v-256z m576 512H640v-64h192v-192h64V640c0 35-29 64-64 64z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="screen-normal" unicode="" d="M128 576H0v64h128V768h64v-128c0-35-29-64-64-64z m0-512H0v-64h128v-128h64V0c0 35-29 64-64 64z m576 128c0-35-29-64-64-64H256c-35 0-64 29-64 64V448c0 35 29 64 64 64h384c35 0 64-29 64-64v-256zM576 384H320v-128h256V384z m128-384v-128h64V0h128v64H768c-35 0-64-29-64-64z m64 640V768h-64v-128c0-35 29-64 64-64h128v64H768z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="search" unicode="" d="M1005-83L761 162c45 63 71 139 71 222 0 212-172 384-384 384S64 596 64 384s172-384 384-384c83 0 159 26 222 71l245-244c12-13 29-19 45-19s33 6 45 19c25 25 25 65 0 90zM448 83c-166 0-301 135-301 301s135 301 301 301 301-135 301-301-135-301-301-301z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="server" unicode="" d="M704 448H64c-35 0-64-29-64-64v-128c0-35 29-64 64-64h640c35 0 64 29 64 64V384c0 35-29 64-64 64zM128 256H64V384h64v-128z m128 0h-64V384h64v-128z m128 0h-64V384h64v-128z m128 0h-64V384h64v-128zM704 768H64C29 768 0 739 0 704v-128c0-35 29-64 64-64h640c35 0 64 29 64 64V704c0 35-29 64-64 64zM128 576H64V704h64v-128z m128 0h-64V704h64v-128z m128 0h-64V704h64v-128z m128 0h-64V704h64v-128z m192 64h-64v64h64v-64z m0-512H64c-35 0-64-29-64-64v-128c0-35 29-64 64-64h640c35 0 64 29 64 64V64c0 35-29 64-64 64zM128-64H64V64h64v-128z m128 0h-64V64h64v-128z m128 0h-64V64h64v-128z m128 0h-64V64h64v-128z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="settings" unicode="" d="M192 384h-64V704h64v-320z m-64-448h64V128h-64v-192z m320 0h64V320h-64v-384z m320 0h64V64h-64v-128z m64 768h-64v-384h64V704z m-320 0h-64v-128h64V704zM256 320H64c-35 0-64-29-64-64s29-64 64-64h192c35 0 64 29 64 64s-29 64-64 64z m320 192H384c-35 0-64-29-64-64s29-64 64-64h192c35 0 64 29 64 64s-29 64-64 64z m320-256H704c-35 0-64-29-64-64s29-64 64-64h192c35 0 64 29 64 64s-29 64-64 64z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="shield" unicode="" d="M448 832L0 704v-385c0-299 340-511 448-511s448 212 448 511V704L448 832zM320 128l73 179c3 15-4 30-16 38-36 23-57 61-57 103 0 70 57 128 127 128 69 0 129-58 129-128 0-42-21-80-57-103-12-8-19-23-16-38l73-179H320z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="sign-in" unicode="" d="M384 400v-336h256V320h64v-256c0-35-29-64-64-64H384v-192L35-18c-21 11-35 33-35 58V768C0 803 29 832 64 832h576c35 0 64-29 64-64v-192h-64V768H128l256-128v-144l192 144v-128h256v-128H576v-128L384 400z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="sign-out" unicode="" d="M768 256V384H512V512h256V640l256-192-256-192zM640 64H384V640L128 768h512v-192h64V768c0 35-29 64-64 64H64C29 832 0 803 0 768v-728c0-25 14-47 35-58l349-174V0h256c35 0 64 29 64 64V320h-64v-256z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="squirrel" unicode="" d="M768 768c-141.385 0-256-83.75-256-186.875C512 457.25 544 387 512 192c0 288-177 405.783-256 405.783 3.266 32.17-30.955 42.217-30.955 42.217s-14-7.124-19.354-21.583c-17.231 20.053-36.154 17.54-36.154 17.54l-8.491-37.081c0 0-117.045-40.876-118.635-206.292C56 371 141.311 353.898 201.887 364.882c57.157-2.956 42.991-50.648 30.193-63.446C178.083 247.438 128 320 64 320s-64-64 0-64 64-64 192-64c-198-77 0-256 0-256h-64c-64 0-64-64-64-64s256 0 384 0c192 0 320 64 320 222.182 0 54.34-27.699 114.629-64 162.228C697.057 349.433 782.453 427.566 832 384s192-64 192 128C1024 653.385 909.385 768 768 768zM160 448c-17.674 0-32 14.327-32 32 0 17.674 14.326 32 32 32 17.673 0 32-14.326 32-32C192 462.327 177.673 448 160 448z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="star" unicode="" d="M896 448l-313.5 40.781L448 768 313.469 488.781 0 448l230.469-208.875L171-63.93799999999999l277 148.812 277.062-148.812L665.5 239.125 896 448z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="stop" unicode="" d="M640 768H256L0 512v-384l256-256h384l256 256V512L640 768z m192-608L608-64H288L64 160V480l224 224h320l224-224v-320zM384 576h128v-320H384V576z m0-384h128v-128H384V192z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="sync" unicode="" d="M655.461 358.531c11.875-81.719-13.062-167.781-76.812-230.594-94.188-92.938-239.5-104.375-346.375-34.562l74.875 73L31.96 204.75 70.367-64l84.031 80.5c150.907-111.25 364.938-100.75 502.063 34.562 79.5 78.438 115.75 182.562 111.25 285.312L655.461 358.531zM189.46 511.938c94.156 92.938 239.438 104.438 346.313 34.562l-75-72.969 275.188-38.406L697.586 704l-83.938-80.688C462.711 734.656 248.742 724.031 111.585 588.75 32.085 510.344-4.133 406.219 0.335 303.5l112.25-22.125C100.71 363.125 125.71 449.094 189.46 511.938z" horiz-adv-x="768.051" />
|
||||
<glyph glyph-name="tag" unicode="" d="M431 657c-30 30-71 47-113 47H160C72 704 0 632 0 544v-158c0-42 17-83 47-113l388-388c25-25 65-25 90 0l294 294c25 25 25 65 0 90L431 657zM88 314c-20 19-30 45-30 72V544c0 56 46 102 102 102h158c27 0 53-10 72-30l393-392-303-303L88 314z m40 262h128v-128H128V576z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="telescope" unicode="" d="M512 256l192-384h-64L512 128v-320h-64V192L320-128h-64l128 320 128 64zM448 832h-64v-64h64V832zM320 640h-64v-64h64v64zM128 768H64v-64h64V768zM40 256c-14-10-18-28-10-43l35-59c8-15 26-20 41-13l89 42-74 128-81-55z m505 345L174 348l79-137 405 194-113 196z m270-82l-94 161c-9 16-30 21-46 11l-77-53 118-205 85 41c17 8 23 28 14 45z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="terminal" unicode="" d="M448 192h256v-64H448v64z m-192-64l192 192-192 192-48-48 144-144-144-144 48-48z m640 512v-640c0-35-29-64-64-64H64c-35 0-64 29-64 64V640c0 35 29 64 64 64h768c35 0 64-29 64-64z m-64 0H64v-640h768V640z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="three-bars" unicode="" d="M0 640v-128h768v128h-768z m0-384h768v128h-768v-128z m0-256h768v128h-768v-128z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="thumbsdown" unicode="" d="M871 347c9 19 15 40 15 62 0 51-28 96-69 120 4 13 6 27 6 41 0 50-26 93-65 118 2 8 10 19 10 27C768 781 709 832 640 832c0 0-212 0-222 0-88 0-170-43-242-81-42-22-89-47-113-47H0v-576h64c37-2 155-69 206-112 12-10 173-168 173-168 26-26 60-40 96-40 35 0 68 13 92 38 51 51 50 135-2 188-20 20-94 115-117 138l256-44c76 0 128 59 128 131 0 34-3 64-25 88zM768 192l-384 64c-7 0 200-266 200-266 28-29 29-73 3-100-13-13-30-19-48-19s-37 7-52 21L347 34c-86 71-216 158-283 158V642c87 0 221 126 352 126h224c34 0 64-20 64-53 0-34-30-75-64-75h48c45 0 72-25 72-70 0-44-36-90-81-90h63c45 0 81-26 81-71 0-44-36-80-81-80h27c42 0 63-32 63-70 0-39-23-67-64-67z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="thumbsup" unicode="" d="M896 381c0 72-52 131-128 131l-256-44c23 23 97 118 117 138 52 53 53 137 2 188-24 25-57 38-92 38-36 0-70-14-96-40 0 0-161-158-173-168-51-43-169-110-206-112H0v-576h63c24 0 71-25 113-47 72-38 154-81 242-81 10 0 222 0 222 0 69 0 128 51 128 117 0 8-8 19-10 27 39 25 65 68 65 118 0 14-2 28-6 41 41 24 69 69 69 120 0 22-6 43-15 62 22 24 25 54 25 88z m-64 0c0-38-21-70-63-70h-27c45 0 81-36 81-80 0-45-36-71-81-71h-63c45 0 81-46 81-90 0-45-27-70-72-70h-48c34 0 64-41 64-75 0-33-30-53-64-53H416c-131 0-265 126-352 126V448c67 0 197 87 283 158L487 748c15 14 34 21 52 21s35-6 48-19c26-27 25-71-3-100 0 0-207-266-200-266l384 64c41 0 64-28 64-67z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="tools" unicode="" d="M286.547 366.984c16.843-16.812 81.716-85.279 81.716-85.279l35.968 37.093-56.373 58.248L456.072 491.98c0 0-48.842 47.623-27.468 28.655 20.438 75.903 1.812 160.589-55.842 220.243C315.608 800.064 234.392 819.47 161.425 799.096l123.653-127.715-32.53-125.309-121.06-33.438L7.898 640.3820000000001c-19.718-75.436-0.969-159.339 56.311-218.556C124.302 359.703 210.83 341.453 286.547 366.984zM698.815 242.769L549.694 95.46100000000001l245.932-254.805c20.062-20.812 46.498-31.188 72.872-31.188 26.25 0 52.624 10.375 72.811 31.188 40.249 41.624 40.249 108.997 0 150.62L698.815 242.769zM1023.681 670.162L867.06 832.001 405.387 354.703l56.373-58.248L185.425 10.839000000000055l-63.154-33.749-89.217-145.559 22.719-23.562 140.839 92.247 32.655 65.312 276.336 285.554 56.404-58.248L1023.681 670.162z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="trashcan" unicode="" d="M640 704H512c0 35-29 64-64 64H256c-35 0-64-29-64-64H64c-35 0-64-29-64-64v-64c0-35 29-64 64-64v-576c0-35 29-64 64-64h448c35 0 64 29 64 64V512c35 0 64 29 64 64v64c0 35-29 64-64 64z m-64-768H128V512h64v-512h64V512h64v-512h64V512h64v-512h64V512h64v-576z m64 640H64v64h576v-64z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="triangle-down" unicode="" d="M0 448l383.75-383.75L767.5 448H0z" horiz-adv-x="767.5" />
|
||||
<glyph glyph-name="triangle-left" unicode="" d="M0 320.125l383.75-383.75v767.5L0 320.125z" horiz-adv-x="383.75" />
|
||||
<glyph glyph-name="triangle-right" unicode="" d="M0.062 703.75L383.812 320 0.062-63.75V703.75z" horiz-adv-x="383.875" />
|
||||
<glyph glyph-name="triangle-up" unicode="" d="M383.75 576L0 192.25h767.5L383.75 576z" horiz-adv-x="767.5" />
|
||||
<glyph glyph-name="unfold" unicode="" d="M736 288l160-160c0-35-29-64-64-64H576v64h224L672 256H224L96 128h224v-64H64c-35 0-64 29-64 64l160 160L0 448c0 35 29 64 64 64h256v-64H96l128-128h448l128 128H576v64h256c35 0 64-29 64-64L736 288z m-352 96h128V576h128L448 768 256 576h128v-192z m128-192H384v-192H256l192-192 192 192H512V192z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="unmute" unicode="" d="M704 319c0-70-29-134-75-181l-43 43c35 36 57 84 57 138s-22 103-57 138l43 43c46-46 75-110 75-181zM430 686L192 448H64c-35 0-64-29-64-64v-128c0-35 29-64 64-64h128l238-238c30-30 82-9 82 34V652c0 43-52 64-82 34z m380-5l-43-43c82-82 132-194 132-319 0-124-50-237-132-319l43-43c93 93 150 221 150 362 0 142-57 270-150 362z m-90-90l-44-43c59-59 95-140 95-229s-36-170-95-228l44-43c69 69 112 165 112 271s-43 202-112 272z" horiz-adv-x="1024" />
|
||||
<glyph glyph-name="versions" unicode="" d="M832 640H448c-35 0-64-29-64-64v-512c0-35 29-64 64-64h384c35 0 64 29 64 64V576c0 35-29 64-64 64z m-64-512H512V512h256v-384zM256 576h64v-64h-64v-384h64v-64h-64c-35 0-64 29-64 64V512c0 35 29 64 64 64zM64 512h64v-64H64v-256h64v-64H64c-35 0-64 29-64 64V448c0 35 29 64 64 64z" horiz-adv-x="896" />
|
||||
<glyph glyph-name="watch" unicode="" d="M384 320h128v-64H320V512h64v-192z m384 0c0-142-77-266-192-332v-116c0-35-29-64-64-64H256c-35 0-64 29-64 64V-12C77 54 0 178 0 320s77 266 192 332V768c0 35 29 64 64 64h256c35 0 64-29 64-64v-116c115-66 192-190 192-332z m-64 0c0 177-143 320-320 320S64 497 64 320s143-320 320-320 320 143 320 320z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="x" unicode="" d="M479 320l240-240-95-95-240 240-240-240-95 95 240 240L49 560l95 95 240-240 240 240 95-95-240-240z" horiz-adv-x="768" />
|
||||
<glyph glyph-name="zap" unicode="⚡" d="M640 384H384L576 832 0 256h256L64-192 640 384z" horiz-adv-x="640" />
|
||||
</font>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 66 KiB |
BIN
src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf
Normal file
BIN
src/vs/base/browser/ui/octiconLabel/octicons/octicons.woff
Normal file
217
src/vs/base/browser/ui/octiconLabel/octicons/sprockets-octicons.scss
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
@font-face {
|
||||
font-family: 'octicons';
|
||||
src: font-url('octicons.eot?#iefix') format('embedded-opentype'),
|
||||
font-url('octicons.woff') format('woff'),
|
||||
font-url('octicons.ttf') format('truetype'),
|
||||
font-url('octicons.svg#octicons') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
// .octicon is optimized for 16px.
|
||||
// .mega-octicon is optimized for 32px but can be used larger.
|
||||
.octicon, .mega-octicon {
|
||||
font: normal normal normal 16px/1 octicons;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.mega-octicon { font-size: 32px; }
|
||||
|
||||
.octicon-alert:before { content: '\f02d'} /* */
|
||||
.octicon-arrow-down:before { content: '\f03f'} /* */
|
||||
.octicon-arrow-left:before { content: '\f040'} /* */
|
||||
.octicon-arrow-right:before { content: '\f03e'} /* */
|
||||
.octicon-arrow-small-down:before { content: '\f0a0'} /* */
|
||||
.octicon-arrow-small-left:before { content: '\f0a1'} /* */
|
||||
.octicon-arrow-small-right:before { content: '\f071'} /* */
|
||||
.octicon-arrow-small-up:before { content: '\f09f'} /* */
|
||||
.octicon-arrow-up:before { content: '\f03d'} /* */
|
||||
.octicon-microscope:before,
|
||||
.octicon-beaker:before { content: '\f0dd'} /* */
|
||||
.octicon-bell:before { content: '\f0de'} /* */
|
||||
.octicon-book:before { content: '\f007'} /* */
|
||||
.octicon-bookmark:before { content: '\f07b'} /* */
|
||||
.octicon-briefcase:before { content: '\f0d3'} /* */
|
||||
.octicon-broadcast:before { content: '\f048'} /* */
|
||||
.octicon-browser:before { content: '\f0c5'} /* */
|
||||
.octicon-bug:before { content: '\f091'} /* */
|
||||
.octicon-calendar:before { content: '\f068'} /* */
|
||||
.octicon-check:before { content: '\f03a'} /* */
|
||||
.octicon-checklist:before { content: '\f076'} /* */
|
||||
.octicon-chevron-down:before { content: '\f0a3'} /* */
|
||||
.octicon-chevron-left:before { content: '\f0a4'} /* */
|
||||
.octicon-chevron-right:before { content: '\f078'} /* */
|
||||
.octicon-chevron-up:before { content: '\f0a2'} /* */
|
||||
.octicon-circle-slash:before { content: '\f084'} /* */
|
||||
.octicon-circuit-board:before { content: '\f0d6'} /* */
|
||||
.octicon-clippy:before { content: '\f035'} /* */
|
||||
.octicon-clock:before { content: '\f046'} /* */
|
||||
.octicon-cloud-download:before { content: '\f00b'} /* */
|
||||
.octicon-cloud-upload:before { content: '\f00c'} /* */
|
||||
.octicon-code:before { content: '\f05f'} /* */
|
||||
.octicon-color-mode:before { content: '\f065'} /* */
|
||||
.octicon-comment-add:before,
|
||||
.octicon-comment:before { content: '\f02b'} /* */
|
||||
.octicon-comment-discussion:before { content: '\f04f'} /* */
|
||||
.octicon-credit-card:before { content: '\f045'} /* */
|
||||
.octicon-dash:before { content: '\f0ca'} /* */
|
||||
.octicon-dashboard:before { content: '\f07d'} /* */
|
||||
.octicon-database:before { content: '\f096'} /* */
|
||||
.octicon-clone:before,
|
||||
.octicon-desktop-download:before { content: '\f0dc'} /* */
|
||||
.octicon-device-camera:before { content: '\f056'} /* */
|
||||
.octicon-device-camera-video:before { content: '\f057'} /* */
|
||||
.octicon-device-desktop:before { content: '\f27c'} /* */
|
||||
.octicon-device-mobile:before { content: '\f038'} /* */
|
||||
.octicon-diff:before { content: '\f04d'} /* */
|
||||
.octicon-diff-added:before { content: '\f06b'} /* */
|
||||
.octicon-diff-ignored:before { content: '\f099'} /* */
|
||||
.octicon-diff-modified:before { content: '\f06d'} /* */
|
||||
.octicon-diff-removed:before { content: '\f06c'} /* */
|
||||
.octicon-diff-renamed:before { content: '\f06e'} /* */
|
||||
.octicon-ellipsis:before { content: '\f09a'} /* */
|
||||
.octicon-eye-unwatch:before,
|
||||
.octicon-eye-watch:before,
|
||||
.octicon-eye:before { content: '\f04e'} /* */
|
||||
.octicon-file-binary:before { content: '\f094'} /* */
|
||||
.octicon-file-code:before { content: '\f010'} /* */
|
||||
.octicon-file-directory:before { content: '\f016'} /* */
|
||||
.octicon-file-media:before { content: '\f012'} /* */
|
||||
.octicon-file-pdf:before { content: '\f014'} /* */
|
||||
.octicon-file-submodule:before { content: '\f017'} /* */
|
||||
.octicon-file-symlink-directory:before { content: '\f0b1'} /* */
|
||||
.octicon-file-symlink-file:before { content: '\f0b0'} /* */
|
||||
.octicon-file-text:before { content: '\f011'} /* */
|
||||
.octicon-file-zip:before { content: '\f013'} /* */
|
||||
.octicon-flame:before { content: '\f0d2'} /* */
|
||||
.octicon-fold:before { content: '\f0cc'} /* */
|
||||
.octicon-gear:before { content: '\f02f'} /* */
|
||||
.octicon-gift:before { content: '\f042'} /* */
|
||||
.octicon-gist:before { content: '\f00e'} /* */
|
||||
.octicon-gist-secret:before { content: '\f08c'} /* */
|
||||
.octicon-git-branch-create:before,
|
||||
.octicon-git-branch-delete:before,
|
||||
.octicon-git-branch:before { content: '\f020'} /* */
|
||||
.octicon-git-commit:before { content: '\f01f'} /* */
|
||||
.octicon-git-compare:before { content: '\f0ac'} /* */
|
||||
.octicon-git-merge:before { content: '\f023'} /* */
|
||||
.octicon-git-pull-request-abandoned:before,
|
||||
.octicon-git-pull-request:before { content: '\f009'} /* */
|
||||
.octicon-globe:before { content: '\f0b6'} /* */
|
||||
.octicon-graph:before { content: '\f043'} /* */
|
||||
.octicon-heart:before { content: '\2665'} /* ♥ */
|
||||
.octicon-history:before { content: '\f07e'} /* */
|
||||
.octicon-home:before { content: '\f08d'} /* */
|
||||
.octicon-horizontal-rule:before { content: '\f070'} /* */
|
||||
.octicon-hubot:before { content: '\f09d'} /* */
|
||||
.octicon-inbox:before { content: '\f0cf'} /* */
|
||||
.octicon-info:before { content: '\f059'} /* */
|
||||
.octicon-issue-closed:before { content: '\f028'} /* */
|
||||
.octicon-issue-opened:before { content: '\f026'} /* */
|
||||
.octicon-issue-reopened:before { content: '\f027'} /* */
|
||||
.octicon-jersey:before { content: '\f019'} /* */
|
||||
.octicon-key:before { content: '\f049'} /* */
|
||||
.octicon-keyboard:before { content: '\f00d'} /* */
|
||||
.octicon-law:before { content: '\f0d8'} /* */
|
||||
.octicon-light-bulb:before { content: '\f000'} /* */
|
||||
.octicon-link:before { content: '\f05c'} /* */
|
||||
.octicon-link-external:before { content: '\f07f'} /* */
|
||||
.octicon-list-ordered:before { content: '\f062'} /* */
|
||||
.octicon-list-unordered:before { content: '\f061'} /* */
|
||||
.octicon-location:before { content: '\f060'} /* */
|
||||
.octicon-gist-private:before,
|
||||
.octicon-mirror-private:before,
|
||||
.octicon-git-fork-private:before,
|
||||
.octicon-lock:before { content: '\f06a'} /* */
|
||||
.octicon-logo-github:before { content: '\f092'} /* */
|
||||
.octicon-mail:before { content: '\f03b'} /* */
|
||||
.octicon-mail-read:before { content: '\f03c'} /* */
|
||||
.octicon-mail-reply:before { content: '\f051'} /* */
|
||||
.octicon-mark-github:before { content: '\f00a'} /* */
|
||||
.octicon-markdown:before { content: '\f0c9'} /* */
|
||||
.octicon-megaphone:before { content: '\f077'} /* */
|
||||
.octicon-mention:before { content: '\f0be'} /* */
|
||||
.octicon-milestone:before { content: '\f075'} /* */
|
||||
.octicon-mirror-public:before,
|
||||
.octicon-mirror:before { content: '\f024'} /* */
|
||||
.octicon-mortar-board:before { content: '\f0d7'} /* */
|
||||
.octicon-mute:before { content: '\f080'} /* */
|
||||
.octicon-no-newline:before { content: '\f09c'} /* */
|
||||
.octicon-octoface:before { content: '\f008'} /* */
|
||||
.octicon-organization:before { content: '\f037'} /* */
|
||||
.octicon-package:before { content: '\f0c4'} /* */
|
||||
.octicon-paintcan:before { content: '\f0d1'} /* */
|
||||
.octicon-pencil:before { content: '\f058'} /* */
|
||||
.octicon-person-add:before,
|
||||
.octicon-person-follow:before,
|
||||
.octicon-person:before { content: '\f018'} /* */
|
||||
.octicon-pin:before { content: '\f041'} /* */
|
||||
.octicon-plug:before { content: '\f0d4'} /* */
|
||||
.octicon-repo-create:before,
|
||||
.octicon-gist-new:before,
|
||||
.octicon-file-directory-create:before,
|
||||
.octicon-file-add:before,
|
||||
.octicon-plus:before { content: '\f05d'} /* */
|
||||
.octicon-primitive-dot:before { content: '\f052'} /* */
|
||||
.octicon-primitive-square:before { content: '\f053'} /* */
|
||||
.octicon-pulse:before { content: '\f085'} /* */
|
||||
.octicon-question:before { content: '\f02c'} /* */
|
||||
.octicon-quote:before { content: '\f063'} /* */
|
||||
.octicon-radio-tower:before { content: '\f030'} /* */
|
||||
.octicon-repo-delete:before,
|
||||
.octicon-repo:before { content: '\f001'} /* */
|
||||
.octicon-repo-clone:before { content: '\f04c'} /* */
|
||||
.octicon-repo-force-push:before { content: '\f04a'} /* */
|
||||
.octicon-gist-fork:before,
|
||||
.octicon-repo-forked:before { content: '\f002'} /* */
|
||||
.octicon-repo-pull:before { content: '\f006'} /* */
|
||||
.octicon-repo-push:before { content: '\f005'} /* */
|
||||
.octicon-rocket:before { content: '\f033'} /* */
|
||||
.octicon-rss:before { content: '\f034'} /* */
|
||||
.octicon-ruby:before { content: '\f047'} /* */
|
||||
.octicon-screen-full:before { content: '\f066'} /* */
|
||||
.octicon-screen-normal:before { content: '\f067'} /* */
|
||||
.octicon-search-save:before,
|
||||
.octicon-search:before { content: '\f02e'} /* */
|
||||
.octicon-server:before { content: '\f097'} /* */
|
||||
.octicon-settings:before { content: '\f07c'} /* */
|
||||
.octicon-shield:before { content: '\f0e1'} /* */
|
||||
.octicon-log-in:before,
|
||||
.octicon-sign-in:before { content: '\f036'} /* */
|
||||
.octicon-log-out:before,
|
||||
.octicon-sign-out:before { content: '\f032'} /* */
|
||||
.octicon-squirrel:before { content: '\f0b2'} /* */
|
||||
.octicon-star-add:before,
|
||||
.octicon-star-delete:before,
|
||||
.octicon-star:before { content: '\f02a'} /* */
|
||||
.octicon-stop:before { content: '\f08f'} /* */
|
||||
.octicon-repo-sync:before,
|
||||
.octicon-sync:before { content: '\f087'} /* */
|
||||
.octicon-tag-remove:before,
|
||||
.octicon-tag-add:before,
|
||||
.octicon-tag:before { content: '\f015'} /* */
|
||||
.octicon-telescope:before { content: '\f088'} /* */
|
||||
.octicon-terminal:before { content: '\f0c8'} /* */
|
||||
.octicon-three-bars:before { content: '\f05e'} /* */
|
||||
.octicon-thumbsdown:before { content: '\f0db'} /* */
|
||||
.octicon-thumbsup:before { content: '\f0da'} /* */
|
||||
.octicon-tools:before { content: '\f031'} /* */
|
||||
.octicon-trashcan:before { content: '\f0d0'} /* */
|
||||
.octicon-triangle-down:before { content: '\f05b'} /* */
|
||||
.octicon-triangle-left:before { content: '\f044'} /* */
|
||||
.octicon-triangle-right:before { content: '\f05a'} /* */
|
||||
.octicon-triangle-up:before { content: '\f0aa'} /* */
|
||||
.octicon-unfold:before { content: '\f039'} /* */
|
||||
.octicon-unmute:before { content: '\f0ba'} /* */
|
||||
.octicon-versions:before { content: '\f064'} /* */
|
||||
.octicon-watch:before { content: '\f0e0'} /* */
|
||||
.octicon-remove-close:before,
|
||||
.octicon-x:before { content: '\f081'} /* */
|
||||
.octicon-zap:before { content: '\26A1'} /* ⚡ */
|
||||
59
src/vs/base/browser/ui/progressbar/progressbar.css
Normal file
@@ -0,0 +1,59 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
.progress-container {
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
.progress-container .progress-bit {
|
||||
width: 2%;
|
||||
height: 5px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.progress-container.active .progress-bit {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
.progress-container.discrete .progress-bit {
|
||||
left: 0;
|
||||
transition: width 100ms linear;
|
||||
-webkit-transition: width 100ms linear;
|
||||
-o-transition: width 100ms linear;
|
||||
-moz-transition: width 100ms linear;
|
||||
-ms-transition: width 100ms linear;
|
||||
}
|
||||
|
||||
.progress-container.discrete.done .progress-bit {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.progress-container.infinite .progress-bit {
|
||||
animation-name: progress;
|
||||
animation-duration: 4s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
-ms-animation-name: progress;
|
||||
-ms-animation-duration: 4s;
|
||||
-ms-animation-iteration-count: infinite;
|
||||
-ms-animation-timing-function: linear;
|
||||
-webkit-animation-name: progress;
|
||||
-webkit-animation-duration: 4s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-timing-function: linear;
|
||||
-moz-animation-name: progress;
|
||||
-moz-animation-duration: 4s;
|
||||
-moz-animation-iteration-count: infinite;
|
||||
-moz-animation-timing-function: linear;
|
||||
}
|
||||
|
||||
@keyframes progress { from { left: 0; width: 2%; } 50% { left: 50%; width: 5%; } to { left: 98%; width: 2%; } }
|
||||
@-ms-keyframes progress { from { left: 0; width: 2%; } 50% { left: 50%; width: 5%; } to { left: 98%; width: 2%; } }
|
||||
@-webkit-keyframes progress { from { left: 0; width: 2%; } 50% { left: 50%; width: 5%; } to { left: 98%; width: 2%; } }
|
||||
@-moz-keyframes progress { from { left: 0; width: 2%; } 50% { left: 50%; width: 5%; } to { left: 98%; width: 2%; } }
|
||||
|
||||
|
||||
230
src/vs/base/browser/ui/progressbar/progressbar.ts
Normal file
@@ -0,0 +1,230 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./progressbar';
|
||||
import { TPromise, ValueCallback } from 'vs/base/common/winjs.base';
|
||||
import assert = require('vs/base/common/assert');
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
const css_done = 'done';
|
||||
const css_active = 'active';
|
||||
const css_infinite = 'infinite';
|
||||
const css_discrete = 'discrete';
|
||||
const css_progress_container = 'progress-container';
|
||||
const css_progress_bit = 'progress-bit';
|
||||
|
||||
export interface IProgressBarOptions extends IProgressBarStyles {
|
||||
}
|
||||
|
||||
export interface IProgressBarStyles {
|
||||
progressBarBackground?: Color;
|
||||
}
|
||||
|
||||
const defaultOpts = {
|
||||
progressBarBackground: Color.fromHex('#0E70C0')
|
||||
};
|
||||
|
||||
/**
|
||||
* A progress bar with support for infinite or discrete progress.
|
||||
*/
|
||||
export class ProgressBar {
|
||||
private options: IProgressBarOptions;
|
||||
private toUnbind: IDisposable[];
|
||||
private workedVal: number;
|
||||
private element: Builder;
|
||||
private animationRunning: boolean;
|
||||
private bit: HTMLElement;
|
||||
private totalWork: number;
|
||||
private animationStopToken: ValueCallback;
|
||||
private progressBarBackground: Color;
|
||||
|
||||
constructor(builder: Builder, options?: IProgressBarOptions) {
|
||||
this.options = options || Object.create(null);
|
||||
mixin(this.options, defaultOpts, false);
|
||||
|
||||
this.toUnbind = [];
|
||||
this.workedVal = 0;
|
||||
|
||||
this.progressBarBackground = this.options.progressBarBackground;
|
||||
|
||||
this.create(builder);
|
||||
}
|
||||
|
||||
private create(parent: Builder): void {
|
||||
parent.div({ 'class': css_progress_container }, (builder) => {
|
||||
this.element = builder.clone();
|
||||
|
||||
builder.div({ 'class': css_progress_bit }).on([DOM.EventType.ANIMATION_START, DOM.EventType.ANIMATION_END, DOM.EventType.ANIMATION_ITERATION], (e: Event) => {
|
||||
switch (e.type) {
|
||||
case DOM.EventType.ANIMATION_START:
|
||||
case DOM.EventType.ANIMATION_END:
|
||||
this.animationRunning = e.type === DOM.EventType.ANIMATION_START;
|
||||
break;
|
||||
|
||||
case DOM.EventType.ANIMATION_ITERATION:
|
||||
if (this.animationStopToken) {
|
||||
this.animationStopToken(null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}, this.toUnbind);
|
||||
|
||||
this.bit = builder.getHTMLElement();
|
||||
});
|
||||
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
private off(): void {
|
||||
this.bit.style.width = 'inherit';
|
||||
this.bit.style.opacity = '1';
|
||||
this.element.removeClass(css_active);
|
||||
this.element.removeClass(css_infinite);
|
||||
this.element.removeClass(css_discrete);
|
||||
|
||||
this.workedVal = 0;
|
||||
this.totalWork = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates to the progress bar that all work is done.
|
||||
*/
|
||||
public done(): ProgressBar {
|
||||
return this.doDone(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the progressbar from showing any progress instantly without fading out.
|
||||
*/
|
||||
public stop(): ProgressBar {
|
||||
return this.doDone(false);
|
||||
}
|
||||
|
||||
private doDone(delayed: boolean): ProgressBar {
|
||||
this.element.addClass(css_done);
|
||||
|
||||
// let it grow to 100% width and hide afterwards
|
||||
if (!this.element.hasClass(css_infinite)) {
|
||||
this.bit.style.width = 'inherit';
|
||||
|
||||
if (delayed) {
|
||||
TPromise.timeout(200).then(() => this.off());
|
||||
} else {
|
||||
this.off();
|
||||
}
|
||||
}
|
||||
|
||||
// let it fade out and hide afterwards
|
||||
else {
|
||||
this.bit.style.opacity = '0';
|
||||
if (delayed) {
|
||||
TPromise.timeout(200).then(() => this.off());
|
||||
} else {
|
||||
this.off();
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this mode to indicate progress that has no total number of work units.
|
||||
*/
|
||||
public infinite(): ProgressBar {
|
||||
this.bit.style.width = '2%';
|
||||
this.bit.style.opacity = '1';
|
||||
|
||||
this.element.removeClass(css_discrete);
|
||||
this.element.removeClass(css_done);
|
||||
this.element.addClass(css_active);
|
||||
this.element.addClass(css_infinite);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the progress bar the total number of work. Use in combination with workedVal() to let
|
||||
* the progress bar show the actual progress based on the work that is done.
|
||||
*/
|
||||
public total(value: number): ProgressBar {
|
||||
this.workedVal = 0;
|
||||
this.totalWork = value;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds out if this progress bar is configured with total work
|
||||
*/
|
||||
public hasTotal(): boolean {
|
||||
return !isNaN(this.totalWork);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the progress bar that an amount of work has been completed.
|
||||
*/
|
||||
public worked(value: number): ProgressBar {
|
||||
assert.ok(!isNaN(this.totalWork), 'Total work not set');
|
||||
|
||||
value = Number(value);
|
||||
assert.ok(!isNaN(value), 'Value is not a number');
|
||||
value = Math.max(1, value);
|
||||
|
||||
this.workedVal += value;
|
||||
this.workedVal = Math.min(this.totalWork, this.workedVal);
|
||||
|
||||
if (this.element.hasClass(css_infinite)) {
|
||||
this.element.removeClass(css_infinite);
|
||||
}
|
||||
|
||||
if (this.element.hasClass(css_done)) {
|
||||
this.element.removeClass(css_done);
|
||||
}
|
||||
|
||||
if (!this.element.hasClass(css_active)) {
|
||||
this.element.addClass(css_active);
|
||||
}
|
||||
|
||||
if (!this.element.hasClass(css_discrete)) {
|
||||
this.element.addClass(css_discrete);
|
||||
}
|
||||
|
||||
this.bit.style.width = 100 * (this.workedVal / this.totalWork) + '%';
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the builder this progress bar is building in.
|
||||
*/
|
||||
public getContainer(): Builder {
|
||||
return $(this.element);
|
||||
}
|
||||
|
||||
public style(styles: IProgressBarStyles): void {
|
||||
this.progressBarBackground = styles.progressBarBackground;
|
||||
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
protected applyStyles(): void {
|
||||
if (this.bit) {
|
||||
const background = this.progressBarBackground ? this.progressBarBackground.toString() : null;
|
||||
|
||||
this.bit.style.backgroundColor = background;
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toUnbind = dispose(this.toUnbind);
|
||||
}
|
||||
}
|
||||
211
src/vs/base/browser/ui/resourceviewer/resourceViewer.ts
Normal file
@@ -0,0 +1,211 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./resourceviewer';
|
||||
import nls = require('vs/nls');
|
||||
import mimes = require('vs/base/common/mime');
|
||||
import URI from 'vs/base/common/uri';
|
||||
import paths = require('vs/base/common/paths');
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { BoundedMap } from 'vs/base/common/map';
|
||||
|
||||
|
||||
interface MapExtToMediaMimes {
|
||||
[index: string]: string;
|
||||
}
|
||||
|
||||
// Known media mimes that we can handle
|
||||
const mapExtToMediaMimes: MapExtToMediaMimes = {
|
||||
'.bmp': 'image/bmp',
|
||||
'.gif': 'image/gif',
|
||||
'.jpg': 'image/jpg',
|
||||
'.jpeg': 'image/jpg',
|
||||
'.jpe': 'image/jpg',
|
||||
'.png': 'image/png',
|
||||
'.tiff': 'image/tiff',
|
||||
'.tif': 'image/tiff',
|
||||
'.ico': 'image/x-icon',
|
||||
'.tga': 'image/x-tga',
|
||||
'.psd': 'image/vnd.adobe.photoshop',
|
||||
'.mid': 'audio/midi',
|
||||
'.midi': 'audio/midi',
|
||||
'.mp4a': 'audio/mp4',
|
||||
'.mpga': 'audio/mpeg',
|
||||
'.mp2': 'audio/mpeg',
|
||||
'.mp2a': 'audio/mpeg',
|
||||
'.mp3': 'audio/mpeg',
|
||||
'.m2a': 'audio/mpeg',
|
||||
'.m3a': 'audio/mpeg',
|
||||
'.oga': 'audio/ogg',
|
||||
'.ogg': 'audio/ogg',
|
||||
'.spx': 'audio/ogg',
|
||||
'.aac': 'audio/x-aac',
|
||||
'.wav': 'audio/x-wav',
|
||||
'.wma': 'audio/x-ms-wma',
|
||||
'.mp4': 'video/mp4',
|
||||
'.mp4v': 'video/mp4',
|
||||
'.mpg4': 'video/mp4',
|
||||
'.mpeg': 'video/mpeg',
|
||||
'.mpg': 'video/mpeg',
|
||||
'.mpe': 'video/mpeg',
|
||||
'.m1v': 'video/mpeg',
|
||||
'.m2v': 'video/mpeg',
|
||||
'.ogv': 'video/ogg',
|
||||
'.qt': 'video/quicktime',
|
||||
'.mov': 'video/quicktime',
|
||||
'.webm': 'video/webm',
|
||||
'.mkv': 'video/x-matroska',
|
||||
'.mk3d': 'video/x-matroska',
|
||||
'.mks': 'video/x-matroska',
|
||||
'.wmv': 'video/x-ms-wmv',
|
||||
'.flv': 'video/x-flv',
|
||||
'.avi': 'video/x-msvideo',
|
||||
'.movie': 'video/x-sgi-movie'
|
||||
};
|
||||
|
||||
export interface IResourceDescriptor {
|
||||
resource: URI;
|
||||
name: string;
|
||||
size: number;
|
||||
etag: string;
|
||||
}
|
||||
|
||||
// Chrome is caching images very aggressively and so we use the ETag information to find out if
|
||||
// we need to bypass the cache or not. We could always bypass the cache everytime we show the image
|
||||
// however that has very bad impact on memory consumption because each time the image gets shown,
|
||||
// memory grows (see also https://github.com/electron/electron/issues/6275)
|
||||
const IMAGE_RESOURCE_ETAG_CACHE = new BoundedMap<{ etag: string, src: string }>(100);
|
||||
function imageSrc(descriptor: IResourceDescriptor): string {
|
||||
const src = descriptor.resource.toString();
|
||||
|
||||
let cached = IMAGE_RESOURCE_ETAG_CACHE.get(src);
|
||||
if (!cached) {
|
||||
cached = { etag: descriptor.etag, src };
|
||||
IMAGE_RESOURCE_ETAG_CACHE.set(src, cached);
|
||||
}
|
||||
|
||||
if (cached.etag !== descriptor.etag) {
|
||||
cached.etag = descriptor.etag;
|
||||
cached.src = `${src}?${Date.now()}`; // bypass cache with this trick
|
||||
}
|
||||
|
||||
return cached.src;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to actually render the given resource into the provided container. Will adjust scrollbar (if provided) automatically based on loading
|
||||
* progress of the binary resource.
|
||||
*/
|
||||
export class ResourceViewer {
|
||||
|
||||
private static KB = 1024;
|
||||
private static MB = ResourceViewer.KB * ResourceViewer.KB;
|
||||
private static GB = ResourceViewer.MB * ResourceViewer.KB;
|
||||
private static TB = ResourceViewer.GB * ResourceViewer.KB;
|
||||
|
||||
private static MAX_IMAGE_SIZE = ResourceViewer.MB; // showing images inline is memory intense, so we have a limit
|
||||
|
||||
public static show(
|
||||
descriptor: IResourceDescriptor,
|
||||
container: Builder,
|
||||
scrollbar: DomScrollableElement,
|
||||
openExternal: (URI) => void,
|
||||
metadataClb?: (meta: string) => void
|
||||
): void {
|
||||
// Ensure CSS class
|
||||
$(container).setClass('monaco-resource-viewer');
|
||||
|
||||
// Lookup media mime if any
|
||||
let mime: string;
|
||||
const ext = paths.extname(descriptor.resource.toString());
|
||||
if (ext) {
|
||||
mime = mapExtToMediaMimes[ext.toLowerCase()];
|
||||
}
|
||||
|
||||
if (!mime) {
|
||||
mime = mimes.MIME_BINARY;
|
||||
}
|
||||
|
||||
// Show Image inline
|
||||
if (mime.indexOf('image/') >= 0) {
|
||||
if (descriptor.size <= ResourceViewer.MAX_IMAGE_SIZE) {
|
||||
$(container)
|
||||
.empty()
|
||||
.addClass('image')
|
||||
.img({ src: imageSrc(descriptor) })
|
||||
.on(DOM.EventType.LOAD, (e, img) => {
|
||||
const imgElement = <HTMLImageElement>img.getHTMLElement();
|
||||
if (imgElement.naturalWidth > imgElement.width || imgElement.naturalHeight > imgElement.height) {
|
||||
$(container).addClass('oversized');
|
||||
|
||||
img.on(DOM.EventType.CLICK, (e, img) => {
|
||||
$(container).toggleClass('full-size');
|
||||
|
||||
scrollbar.scanDomNode();
|
||||
});
|
||||
}
|
||||
|
||||
if (metadataClb) {
|
||||
metadataClb(nls.localize('imgMeta', "{0}x{1} {2}", imgElement.naturalWidth, imgElement.naturalHeight, ResourceViewer.formatSize(descriptor.size)));
|
||||
}
|
||||
|
||||
scrollbar.scanDomNode();
|
||||
});
|
||||
} else {
|
||||
$(container)
|
||||
.empty()
|
||||
.p({
|
||||
text: nls.localize('largeImageError', "The image is too large to display in the editor. ")
|
||||
})
|
||||
.append($('a', {
|
||||
role: 'button',
|
||||
class: 'open-external',
|
||||
text: nls.localize('resourceOpenExternalButton', "Open image using external program?")
|
||||
}).on(DOM.EventType.CLICK, (e) => {
|
||||
openExternal(descriptor.resource);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Handle generic Binary Files
|
||||
else {
|
||||
$(container)
|
||||
.empty()
|
||||
.span({
|
||||
text: nls.localize('nativeBinaryError', "The file will not be displayed in the editor because it is either binary, very large or uses an unsupported text encoding.")
|
||||
});
|
||||
|
||||
if (metadataClb) {
|
||||
metadataClb(ResourceViewer.formatSize(descriptor.size));
|
||||
}
|
||||
|
||||
scrollbar.scanDomNode();
|
||||
}
|
||||
}
|
||||
|
||||
private static formatSize(size: number): string {
|
||||
if (size < ResourceViewer.KB) {
|
||||
return nls.localize('sizeB', "{0}B", size);
|
||||
}
|
||||
|
||||
if (size < ResourceViewer.MB) {
|
||||
return nls.localize('sizeKB', "{0}KB", (size / ResourceViewer.KB).toFixed(2));
|
||||
}
|
||||
|
||||
if (size < ResourceViewer.GB) {
|
||||
return nls.localize('sizeMB', "{0}MB", (size / ResourceViewer.MB).toFixed(2));
|
||||
}
|
||||
|
||||
if (size < ResourceViewer.TB) {
|
||||
return nls.localize('sizeGB', "{0}GB", (size / ResourceViewer.GB).toFixed(2));
|
||||
}
|
||||
|
||||
return nls.localize('sizeTB', "{0}TB", (size / ResourceViewer.TB).toFixed(2));
|
||||
}
|
||||
}
|
||||
56
src/vs/base/browser/ui/resourceviewer/resourceviewer.css
Normal file
@@ -0,0 +1,56 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-resource-viewer:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer {
|
||||
padding: 5px 0 0 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer.image {
|
||||
padding: 10px 10px 0 10px;
|
||||
background-position: 0 0, 8px 8px;
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer.image.full-size {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.vs .monaco-resource-viewer.image {
|
||||
background-image:
|
||||
linear-gradient(45deg, rgb(230, 230, 230) 25%, transparent 25%, transparent 75%, rgb(230, 230, 230) 75%, rgb(230, 230, 230)),
|
||||
linear-gradient(45deg, rgb(230, 230, 230) 25%, transparent 25%, transparent 75%, rgb(230, 230, 230) 75%, rgb(230, 230, 230));
|
||||
}
|
||||
|
||||
.vs-dark .monaco-resource-viewer.image {
|
||||
background-image:
|
||||
linear-gradient(45deg, rgb(20, 20, 20) 25%, transparent 25%, transparent 75%, rgb(20, 20, 20) 75%, rgb(20, 20, 20)),
|
||||
linear-gradient(45deg, rgb(20, 20, 20) 25%, transparent 25%, transparent 75%, rgb(20, 20, 20) 75%, rgb(20, 20, 20));
|
||||
}
|
||||
|
||||
.monaco-resource-viewer img {
|
||||
max-width: 100%;
|
||||
max-height: calc(100% - 10px); /* somehow this prevents scrollbars from showing up */
|
||||
}
|
||||
|
||||
.monaco-resource-viewer.oversized img {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer.full-size img {
|
||||
max-width: initial;
|
||||
max-height: initial;
|
||||
cursor: zoom-out;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer .open-external,
|
||||
.monaco-resource-viewer .open-external:hover {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
46
src/vs/base/browser/ui/sash/sash.css
Normal file
@@ -0,0 +1,46 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-sash {
|
||||
position: absolute;
|
||||
z-index: 90;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.monaco-sash.vertical {
|
||||
cursor: ew-resize;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.monaco-sash.horizontal {
|
||||
cursor: ns-resize;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.monaco-sash.disabled {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
.vertical-cursor-container {
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
.horizontal-cursor-container {
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
/** Custom Mac Cursor */
|
||||
|
||||
.monaco-sash.mac.vertical,
|
||||
.vertical-cursor-container-mac {
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
.monaco-sash.mac.horizontal,
|
||||
.horizontal-cursor-container-mac {
|
||||
cursor: row-resize;
|
||||
}
|
||||
368
src/vs/base/browser/ui/sash/sash.ts
Normal file
@@ -0,0 +1,368 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./sash';
|
||||
import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Builder, $, Dimension } from 'vs/base/browser/builder';
|
||||
import { isIPad } from 'vs/base/browser/browser';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import types = require('vs/base/common/types');
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { Gesture, EventType, GestureEvent } from 'vs/base/browser/touch';
|
||||
import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
|
||||
export interface ISashLayoutProvider { }
|
||||
|
||||
export interface IVerticalSashLayoutProvider extends ISashLayoutProvider {
|
||||
getVerticalSashLeft(sash: Sash): number;
|
||||
getVerticalSashTop?(sash: Sash): number;
|
||||
getVerticalSashHeight?(sash: Sash): number;
|
||||
}
|
||||
|
||||
export interface IHorizontalSashLayoutProvider extends ISashLayoutProvider {
|
||||
getHorizontalSashTop(sash: Sash): number;
|
||||
getHorizontalSashLeft?(sash: Sash): number;
|
||||
getHorizontalSashWidth?(sash: Sash): number;
|
||||
}
|
||||
|
||||
export interface ISashEvent {
|
||||
startX: number;
|
||||
currentX: number;
|
||||
startY: number;
|
||||
currentY: number;
|
||||
}
|
||||
|
||||
export interface ISashOptions {
|
||||
baseSize?: number;
|
||||
orientation?: Orientation;
|
||||
}
|
||||
|
||||
export enum Orientation {
|
||||
VERTICAL,
|
||||
HORIZONTAL
|
||||
}
|
||||
|
||||
export class Sash extends EventEmitter {
|
||||
|
||||
private $e: Builder;
|
||||
private gesture: Gesture;
|
||||
private layoutProvider: ISashLayoutProvider;
|
||||
private isDisabled: boolean;
|
||||
private hidden: boolean;
|
||||
private orientation: Orientation;
|
||||
private size: number;
|
||||
|
||||
constructor(container: HTMLElement, layoutProvider: ISashLayoutProvider, options: ISashOptions = {}) {
|
||||
super();
|
||||
|
||||
this.$e = $('.monaco-sash').appendTo(container);
|
||||
|
||||
if (isMacintosh) {
|
||||
this.$e.addClass('mac');
|
||||
}
|
||||
|
||||
this.gesture = new Gesture(this.$e.getHTMLElement());
|
||||
|
||||
this.$e.on(DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => { this.onMouseDown(e); });
|
||||
this.$e.on(DOM.EventType.DBLCLICK, (e: MouseEvent) => { this.emit('reset', e); });
|
||||
this.$e.on(EventType.Start, (e: GestureEvent) => { this.onTouchStart(e); });
|
||||
|
||||
this.size = options.baseSize || 5;
|
||||
|
||||
if (isIPad) {
|
||||
this.size *= 4; // see also http://ux.stackexchange.com/questions/39023/what-is-the-optimum-button-size-of-touch-screen-applications
|
||||
this.$e.addClass('touch');
|
||||
}
|
||||
|
||||
this.setOrientation(options.orientation || Orientation.VERTICAL);
|
||||
|
||||
this.isDisabled = false;
|
||||
this.hidden = false;
|
||||
this.layoutProvider = layoutProvider;
|
||||
}
|
||||
|
||||
public getHTMLElement(): HTMLElement {
|
||||
return this.$e.getHTMLElement();
|
||||
}
|
||||
|
||||
public setOrientation(orientation: Orientation): void {
|
||||
this.orientation = orientation;
|
||||
|
||||
this.$e.removeClass('horizontal', 'vertical');
|
||||
this.$e.addClass(this.getOrientation());
|
||||
|
||||
if (this.orientation === Orientation.HORIZONTAL) {
|
||||
this.$e.size(null, this.size);
|
||||
} else {
|
||||
this.$e.size(this.size);
|
||||
}
|
||||
|
||||
if (this.layoutProvider) {
|
||||
this.layout();
|
||||
}
|
||||
}
|
||||
|
||||
private getOrientation(): 'horizontal' | 'vertical' {
|
||||
return this.orientation === Orientation.HORIZONTAL ? 'horizontal' : 'vertical';
|
||||
}
|
||||
|
||||
private onMouseDown(e: MouseEvent): void {
|
||||
DOM.EventHelper.stop(e, false);
|
||||
|
||||
if (this.isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const iframes = $(DOM.getElementsByTagName('iframe'));
|
||||
if (iframes) {
|
||||
iframes.style('pointer-events', 'none'); // disable mouse events on iframes as long as we drag the sash
|
||||
}
|
||||
|
||||
let mouseDownEvent = new StandardMouseEvent(e);
|
||||
let startX = mouseDownEvent.posx;
|
||||
let startY = mouseDownEvent.posy;
|
||||
|
||||
let startEvent: ISashEvent = {
|
||||
startX: startX,
|
||||
currentX: startX,
|
||||
startY: startY,
|
||||
currentY: startY
|
||||
};
|
||||
|
||||
this.$e.addClass('active');
|
||||
this.emit('start', startEvent);
|
||||
|
||||
let $window = $(window);
|
||||
let containerCSSClass = `${this.getOrientation()}-cursor-container${isMacintosh ? '-mac' : ''}`;
|
||||
|
||||
let lastCurrentX = startX;
|
||||
let lastCurrentY = startY;
|
||||
|
||||
$window.on('mousemove', (e: MouseEvent) => {
|
||||
DOM.EventHelper.stop(e, false);
|
||||
let mouseMoveEvent = new StandardMouseEvent(e);
|
||||
|
||||
let event: ISashEvent = {
|
||||
startX: startX,
|
||||
currentX: mouseMoveEvent.posx,
|
||||
startY: startY,
|
||||
currentY: mouseMoveEvent.posy
|
||||
};
|
||||
|
||||
lastCurrentX = mouseMoveEvent.posx;
|
||||
lastCurrentY = mouseMoveEvent.posy;
|
||||
|
||||
this.emit('change', event);
|
||||
}).once('mouseup', (e: MouseEvent) => {
|
||||
DOM.EventHelper.stop(e, false);
|
||||
this.$e.removeClass('active');
|
||||
this.emit('end');
|
||||
|
||||
$window.off('mousemove');
|
||||
document.body.classList.remove(containerCSSClass);
|
||||
|
||||
const iframes = $(DOM.getElementsByTagName('iframe'));
|
||||
if (iframes) {
|
||||
iframes.style('pointer-events', 'auto');
|
||||
}
|
||||
});
|
||||
|
||||
document.body.classList.add(containerCSSClass);
|
||||
}
|
||||
|
||||
private onTouchStart(event: GestureEvent): void {
|
||||
DOM.EventHelper.stop(event);
|
||||
|
||||
let listeners: IDisposable[] = [];
|
||||
|
||||
let startX = event.pageX;
|
||||
let startY = event.pageY;
|
||||
|
||||
this.emit('start', {
|
||||
startX: startX,
|
||||
currentX: startX,
|
||||
startY: startY,
|
||||
currentY: startY
|
||||
});
|
||||
|
||||
let lastCurrentX = startX;
|
||||
let lastCurrentY = startY;
|
||||
|
||||
listeners.push(DOM.addDisposableListener(this.$e.getHTMLElement(), EventType.Change, (event: GestureEvent) => {
|
||||
if (types.isNumber(event.pageX) && types.isNumber(event.pageY)) {
|
||||
this.emit('change', {
|
||||
startX: startX,
|
||||
currentX: event.pageX,
|
||||
startY: startY,
|
||||
currentY: event.pageY
|
||||
});
|
||||
|
||||
lastCurrentX = event.pageX;
|
||||
lastCurrentY = event.pageY;
|
||||
}
|
||||
}));
|
||||
|
||||
listeners.push(DOM.addDisposableListener(this.$e.getHTMLElement(), EventType.End, (event: GestureEvent) => {
|
||||
this.emit('end');
|
||||
dispose(listeners);
|
||||
}));
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
let style: { top?: string; left?: string; height?: string; width?: string; };
|
||||
|
||||
if (this.orientation === Orientation.VERTICAL) {
|
||||
let verticalProvider = (<IVerticalSashLayoutProvider>this.layoutProvider);
|
||||
style = { left: verticalProvider.getVerticalSashLeft(this) - (this.size / 2) + 'px' };
|
||||
|
||||
if (verticalProvider.getVerticalSashTop) {
|
||||
style.top = verticalProvider.getVerticalSashTop(this) + 'px';
|
||||
}
|
||||
|
||||
if (verticalProvider.getVerticalSashHeight) {
|
||||
style.height = verticalProvider.getVerticalSashHeight(this) + 'px';
|
||||
}
|
||||
} else {
|
||||
let horizontalProvider = (<IHorizontalSashLayoutProvider>this.layoutProvider);
|
||||
style = { top: horizontalProvider.getHorizontalSashTop(this) - (this.size / 2) + 'px' };
|
||||
|
||||
if (horizontalProvider.getHorizontalSashLeft) {
|
||||
style.left = horizontalProvider.getHorizontalSashLeft(this) + 'px';
|
||||
}
|
||||
|
||||
if (horizontalProvider.getHorizontalSashWidth) {
|
||||
style.width = horizontalProvider.getHorizontalSashWidth(this) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
this.$e.style(style);
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
this.hidden = false;
|
||||
this.$e.show();
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
this.hidden = true;
|
||||
this.$e.hide();
|
||||
}
|
||||
|
||||
public isHidden(): boolean {
|
||||
return this.hidden;
|
||||
}
|
||||
|
||||
public enable(): void {
|
||||
this.$e.removeClass('disabled');
|
||||
this.isDisabled = false;
|
||||
}
|
||||
|
||||
public disable(): void {
|
||||
this.$e.addClass('disabled');
|
||||
this.isDisabled = true;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this.$e) {
|
||||
this.$e.destroy();
|
||||
this.$e = null;
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple Vertical Sash that computes the position of the sash when it is moved between the given dimension.
|
||||
* Triggers onPositionChange event when the position is changed
|
||||
*/
|
||||
export class VSash extends Disposable implements IVerticalSashLayoutProvider {
|
||||
|
||||
private sash: Sash;
|
||||
private ratio: number;
|
||||
private startPosition: number;
|
||||
private position: number;
|
||||
private dimension: Dimension;
|
||||
|
||||
private _onPositionChange: Emitter<number> = new Emitter<number>();
|
||||
public get onPositionChange(): Event<number> { return this._onPositionChange.event; }
|
||||
|
||||
constructor(container: HTMLElement, private minWidth: number) {
|
||||
super();
|
||||
this.ratio = 0.5;
|
||||
this.sash = new Sash(container, this);
|
||||
|
||||
this._register(this.sash.addListener('start', () => this.onSashDragStart()));
|
||||
this._register(this.sash.addListener('change', (e: ISashEvent) => this.onSashDrag(e)));
|
||||
this._register(this.sash.addListener('end', () => this.onSashDragEnd()));
|
||||
this._register(this.sash.addListener('reset', () => this.onSashReset()));
|
||||
}
|
||||
|
||||
public getVerticalSashTop(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public getVerticalSashLeft(): number {
|
||||
return this.position;
|
||||
}
|
||||
|
||||
public getVerticalSashHeight(): number {
|
||||
return this.dimension.height;
|
||||
}
|
||||
|
||||
public setDimenesion(dimension: Dimension) {
|
||||
this.dimension = dimension;
|
||||
this.compute(this.ratio);
|
||||
}
|
||||
|
||||
private onSashDragStart(): void {
|
||||
this.startPosition = this.position;
|
||||
}
|
||||
|
||||
private onSashDrag(e: ISashEvent): void {
|
||||
this.compute((this.startPosition + (e.currentX - e.startX)) / this.dimension.width);
|
||||
}
|
||||
|
||||
private compute(ratio: number) {
|
||||
this.computeSashPosition(ratio);
|
||||
this.ratio = this.position / this.dimension.width;
|
||||
this._onPositionChange.fire(this.position);
|
||||
}
|
||||
|
||||
private onSashDragEnd(): void {
|
||||
this.sash.layout();
|
||||
}
|
||||
|
||||
private onSashReset(): void {
|
||||
this.ratio = 0.5;
|
||||
this._onPositionChange.fire(this.position);
|
||||
this.sash.layout();
|
||||
}
|
||||
|
||||
private computeSashPosition(sashRatio: number = this.ratio) {
|
||||
let contentWidth = this.dimension.width;
|
||||
let sashPosition = Math.floor((sashRatio || 0.5) * contentWidth);
|
||||
let midPoint = Math.floor(0.5 * contentWidth);
|
||||
|
||||
if (contentWidth > this.minWidth * 2) {
|
||||
if (sashPosition < this.minWidth) {
|
||||
sashPosition = this.minWidth;
|
||||
}
|
||||
if (sashPosition > contentWidth - this.minWidth) {
|
||||
sashPosition = contentWidth - this.minWidth;
|
||||
}
|
||||
} else {
|
||||
sashPosition = midPoint;
|
||||
}
|
||||
if (this.position !== sashPosition) {
|
||||
this.position = sashPosition;
|
||||
this.sash.layout();
|
||||
}
|
||||
}
|
||||
}
|
||||
262
src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts
Normal file
@@ -0,0 +1,262 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as Platform from 'vs/base/common/platform';
|
||||
import * as DomUtils from 'vs/base/browser/dom';
|
||||
import { IMouseEvent, StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState';
|
||||
import { ScrollbarArrow, ScrollbarArrowOptions } from 'vs/base/browser/ui/scrollbar/scrollbarArrow';
|
||||
import { ScrollbarVisibilityController } from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController';
|
||||
import { Scrollable, ScrollbarVisibility, INewScrollPosition } from 'vs/base/common/scrollable';
|
||||
|
||||
/**
|
||||
* The orthogonal distance to the slider at which dragging "resets". This implements "snapping"
|
||||
*/
|
||||
const MOUSE_DRAG_RESET_DISTANCE = 140;
|
||||
|
||||
export interface ISimplifiedMouseEvent {
|
||||
posx: number;
|
||||
posy: number;
|
||||
}
|
||||
|
||||
export interface ScrollbarHost {
|
||||
onMouseWheel(mouseWheelEvent: StandardMouseWheelEvent): void;
|
||||
onDragStart(): void;
|
||||
onDragEnd(): void;
|
||||
}
|
||||
|
||||
export interface AbstractScrollbarOptions {
|
||||
lazyRender: boolean;
|
||||
host: ScrollbarHost;
|
||||
scrollbarState: ScrollbarState;
|
||||
visibility: ScrollbarVisibility;
|
||||
extraScrollbarClassName: string;
|
||||
scrollable: Scrollable;
|
||||
}
|
||||
|
||||
export abstract class AbstractScrollbar extends Widget {
|
||||
|
||||
protected _host: ScrollbarHost;
|
||||
protected _scrollable: Scrollable;
|
||||
private _lazyRender: boolean;
|
||||
protected _scrollbarState: ScrollbarState;
|
||||
private _visibilityController: ScrollbarVisibilityController;
|
||||
private _mouseMoveMonitor: GlobalMouseMoveMonitor<IStandardMouseMoveEventData>;
|
||||
|
||||
public domNode: FastDomNode<HTMLElement>;
|
||||
public slider: FastDomNode<HTMLElement>;
|
||||
|
||||
protected _shouldRender: boolean;
|
||||
|
||||
constructor(opts: AbstractScrollbarOptions) {
|
||||
super();
|
||||
this._lazyRender = opts.lazyRender;
|
||||
this._host = opts.host;
|
||||
this._scrollable = opts.scrollable;
|
||||
this._scrollbarState = opts.scrollbarState;
|
||||
this._visibilityController = this._register(new ScrollbarVisibilityController(opts.visibility, 'visible scrollbar ' + opts.extraScrollbarClassName, 'invisible scrollbar ' + opts.extraScrollbarClassName));
|
||||
this._mouseMoveMonitor = this._register(new GlobalMouseMoveMonitor<IStandardMouseMoveEventData>());
|
||||
this._shouldRender = true;
|
||||
this.domNode = createFastDomNode(document.createElement('div'));
|
||||
this.domNode.setAttribute('role', 'presentation');
|
||||
this.domNode.setAttribute('aria-hidden', 'true');
|
||||
|
||||
this._visibilityController.setDomNode(this.domNode);
|
||||
this.domNode.setPosition('absolute');
|
||||
|
||||
this.onmousedown(this.domNode.domNode, (e) => this._domNodeMouseDown(e));
|
||||
}
|
||||
|
||||
// ----------------- creation
|
||||
|
||||
/**
|
||||
* Creates the dom node for an arrow & adds it to the container
|
||||
*/
|
||||
protected _createArrow(opts: ScrollbarArrowOptions): void {
|
||||
let arrow = this._register(new ScrollbarArrow(opts));
|
||||
this.domNode.domNode.appendChild(arrow.bgDomNode);
|
||||
this.domNode.domNode.appendChild(arrow.domNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the slider dom node, adds it to the container & hooks up the events
|
||||
*/
|
||||
protected _createSlider(top: number, left: number, width: number, height: number): void {
|
||||
this.slider = createFastDomNode(document.createElement('div'));
|
||||
this.slider.setClassName('slider');
|
||||
this.slider.setPosition('absolute');
|
||||
this.slider.setTop(top);
|
||||
this.slider.setLeft(left);
|
||||
this.slider.setWidth(width);
|
||||
this.slider.setHeight(height);
|
||||
this.slider.setLayerHinting(true);
|
||||
|
||||
this.domNode.domNode.appendChild(this.slider.domNode);
|
||||
|
||||
this.onmousedown(this.slider.domNode, (e) => {
|
||||
if (e.leftButton) {
|
||||
e.preventDefault();
|
||||
this._sliderMouseDown(e, () => { /*nothing to do*/ });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------- Update state
|
||||
|
||||
protected _onElementSize(visibleSize: number): boolean {
|
||||
if (this._scrollbarState.setVisibleSize(visibleSize)) {
|
||||
this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
|
||||
this._shouldRender = true;
|
||||
if (!this._lazyRender) {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
return this._shouldRender;
|
||||
}
|
||||
|
||||
protected _onElementScrollSize(elementScrollSize: number): boolean {
|
||||
if (this._scrollbarState.setScrollSize(elementScrollSize)) {
|
||||
this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
|
||||
this._shouldRender = true;
|
||||
if (!this._lazyRender) {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
return this._shouldRender;
|
||||
}
|
||||
|
||||
protected _onElementScrollPosition(elementScrollPosition: number): boolean {
|
||||
if (this._scrollbarState.setScrollPosition(elementScrollPosition)) {
|
||||
this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
|
||||
this._shouldRender = true;
|
||||
if (!this._lazyRender) {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
return this._shouldRender;
|
||||
}
|
||||
|
||||
// ----------------- rendering
|
||||
|
||||
public beginReveal(): void {
|
||||
this._visibilityController.setShouldBeVisible(true);
|
||||
}
|
||||
|
||||
public beginHide(): void {
|
||||
this._visibilityController.setShouldBeVisible(false);
|
||||
}
|
||||
|
||||
public render(): void {
|
||||
if (!this._shouldRender) {
|
||||
return;
|
||||
}
|
||||
this._shouldRender = false;
|
||||
|
||||
this._renderDomNode(this._scrollbarState.getRectangleLargeSize(), this._scrollbarState.getRectangleSmallSize());
|
||||
this._updateSlider(this._scrollbarState.getSliderSize(), this._scrollbarState.getArrowSize() + this._scrollbarState.getSliderPosition());
|
||||
}
|
||||
// ----------------- DOM events
|
||||
|
||||
private _domNodeMouseDown(e: IMouseEvent): void {
|
||||
if (e.target !== this.domNode.domNode) {
|
||||
return;
|
||||
}
|
||||
this._onMouseDown(e);
|
||||
}
|
||||
|
||||
public delegateMouseDown(e: IMouseEvent): void {
|
||||
let domTop = this.domNode.domNode.getClientRects()[0].top;
|
||||
let sliderStart = domTop + this._scrollbarState.getSliderPosition();
|
||||
let sliderStop = domTop + this._scrollbarState.getSliderPosition() + this._scrollbarState.getSliderSize();
|
||||
let mousePos = this._sliderMousePosition(e);
|
||||
if (sliderStart <= mousePos && mousePos <= sliderStop) {
|
||||
// Act as if it was a mouse down on the slider
|
||||
if (e.leftButton) {
|
||||
e.preventDefault();
|
||||
this._sliderMouseDown(e, () => { /*nothing to do*/ });
|
||||
}
|
||||
} else {
|
||||
// Act as if it was a mouse down on the scrollbar
|
||||
this._onMouseDown(e);
|
||||
}
|
||||
}
|
||||
|
||||
public delegateSliderMouseDown(e: ISimplifiedMouseEvent, onDragFinished: () => void): void {
|
||||
this._sliderMouseDown(e, onDragFinished);
|
||||
}
|
||||
|
||||
private _onMouseDown(e: IMouseEvent): void {
|
||||
let offsetX: number;
|
||||
let offsetY: number;
|
||||
if (e.target === this.domNode.domNode && typeof e.browserEvent.offsetX === 'number' && typeof e.browserEvent.offsetY === 'number') {
|
||||
offsetX = e.browserEvent.offsetX;
|
||||
offsetY = e.browserEvent.offsetY;
|
||||
} else {
|
||||
const domNodePosition = DomUtils.getDomNodePagePosition(this.domNode.domNode);
|
||||
offsetX = e.posx - domNodePosition.left;
|
||||
offsetY = e.posy - domNodePosition.top;
|
||||
}
|
||||
this._setDesiredScrollPositionNow(this._scrollbarState.getDesiredScrollPositionFromOffset(this._mouseDownRelativePosition(offsetX, offsetY)));
|
||||
if (e.leftButton) {
|
||||
e.preventDefault();
|
||||
this._sliderMouseDown(e, () => { /*nothing to do*/ });
|
||||
}
|
||||
}
|
||||
|
||||
private _sliderMouseDown(e: ISimplifiedMouseEvent, onDragFinished: () => void): void {
|
||||
const initialMousePosition = this._sliderMousePosition(e);
|
||||
const initialMouseOrthogonalPosition = this._sliderOrthogonalMousePosition(e);
|
||||
const initialScrollbarState = this._scrollbarState.clone();
|
||||
this.slider.toggleClassName('active', true);
|
||||
|
||||
this._mouseMoveMonitor.startMonitoring(
|
||||
standardMouseMoveMerger,
|
||||
(mouseMoveData: IStandardMouseMoveEventData) => {
|
||||
const mouseOrthogonalPosition = this._sliderOrthogonalMousePosition(mouseMoveData);
|
||||
const mouseOrthogonalDelta = Math.abs(mouseOrthogonalPosition - initialMouseOrthogonalPosition);
|
||||
|
||||
if (Platform.isWindows && mouseOrthogonalDelta > MOUSE_DRAG_RESET_DISTANCE) {
|
||||
// The mouse has wondered away from the scrollbar => reset dragging
|
||||
this._setDesiredScrollPositionNow(initialScrollbarState.getScrollPosition());
|
||||
return;
|
||||
}
|
||||
|
||||
const mousePosition = this._sliderMousePosition(mouseMoveData);
|
||||
const mouseDelta = mousePosition - initialMousePosition;
|
||||
this._setDesiredScrollPositionNow(initialScrollbarState.getDesiredScrollPositionFromDelta(mouseDelta));
|
||||
},
|
||||
() => {
|
||||
this.slider.toggleClassName('active', false);
|
||||
this._host.onDragEnd();
|
||||
onDragFinished();
|
||||
}
|
||||
);
|
||||
|
||||
this._host.onDragStart();
|
||||
}
|
||||
|
||||
private _setDesiredScrollPositionNow(_desiredScrollPosition: number): void {
|
||||
|
||||
let desiredScrollPosition: INewScrollPosition = {};
|
||||
this.writeScrollPosition(desiredScrollPosition, _desiredScrollPosition);
|
||||
|
||||
this._scrollable.setScrollPositionNow(desiredScrollPosition);
|
||||
}
|
||||
|
||||
// ----------------- Overwrite these
|
||||
|
||||
protected abstract _renderDomNode(largeSize: number, smallSize: number): void;
|
||||
protected abstract _updateSlider(sliderSize: number, sliderPosition: number): void;
|
||||
|
||||
protected abstract _mouseDownRelativePosition(offsetX: number, offsetY: number): number;
|
||||
protected abstract _sliderMousePosition(e: ISimplifiedMouseEvent): number;
|
||||
protected abstract _sliderOrthogonalMousePosition(e: ISimplifiedMouseEvent): number;
|
||||
|
||||
public abstract writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void;
|
||||
}
|
||||
94
src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { AbstractScrollbar, ScrollbarHost, ISimplifiedMouseEvent } from 'vs/base/browser/ui/scrollbar/abstractScrollbar';
|
||||
import { StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
import { Scrollable, ScrollEvent, ScrollbarVisibility, INewScrollPosition } from 'vs/base/common/scrollable';
|
||||
import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState';
|
||||
import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow';
|
||||
|
||||
export class HorizontalScrollbar extends AbstractScrollbar {
|
||||
|
||||
constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) {
|
||||
super({
|
||||
lazyRender: options.lazyRender,
|
||||
host: host,
|
||||
scrollbarState: new ScrollbarState(
|
||||
(options.horizontalHasArrows ? options.arrowSize : 0),
|
||||
(options.horizontal === ScrollbarVisibility.Hidden ? 0 : options.horizontalScrollbarSize),
|
||||
(options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize)
|
||||
),
|
||||
visibility: options.horizontal,
|
||||
extraScrollbarClassName: 'horizontal',
|
||||
scrollable: scrollable
|
||||
});
|
||||
|
||||
if (options.horizontalHasArrows) {
|
||||
let arrowDelta = (options.arrowSize - ARROW_IMG_SIZE) / 2;
|
||||
let scrollbarDelta = (options.horizontalScrollbarSize - ARROW_IMG_SIZE) / 2;
|
||||
|
||||
this._createArrow({
|
||||
className: 'left-arrow',
|
||||
top: scrollbarDelta,
|
||||
left: arrowDelta,
|
||||
bottom: void 0,
|
||||
right: void 0,
|
||||
bgWidth: options.arrowSize,
|
||||
bgHeight: options.horizontalScrollbarSize,
|
||||
onActivate: () => this._host.onMouseWheel(new StandardMouseWheelEvent(null, 1, 0)),
|
||||
});
|
||||
|
||||
this._createArrow({
|
||||
className: 'right-arrow',
|
||||
top: scrollbarDelta,
|
||||
left: void 0,
|
||||
bottom: void 0,
|
||||
right: arrowDelta,
|
||||
bgWidth: options.arrowSize,
|
||||
bgHeight: options.horizontalScrollbarSize,
|
||||
onActivate: () => this._host.onMouseWheel(new StandardMouseWheelEvent(null, -1, 0)),
|
||||
});
|
||||
}
|
||||
|
||||
this._createSlider(Math.floor((options.horizontalScrollbarSize - options.horizontalSliderSize) / 2), 0, null, options.horizontalSliderSize);
|
||||
}
|
||||
|
||||
protected _updateSlider(sliderSize: number, sliderPosition: number): void {
|
||||
this.slider.setWidth(sliderSize);
|
||||
this.slider.setLeft(sliderPosition);
|
||||
}
|
||||
|
||||
protected _renderDomNode(largeSize: number, smallSize: number): void {
|
||||
this.domNode.setWidth(largeSize);
|
||||
this.domNode.setHeight(smallSize);
|
||||
this.domNode.setLeft(0);
|
||||
this.domNode.setBottom(0);
|
||||
}
|
||||
|
||||
public onDidScroll(e: ScrollEvent): boolean {
|
||||
this._shouldRender = this._onElementScrollSize(e.scrollWidth) || this._shouldRender;
|
||||
this._shouldRender = this._onElementScrollPosition(e.scrollLeft) || this._shouldRender;
|
||||
this._shouldRender = this._onElementSize(e.width) || this._shouldRender;
|
||||
return this._shouldRender;
|
||||
}
|
||||
|
||||
protected _mouseDownRelativePosition(offsetX: number, offsetY: number): number {
|
||||
return offsetX;
|
||||
}
|
||||
|
||||
protected _sliderMousePosition(e: ISimplifiedMouseEvent): number {
|
||||
return e.posx;
|
||||
}
|
||||
|
||||
protected _sliderOrthogonalMousePosition(e: ISimplifiedMouseEvent): number {
|
||||
return e.posy;
|
||||
}
|
||||
|
||||
public writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void {
|
||||
target.scrollLeft = scrollPosition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 11"><path transform="rotate(-180 5.49045991897583,5.811500072479248)" fill="#E8E8E8" d="m9.48046,8.9615l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z"/></svg>
|
||||
|
After Width: | Height: | Size: 233 B |
1
src/vs/base/browser/ui/scrollbar/media/arrow-down.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 11"><path transform="rotate(-180 5.49045991897583,5.811500072479248)" fill="#424242" d="m9.48046,8.9615l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z"/></svg>
|
||||
|
After Width: | Height: | Size: 233 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 11"><path transform="rotate(-90 5.490459918975831,5.431382179260254)" fill="#E8E8E8" d="m9.48046,8.58138l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z"/></svg>
|
||||
|
After Width: | Height: | Size: 234 B |
1
src/vs/base/browser/ui/scrollbar/media/arrow-left.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 11"><path transform="rotate(-90 5.490459918975831,5.431382179260254)" fill="#424242" d="m9.48046,8.58138l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z"/></svg>
|
||||
|
After Width: | Height: | Size: 234 B |
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 11 11" xmlns="http://www.w3.org/2000/svg"><path transform="rotate(90 5.6171650886535645,5.55808973312378) " fill="#E8E8E8" d="m9.60717,8.70809l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z"/></svg>
|
||||
|
After Width: | Height: | Size: 234 B |
1
src/vs/base/browser/ui/scrollbar/media/arrow-right.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 11 11" xmlns="http://www.w3.org/2000/svg"><path transform="rotate(90 5.6171650886535645,5.55808973312378) " fill="#424242" d="m9.60717,8.70809l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z"/></svg>
|
||||
|
After Width: | Height: | Size: 234 B |
1
src/vs/base/browser/ui/scrollbar/media/arrow-up-dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 11 11" xmlns="http://www.w3.org/2000/svg"><path d="m9.48046,8.9615l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z" fill="#E8E8E8"/></svg>
|
||||
|
After Width: | Height: | Size: 173 B |
1
src/vs/base/browser/ui/scrollbar/media/arrow-up.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 11 11" xmlns="http://www.w3.org/2000/svg"><path d="m9.48046,8.9615l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z" fill="#424242"/></svg>
|
||||
|
After Width: | Height: | Size: 173 B |
147
src/vs/base/browser/ui/scrollbar/media/scrollbars.css
Normal file
@@ -0,0 +1,147 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Arrows */
|
||||
.monaco-scrollable-element > .scrollbar > .up-arrow {
|
||||
background: url('arrow-up.svg');
|
||||
cursor: pointer;
|
||||
}
|
||||
.monaco-scrollable-element > .scrollbar > .down-arrow {
|
||||
background: url('arrow-down.svg');
|
||||
cursor: pointer;
|
||||
}
|
||||
.monaco-scrollable-element > .scrollbar > .left-arrow {
|
||||
background: url('arrow-left.svg');
|
||||
cursor: pointer;
|
||||
}
|
||||
.monaco-scrollable-element > .scrollbar > .right-arrow {
|
||||
background: url('arrow-right.svg');
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hc-black .monaco-scrollable-element > .scrollbar > .up-arrow,
|
||||
.vs-dark .monaco-scrollable-element > .scrollbar > .up-arrow {
|
||||
background: url('arrow-up-dark.svg');
|
||||
}
|
||||
.hc-black .monaco-scrollable-element > .scrollbar > .down-arrow,
|
||||
.vs-dark .monaco-scrollable-element > .scrollbar > .down-arrow {
|
||||
background: url('arrow-down-dark.svg');
|
||||
}
|
||||
.hc-black .monaco-scrollable-element > .scrollbar > .left-arrow,
|
||||
.vs-dark .monaco-scrollable-element > .scrollbar > .left-arrow {
|
||||
background: url('arrow-left-dark.svg');
|
||||
}
|
||||
.hc-black .monaco-scrollable-element > .scrollbar > .right-arrow,
|
||||
.vs-dark .monaco-scrollable-element > .scrollbar > .right-arrow {
|
||||
background: url('arrow-right-dark.svg');
|
||||
}
|
||||
|
||||
.monaco-scrollable-element > .visible {
|
||||
opacity: 1;
|
||||
|
||||
/* Background rule added for IE9 - to allow clicks on dom node */
|
||||
background:rgba(0,0,0,0);
|
||||
|
||||
-webkit-transition: opacity 100ms linear;
|
||||
-o-transition: opacity 100ms linear;
|
||||
-moz-transition: opacity 100ms linear;
|
||||
-ms-transition: opacity 100ms linear;
|
||||
transition: opacity 100ms linear;
|
||||
}
|
||||
.monaco-scrollable-element > .invisible {
|
||||
opacity: 0;
|
||||
}
|
||||
.monaco-scrollable-element > .invisible.fade {
|
||||
-webkit-transition: opacity 800ms linear;
|
||||
-o-transition: opacity 800ms linear;
|
||||
-moz-transition: opacity 800ms linear;
|
||||
-ms-transition: opacity 800ms linear;
|
||||
transition: opacity 800ms linear;
|
||||
}
|
||||
|
||||
/* Scrollable Content Inset Shadow */
|
||||
.monaco-scrollable-element > .shadow {
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
.monaco-scrollable-element > .shadow.top {
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 3px;
|
||||
height: 3px;
|
||||
width: 100%;
|
||||
box-shadow: #DDD 0 6px 6px -6px inset;
|
||||
}
|
||||
.monaco-scrollable-element > .shadow.left {
|
||||
display: block;
|
||||
top: 3px;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 3px;
|
||||
box-shadow: #DDD 6px 0 6px -6px inset;
|
||||
}
|
||||
.monaco-scrollable-element > .shadow.top-left-corner {
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 3px;
|
||||
width: 3px;
|
||||
}
|
||||
.monaco-scrollable-element > .shadow.top.left {
|
||||
box-shadow: #DDD 6px 6px 6px -6px inset;
|
||||
}
|
||||
|
||||
/* ---------- Default Style ---------- */
|
||||
|
||||
.vs .monaco-scrollable-element > .scrollbar > .slider {
|
||||
background: rgba(100, 100, 100, .4);
|
||||
}
|
||||
.vs-dark .monaco-scrollable-element > .scrollbar > .slider {
|
||||
background: rgba(121, 121, 121, .4);
|
||||
}
|
||||
.hc-black .monaco-scrollable-element > .scrollbar > .slider {
|
||||
background: rgba(111, 195, 223, .6);
|
||||
}
|
||||
|
||||
.monaco-scrollable-element > .scrollbar > .slider:hover {
|
||||
background: rgba(100, 100, 100, .7);
|
||||
}
|
||||
.hc-black .monaco-scrollable-element > .scrollbar > .slider:hover {
|
||||
background: rgba(111, 195, 223, .8);
|
||||
}
|
||||
|
||||
.monaco-scrollable-element > .scrollbar > .slider.active {
|
||||
background: rgba(0, 0, 0, .6);
|
||||
}
|
||||
.vs-dark .monaco-scrollable-element > .scrollbar > .slider.active {
|
||||
background: rgba(191, 191, 191, .4);
|
||||
}
|
||||
.hc-black .monaco-scrollable-element > .scrollbar > .slider.active {
|
||||
background: rgba(111, 195, 223, 1);
|
||||
}
|
||||
|
||||
.vs-dark .monaco-scrollable-element .shadow.top {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-scrollable-element .shadow.left {
|
||||
box-shadow: #000 6px 0 6px -6px inset;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-scrollable-element .shadow.top.left {
|
||||
box-shadow: #000 6px 6px 6px -6px inset;
|
||||
}
|
||||
|
||||
.hc-black .monaco-scrollable-element .shadow.top {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.hc-black .monaco-scrollable-element .shadow.left {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.hc-black .monaco-scrollable-element .shadow.top.left {
|
||||
box-shadow: none;
|
||||
}
|
||||
580
src/vs/base/browser/ui/scrollbar/scrollableElement.ts
Normal file
@@ -0,0 +1,580 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./media/scrollbars';
|
||||
|
||||
import * as DomUtils from 'vs/base/browser/dom';
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
import { StandardMouseWheelEvent, IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { HorizontalScrollbar } from 'vs/base/browser/ui/scrollbar/horizontalScrollbar';
|
||||
import { VerticalScrollbar } from 'vs/base/browser/ui/scrollbar/verticalScrollbar';
|
||||
import { ScrollableElementCreationOptions, ScrollableElementChangeOptions, ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Scrollable, ScrollEvent, ScrollbarVisibility, INewScrollDimensions, IScrollDimensions, INewScrollPosition, IScrollPosition } from 'vs/base/common/scrollable';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { TimeoutTimer } from 'vs/base/common/async';
|
||||
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { ScrollbarHost, ISimplifiedMouseEvent } from 'vs/base/browser/ui/scrollbar/abstractScrollbar';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
|
||||
const HIDE_TIMEOUT = 500;
|
||||
const SCROLL_WHEEL_SENSITIVITY = 50;
|
||||
const SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED = true;
|
||||
|
||||
export interface IOverviewRulerLayoutInfo {
|
||||
parent: HTMLElement;
|
||||
insertBefore: HTMLElement;
|
||||
}
|
||||
|
||||
class MouseWheelClassifierItem {
|
||||
public timestamp: number;
|
||||
public deltaX: number;
|
||||
public deltaY: number;
|
||||
public score: number;
|
||||
|
||||
constructor(timestamp: number, deltaX: number, deltaY: number) {
|
||||
this.timestamp = timestamp;
|
||||
this.deltaX = deltaX;
|
||||
this.deltaY = deltaY;
|
||||
this.score = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class MouseWheelClassifier {
|
||||
|
||||
public static INSTANCE = new MouseWheelClassifier();
|
||||
|
||||
private readonly _capacity: number;
|
||||
private _memory: MouseWheelClassifierItem[];
|
||||
private _front: number;
|
||||
private _rear: number;
|
||||
|
||||
constructor() {
|
||||
this._capacity = 5;
|
||||
this._memory = [];
|
||||
this._front = -1;
|
||||
this._rear = -1;
|
||||
}
|
||||
|
||||
public isPhysicalMouseWheel(): boolean {
|
||||
if (this._front === -1 && this._rear === -1) {
|
||||
// no elements
|
||||
return false;
|
||||
}
|
||||
|
||||
// 0.5 * last + 0.25 * before last + 0.125 * before before last + ...
|
||||
let remainingInfluence = 1;
|
||||
let score = 0;
|
||||
let iteration = 1;
|
||||
|
||||
let index = this._rear;
|
||||
do {
|
||||
const influence = (index === this._front ? remainingInfluence : Math.pow(2, -iteration));
|
||||
remainingInfluence -= influence;
|
||||
score += this._memory[index].score * influence;
|
||||
|
||||
if (index === this._front) {
|
||||
break;
|
||||
}
|
||||
|
||||
index = (this._capacity + index - 1) % this._capacity;
|
||||
iteration++;
|
||||
} while (true);
|
||||
|
||||
return (score <= 0.5);
|
||||
}
|
||||
|
||||
public accept(timestamp: number, deltaX: number, deltaY: number): void {
|
||||
const item = new MouseWheelClassifierItem(timestamp, deltaX, deltaY);
|
||||
item.score = this._computeScore(item);
|
||||
|
||||
if (this._front === -1 && this._rear === -1) {
|
||||
this._memory[0] = item;
|
||||
this._front = 0;
|
||||
this._rear = 0;
|
||||
} else {
|
||||
this._rear = (this._rear + 1) % this._capacity;
|
||||
if (this._rear === this._front) {
|
||||
// Drop oldest
|
||||
this._front = (this._front + 1) % this._capacity;
|
||||
}
|
||||
this._memory[this._rear] = item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A score between 0 and 1 for `item`.
|
||||
* - a score towards 0 indicates that the source appears to be a physical mouse wheel
|
||||
* - a score towards 1 indicates that the source appears to be a touchpad or magic mouse, etc.
|
||||
*/
|
||||
private _computeScore(item: MouseWheelClassifierItem): number {
|
||||
|
||||
if (Math.abs(item.deltaX) > 0 && Math.abs(item.deltaY) > 0) {
|
||||
// both axes exercised => definitely not a physical mouse wheel
|
||||
return 1;
|
||||
}
|
||||
|
||||
let score: number = 0.5;
|
||||
const prev = (this._front === -1 && this._rear === -1 ? null : this._memory[this._rear]);
|
||||
if (prev) {
|
||||
// const deltaT = item.timestamp - prev.timestamp;
|
||||
// if (deltaT < 1000 / 30) {
|
||||
// // sooner than X times per second => indicator that this is not a physical mouse wheel
|
||||
// score += 0.25;
|
||||
// }
|
||||
|
||||
// if (item.deltaX === prev.deltaX && item.deltaY === prev.deltaY) {
|
||||
// // equal amplitude => indicator that this is a physical mouse wheel
|
||||
// score -= 0.25;
|
||||
// }
|
||||
}
|
||||
|
||||
if (Math.abs(item.deltaX - Math.round(item.deltaX)) > 0 || Math.abs(item.deltaY - Math.round(item.deltaY)) > 0) {
|
||||
// non-integer deltas => indicator that this is not a physical mouse wheel
|
||||
score += 0.25;
|
||||
}
|
||||
|
||||
return Math.min(Math.max(score, 0), 1);
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class AbstractScrollableElement extends Widget {
|
||||
|
||||
private readonly _options: ScrollableElementResolvedOptions;
|
||||
protected readonly _scrollable: Scrollable;
|
||||
private readonly _verticalScrollbar: VerticalScrollbar;
|
||||
private readonly _horizontalScrollbar: HorizontalScrollbar;
|
||||
private readonly _domNode: HTMLElement;
|
||||
|
||||
private readonly _leftShadowDomNode: FastDomNode<HTMLElement>;
|
||||
private readonly _topShadowDomNode: FastDomNode<HTMLElement>;
|
||||
private readonly _topLeftShadowDomNode: FastDomNode<HTMLElement>;
|
||||
|
||||
private readonly _listenOnDomNode: HTMLElement;
|
||||
|
||||
private _mouseWheelToDispose: IDisposable[];
|
||||
|
||||
private _isDragging: boolean;
|
||||
private _mouseIsOver: boolean;
|
||||
|
||||
private readonly _hideTimeout: TimeoutTimer;
|
||||
private _shouldRender: boolean;
|
||||
|
||||
private readonly _onScroll = this._register(new Emitter<ScrollEvent>());
|
||||
public onScroll: Event<ScrollEvent> = this._onScroll.event;
|
||||
|
||||
protected constructor(element: HTMLElement, options: ScrollableElementCreationOptions, scrollable?: Scrollable) {
|
||||
super();
|
||||
element.style.overflow = 'hidden';
|
||||
this._options = resolveOptions(options);
|
||||
this._scrollable = scrollable;
|
||||
|
||||
this._register(this._scrollable.onScroll((e) => {
|
||||
this._onDidScroll(e);
|
||||
this._onScroll.fire(e);
|
||||
}));
|
||||
|
||||
let scrollbarHost: ScrollbarHost = {
|
||||
onMouseWheel: (mouseWheelEvent: StandardMouseWheelEvent) => this._onMouseWheel(mouseWheelEvent),
|
||||
onDragStart: () => this._onDragStart(),
|
||||
onDragEnd: () => this._onDragEnd(),
|
||||
};
|
||||
this._verticalScrollbar = this._register(new VerticalScrollbar(this._scrollable, this._options, scrollbarHost));
|
||||
this._horizontalScrollbar = this._register(new HorizontalScrollbar(this._scrollable, this._options, scrollbarHost));
|
||||
|
||||
this._domNode = document.createElement('div');
|
||||
this._domNode.className = 'monaco-scrollable-element ' + this._options.className;
|
||||
this._domNode.setAttribute('role', 'presentation');
|
||||
this._domNode.style.position = 'relative';
|
||||
this._domNode.style.overflow = 'hidden';
|
||||
this._domNode.appendChild(element);
|
||||
this._domNode.appendChild(this._horizontalScrollbar.domNode.domNode);
|
||||
this._domNode.appendChild(this._verticalScrollbar.domNode.domNode);
|
||||
|
||||
if (this._options.useShadows) {
|
||||
this._leftShadowDomNode = createFastDomNode(document.createElement('div'));
|
||||
this._leftShadowDomNode.setClassName('shadow');
|
||||
this._domNode.appendChild(this._leftShadowDomNode.domNode);
|
||||
|
||||
this._topShadowDomNode = createFastDomNode(document.createElement('div'));
|
||||
this._topShadowDomNode.setClassName('shadow');
|
||||
this._domNode.appendChild(this._topShadowDomNode.domNode);
|
||||
|
||||
this._topLeftShadowDomNode = createFastDomNode(document.createElement('div'));
|
||||
this._topLeftShadowDomNode.setClassName('shadow top-left-corner');
|
||||
this._domNode.appendChild(this._topLeftShadowDomNode.domNode);
|
||||
}
|
||||
|
||||
this._listenOnDomNode = this._options.listenOnDomNode || this._domNode;
|
||||
|
||||
this._mouseWheelToDispose = [];
|
||||
this._setListeningToMouseWheel(this._options.handleMouseWheel);
|
||||
|
||||
this.onmouseover(this._listenOnDomNode, (e) => this._onMouseOver(e));
|
||||
this.onnonbubblingmouseout(this._listenOnDomNode, (e) => this._onMouseOut(e));
|
||||
|
||||
this._hideTimeout = this._register(new TimeoutTimer());
|
||||
this._isDragging = false;
|
||||
this._mouseIsOver = false;
|
||||
|
||||
this._shouldRender = true;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._mouseWheelToDispose = dispose(this._mouseWheelToDispose);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generated 'scrollable' dom node
|
||||
*/
|
||||
public getDomNode(): HTMLElement {
|
||||
return this._domNode;
|
||||
}
|
||||
|
||||
public getOverviewRulerLayoutInfo(): IOverviewRulerLayoutInfo {
|
||||
return {
|
||||
parent: this._domNode,
|
||||
insertBefore: this._verticalScrollbar.domNode.domNode,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate a mouse down event to the vertical scrollbar.
|
||||
* This is to help with clicking somewhere else and having the scrollbar react.
|
||||
*/
|
||||
public delegateVerticalScrollbarMouseDown(browserEvent: IMouseEvent): void {
|
||||
this._verticalScrollbar.delegateMouseDown(browserEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate a mouse down event to the vertical scrollbar (directly to the slider!).
|
||||
* This is to help with clicking somewhere else and having the scrollbar react.
|
||||
*/
|
||||
public delegateSliderMouseDown(e: ISimplifiedMouseEvent, onDragFinished: () => void): void {
|
||||
this._verticalScrollbar.delegateSliderMouseDown(e, onDragFinished);
|
||||
}
|
||||
|
||||
public getScrollDimensions(): IScrollDimensions {
|
||||
return this._scrollable.getScrollDimensions();
|
||||
}
|
||||
|
||||
public setScrollDimensions(dimensions: INewScrollDimensions): void {
|
||||
this._scrollable.setScrollDimensions(dimensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the class name of the scrollable element.
|
||||
*/
|
||||
public updateClassName(newClassName: string): void {
|
||||
this._options.className = newClassName;
|
||||
// Defaults are different on Macs
|
||||
if (Platform.isMacintosh) {
|
||||
this._options.className += ' mac';
|
||||
}
|
||||
this._domNode.className = 'monaco-scrollable-element ' + this._options.className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update configuration options for the scrollbar.
|
||||
* Really this is Editor.IEditorScrollbarOptions, but base shouldn't
|
||||
* depend on Editor.
|
||||
*/
|
||||
public updateOptions(newOptions: ScrollableElementChangeOptions): void {
|
||||
let massagedOptions = resolveOptions(newOptions);
|
||||
this._options.handleMouseWheel = massagedOptions.handleMouseWheel;
|
||||
this._options.mouseWheelScrollSensitivity = massagedOptions.mouseWheelScrollSensitivity;
|
||||
this._setListeningToMouseWheel(this._options.handleMouseWheel);
|
||||
|
||||
if (!this._options.lazyRender) {
|
||||
this._render();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- mouse wheel scrolling --------------------
|
||||
|
||||
private _setListeningToMouseWheel(shouldListen: boolean): void {
|
||||
let isListening = (this._mouseWheelToDispose.length > 0);
|
||||
|
||||
if (isListening === shouldListen) {
|
||||
// No change
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop listening (if necessary)
|
||||
this._mouseWheelToDispose = dispose(this._mouseWheelToDispose);
|
||||
|
||||
// Start listening (if necessary)
|
||||
if (shouldListen) {
|
||||
let onMouseWheel = (browserEvent: MouseWheelEvent) => {
|
||||
let e = new StandardMouseWheelEvent(browserEvent);
|
||||
this._onMouseWheel(e);
|
||||
};
|
||||
|
||||
this._mouseWheelToDispose.push(DomUtils.addDisposableListener(this._listenOnDomNode, 'mousewheel', onMouseWheel));
|
||||
this._mouseWheelToDispose.push(DomUtils.addDisposableListener(this._listenOnDomNode, 'DOMMouseScroll', onMouseWheel));
|
||||
}
|
||||
}
|
||||
|
||||
private _onMouseWheel(e: StandardMouseWheelEvent): void {
|
||||
|
||||
const classifier = MouseWheelClassifier.INSTANCE;
|
||||
if (SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED) {
|
||||
classifier.accept(Date.now(), e.deltaX, e.deltaY);
|
||||
}
|
||||
|
||||
// console.log(`${Date.now()}, ${e.deltaY}, ${e.deltaX}`);
|
||||
|
||||
if (e.deltaY || e.deltaX) {
|
||||
let deltaY = e.deltaY * this._options.mouseWheelScrollSensitivity;
|
||||
let deltaX = e.deltaX * this._options.mouseWheelScrollSensitivity;
|
||||
|
||||
if (this._options.flipAxes) {
|
||||
[deltaY, deltaX] = [deltaX, deltaY];
|
||||
}
|
||||
|
||||
// Convert vertical scrolling to horizontal if shift is held, this
|
||||
// is handled at a higher level on Mac
|
||||
const shiftConvert = !Platform.isMacintosh && e.browserEvent.shiftKey;
|
||||
if ((this._options.scrollYToX || shiftConvert) && !deltaX) {
|
||||
deltaX = deltaY;
|
||||
deltaY = 0;
|
||||
}
|
||||
|
||||
if (Platform.isMacintosh) {
|
||||
// Give preference to vertical scrolling
|
||||
if (deltaY && Math.abs(deltaX) < 0.2) {
|
||||
deltaX = 0;
|
||||
}
|
||||
if (Math.abs(deltaY) > Math.abs(deltaX) * 0.5) {
|
||||
deltaX = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const futureScrollPosition = this._scrollable.getFutureScrollPosition();
|
||||
|
||||
let desiredScrollPosition: INewScrollPosition = {};
|
||||
if (deltaY) {
|
||||
const desiredScrollTop = futureScrollPosition.scrollTop - SCROLL_WHEEL_SENSITIVITY * deltaY;
|
||||
this._verticalScrollbar.writeScrollPosition(desiredScrollPosition, desiredScrollTop);
|
||||
}
|
||||
if (deltaX) {
|
||||
const desiredScrollLeft = futureScrollPosition.scrollLeft - SCROLL_WHEEL_SENSITIVITY * deltaX;
|
||||
this._horizontalScrollbar.writeScrollPosition(desiredScrollPosition, desiredScrollLeft);
|
||||
}
|
||||
|
||||
// Check that we are scrolling towards a location which is valid
|
||||
desiredScrollPosition = this._scrollable.validateScrollPosition(desiredScrollPosition);
|
||||
|
||||
if (futureScrollPosition.scrollLeft !== desiredScrollPosition.scrollLeft || futureScrollPosition.scrollTop !== desiredScrollPosition.scrollTop) {
|
||||
|
||||
const canPerformSmoothScroll = (
|
||||
SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED
|
||||
&& this._options.mouseWheelSmoothScroll
|
||||
&& classifier.isPhysicalMouseWheel()
|
||||
);
|
||||
|
||||
if (canPerformSmoothScroll) {
|
||||
this._scrollable.setScrollPositionSmooth(desiredScrollPosition);
|
||||
} else {
|
||||
this._scrollable.setScrollPositionNow(desiredScrollPosition);
|
||||
}
|
||||
this._shouldRender = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._options.alwaysConsumeMouseWheel || this._shouldRender) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
private _onDidScroll(e: ScrollEvent): void {
|
||||
this._shouldRender = this._horizontalScrollbar.onDidScroll(e) || this._shouldRender;
|
||||
this._shouldRender = this._verticalScrollbar.onDidScroll(e) || this._shouldRender;
|
||||
|
||||
if (this._options.useShadows) {
|
||||
this._shouldRender = true;
|
||||
}
|
||||
|
||||
this._reveal();
|
||||
|
||||
if (!this._options.lazyRender) {
|
||||
this._render();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render / mutate the DOM now.
|
||||
* Should be used together with the ctor option `lazyRender`.
|
||||
*/
|
||||
public renderNow(): void {
|
||||
if (!this._options.lazyRender) {
|
||||
throw new Error('Please use `lazyRender` together with `renderNow`!');
|
||||
}
|
||||
|
||||
this._render();
|
||||
}
|
||||
|
||||
private _render(): void {
|
||||
if (!this._shouldRender) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._shouldRender = false;
|
||||
|
||||
this._horizontalScrollbar.render();
|
||||
this._verticalScrollbar.render();
|
||||
|
||||
if (this._options.useShadows) {
|
||||
const scrollState = this._scrollable.getCurrentScrollPosition();
|
||||
let enableTop = scrollState.scrollTop > 0;
|
||||
let enableLeft = scrollState.scrollLeft > 0;
|
||||
|
||||
this._leftShadowDomNode.setClassName('shadow' + (enableLeft ? ' left' : ''));
|
||||
this._topShadowDomNode.setClassName('shadow' + (enableTop ? ' top' : ''));
|
||||
this._topLeftShadowDomNode.setClassName('shadow top-left-corner' + (enableTop ? ' top' : '') + (enableLeft ? ' left' : ''));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- fade in / fade out --------------------
|
||||
|
||||
private _onDragStart(): void {
|
||||
this._isDragging = true;
|
||||
this._reveal();
|
||||
}
|
||||
|
||||
private _onDragEnd(): void {
|
||||
this._isDragging = false;
|
||||
this._hide();
|
||||
}
|
||||
|
||||
private _onMouseOut(e: IMouseEvent): void {
|
||||
this._mouseIsOver = false;
|
||||
this._hide();
|
||||
}
|
||||
|
||||
private _onMouseOver(e: IMouseEvent): void {
|
||||
this._mouseIsOver = true;
|
||||
this._reveal();
|
||||
}
|
||||
|
||||
private _reveal(): void {
|
||||
this._verticalScrollbar.beginReveal();
|
||||
this._horizontalScrollbar.beginReveal();
|
||||
this._scheduleHide();
|
||||
}
|
||||
|
||||
private _hide(): void {
|
||||
if (!this._mouseIsOver && !this._isDragging) {
|
||||
this._verticalScrollbar.beginHide();
|
||||
this._horizontalScrollbar.beginHide();
|
||||
}
|
||||
}
|
||||
|
||||
private _scheduleHide(): void {
|
||||
if (!this._mouseIsOver && !this._isDragging) {
|
||||
this._hideTimeout.cancelAndSet(() => this._hide(), HIDE_TIMEOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ScrollableElement extends AbstractScrollableElement {
|
||||
|
||||
constructor(element: HTMLElement, options: ScrollableElementCreationOptions) {
|
||||
options = options || {};
|
||||
options.mouseWheelSmoothScroll = false;
|
||||
const scrollable = new Scrollable(0, (callback) => DomUtils.scheduleAtNextAnimationFrame(callback));
|
||||
super(element, options, scrollable);
|
||||
this._register(scrollable);
|
||||
}
|
||||
|
||||
public setScrollPosition(update: INewScrollPosition): void {
|
||||
this._scrollable.setScrollPositionNow(update);
|
||||
}
|
||||
|
||||
public getScrollPosition(): IScrollPosition {
|
||||
return this._scrollable.getCurrentScrollPosition();
|
||||
}
|
||||
}
|
||||
|
||||
export class SmoothScrollableElement extends AbstractScrollableElement {
|
||||
|
||||
constructor(element: HTMLElement, options: ScrollableElementCreationOptions, scrollable: Scrollable) {
|
||||
super(element, options, scrollable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class DomScrollableElement extends ScrollableElement {
|
||||
|
||||
private _element: HTMLElement;
|
||||
|
||||
constructor(element: HTMLElement, options: ScrollableElementCreationOptions) {
|
||||
super(element, options);
|
||||
this._element = element;
|
||||
this.onScroll((e) => {
|
||||
if (e.scrollTopChanged) {
|
||||
this._element.scrollTop = e.scrollTop;
|
||||
}
|
||||
if (e.scrollLeftChanged) {
|
||||
this._element.scrollLeft = e.scrollLeft;
|
||||
}
|
||||
});
|
||||
this.scanDomNode();
|
||||
}
|
||||
|
||||
public scanDomNode(): void {
|
||||
// widh, scrollLeft, scrollWidth, height, scrollTop, scrollHeight
|
||||
this.setScrollDimensions({
|
||||
width: this._element.clientWidth,
|
||||
scrollWidth: this._element.scrollWidth,
|
||||
height: this._element.clientHeight,
|
||||
scrollHeight: this._element.scrollHeight
|
||||
});
|
||||
this.setScrollPosition({
|
||||
scrollLeft: this._element.scrollLeft,
|
||||
scrollTop: this._element.scrollTop,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableElementResolvedOptions {
|
||||
let result: ScrollableElementResolvedOptions = {
|
||||
lazyRender: (typeof opts.lazyRender !== 'undefined' ? opts.lazyRender : false),
|
||||
className: (typeof opts.className !== 'undefined' ? opts.className : ''),
|
||||
useShadows: (typeof opts.useShadows !== 'undefined' ? opts.useShadows : true),
|
||||
handleMouseWheel: (typeof opts.handleMouseWheel !== 'undefined' ? opts.handleMouseWheel : true),
|
||||
flipAxes: (typeof opts.flipAxes !== 'undefined' ? opts.flipAxes : false),
|
||||
alwaysConsumeMouseWheel: (typeof opts.alwaysConsumeMouseWheel !== 'undefined' ? opts.alwaysConsumeMouseWheel : false),
|
||||
scrollYToX: (typeof opts.scrollYToX !== 'undefined' ? opts.scrollYToX : false),
|
||||
mouseWheelScrollSensitivity: (typeof opts.mouseWheelScrollSensitivity !== 'undefined' ? opts.mouseWheelScrollSensitivity : 1),
|
||||
mouseWheelSmoothScroll: (typeof opts.mouseWheelSmoothScroll !== 'undefined' ? opts.mouseWheelSmoothScroll : true),
|
||||
arrowSize: (typeof opts.arrowSize !== 'undefined' ? opts.arrowSize : 11),
|
||||
|
||||
listenOnDomNode: (typeof opts.listenOnDomNode !== 'undefined' ? opts.listenOnDomNode : null),
|
||||
|
||||
horizontal: (typeof opts.horizontal !== 'undefined' ? opts.horizontal : ScrollbarVisibility.Auto),
|
||||
horizontalScrollbarSize: (typeof opts.horizontalScrollbarSize !== 'undefined' ? opts.horizontalScrollbarSize : 10),
|
||||
horizontalSliderSize: (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : 0),
|
||||
horizontalHasArrows: (typeof opts.horizontalHasArrows !== 'undefined' ? opts.horizontalHasArrows : false),
|
||||
|
||||
vertical: (typeof opts.vertical !== 'undefined' ? opts.vertical : ScrollbarVisibility.Auto),
|
||||
verticalScrollbarSize: (typeof opts.verticalScrollbarSize !== 'undefined' ? opts.verticalScrollbarSize : 10),
|
||||
verticalHasArrows: (typeof opts.verticalHasArrows !== 'undefined' ? opts.verticalHasArrows : false),
|
||||
verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0)
|
||||
};
|
||||
|
||||
result.horizontalSliderSize = (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : result.horizontalScrollbarSize);
|
||||
result.verticalSliderSize = (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : result.verticalScrollbarSize);
|
||||
|
||||
// Defaults are different on Macs
|
||||
if (Platform.isMacintosh) {
|
||||
result.className += ' mac';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
133
src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
|
||||
export interface ScrollableElementCreationOptions {
|
||||
/**
|
||||
* The scrollable element should not do any DOM mutations until renderNow() is called.
|
||||
* Defaults to false.
|
||||
*/
|
||||
lazyRender?: boolean;
|
||||
/**
|
||||
* CSS Class name for the scrollable element.
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* Drop subtle horizontal and vertical shadows.
|
||||
* Defaults to false.
|
||||
*/
|
||||
useShadows?: boolean;
|
||||
/**
|
||||
* Handle mouse wheel (listen to mouse wheel scrolling).
|
||||
* Defaults to true
|
||||
*/
|
||||
handleMouseWheel?: boolean;
|
||||
/**
|
||||
* If mouse wheel is handled, make mouse wheel scrolling smooth.
|
||||
* Defaults to true.
|
||||
*/
|
||||
mouseWheelSmoothScroll?: boolean;
|
||||
/**
|
||||
* Flip axes. Treat vertical scrolling like horizontal and vice-versa.
|
||||
* Defaults to false.
|
||||
*/
|
||||
flipAxes?: boolean;
|
||||
/**
|
||||
* If enabled, will scroll horizontally when scrolling vertical.
|
||||
* Defaults to false.
|
||||
*/
|
||||
scrollYToX?: boolean;
|
||||
/**
|
||||
* Always consume mouse wheel events, even when scrolling is no longer possible.
|
||||
* Defaults to false.
|
||||
*/
|
||||
alwaysConsumeMouseWheel?: boolean;
|
||||
/**
|
||||
* A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events.
|
||||
* Defaults to 1.
|
||||
*/
|
||||
mouseWheelScrollSensitivity?: number;
|
||||
/**
|
||||
* Height for vertical arrows (top/bottom) and width for horizontal arrows (left/right).
|
||||
* Defaults to 11.
|
||||
*/
|
||||
arrowSize?: number;
|
||||
/**
|
||||
* The dom node events should be bound to.
|
||||
* If no listenOnDomNode is provided, the dom node passed to the constructor will be used for event listening.
|
||||
*/
|
||||
listenOnDomNode?: HTMLElement;
|
||||
/**
|
||||
* Control the visibility of the horizontal scrollbar.
|
||||
* Accepted values: 'auto' (on mouse over), 'visible' (always visible), 'hidden' (never visible)
|
||||
* Defaults to 'auto'.
|
||||
*/
|
||||
horizontal?: ScrollbarVisibility;
|
||||
/**
|
||||
* Height (in px) of the horizontal scrollbar.
|
||||
* Defaults to 10.
|
||||
*/
|
||||
horizontalScrollbarSize?: number;
|
||||
/**
|
||||
* Height (in px) of the horizontal scrollbar slider.
|
||||
* Defaults to `horizontalScrollbarSize`
|
||||
*/
|
||||
horizontalSliderSize?: number;
|
||||
/**
|
||||
* Render arrows (left/right) for the horizontal scrollbar.
|
||||
* Defaults to false.
|
||||
*/
|
||||
horizontalHasArrows?: boolean;
|
||||
/**
|
||||
* Control the visibility of the vertical scrollbar.
|
||||
* Accepted values: 'auto' (on mouse over), 'visible' (always visible), 'hidden' (never visible)
|
||||
* Defaults to 'auto'.
|
||||
*/
|
||||
vertical?: ScrollbarVisibility;
|
||||
/**
|
||||
* Width (in px) of the vertical scrollbar.
|
||||
* Defaults to 10.
|
||||
*/
|
||||
verticalScrollbarSize?: number;
|
||||
/**
|
||||
* Width (in px) of the vertical scrollbar slider.
|
||||
* Defaults to `verticalScrollbarSize`
|
||||
*/
|
||||
verticalSliderSize?: number;
|
||||
/**
|
||||
* Render arrows (top/bottom) for the vertical scrollbar.
|
||||
* Defaults to false.
|
||||
*/
|
||||
verticalHasArrows?: boolean;
|
||||
}
|
||||
|
||||
export interface ScrollableElementChangeOptions {
|
||||
handleMouseWheel?: boolean;
|
||||
mouseWheelScrollSensitivity?: number;
|
||||
}
|
||||
|
||||
export interface ScrollableElementResolvedOptions {
|
||||
lazyRender: boolean;
|
||||
className: string;
|
||||
useShadows: boolean;
|
||||
handleMouseWheel: boolean;
|
||||
flipAxes: boolean;
|
||||
scrollYToX: boolean;
|
||||
alwaysConsumeMouseWheel: boolean;
|
||||
mouseWheelScrollSensitivity: number;
|
||||
mouseWheelSmoothScroll: boolean;
|
||||
arrowSize: number;
|
||||
listenOnDomNode: HTMLElement;
|
||||
horizontal: ScrollbarVisibility;
|
||||
horizontalScrollbarSize: number;
|
||||
horizontalSliderSize: number;
|
||||
horizontalHasArrows: boolean;
|
||||
vertical: ScrollbarVisibility;
|
||||
verticalScrollbarSize: number;
|
||||
verticalSliderSize: number;
|
||||
verticalHasArrows: boolean;
|
||||
}
|
||||
109
src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts
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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { TimeoutTimer, IntervalTimer } from 'vs/base/common/async';
|
||||
|
||||
/**
|
||||
* The arrow image size.
|
||||
*/
|
||||
export const ARROW_IMG_SIZE = 11;
|
||||
|
||||
export interface ScrollbarArrowOptions {
|
||||
onActivate: () => void;
|
||||
className: string;
|
||||
|
||||
bgWidth: number;
|
||||
bgHeight: number;
|
||||
|
||||
top?: number;
|
||||
left?: number;
|
||||
bottom?: number;
|
||||
right?: number;
|
||||
}
|
||||
|
||||
export class ScrollbarArrow extends Widget {
|
||||
|
||||
private _onActivate: () => void;
|
||||
public bgDomNode: HTMLElement;
|
||||
public domNode: HTMLElement;
|
||||
private _mousedownRepeatTimer: IntervalTimer;
|
||||
private _mousedownScheduleRepeatTimer: TimeoutTimer;
|
||||
private _mouseMoveMonitor: GlobalMouseMoveMonitor<IStandardMouseMoveEventData>;
|
||||
|
||||
constructor(opts: ScrollbarArrowOptions) {
|
||||
super();
|
||||
this._onActivate = opts.onActivate;
|
||||
|
||||
this.bgDomNode = document.createElement('div');
|
||||
this.bgDomNode.className = 'arrow-background';
|
||||
this.bgDomNode.style.position = 'absolute';
|
||||
this.bgDomNode.style.width = opts.bgWidth + 'px';
|
||||
this.bgDomNode.style.height = opts.bgHeight + 'px';
|
||||
if (typeof opts.top !== 'undefined') {
|
||||
this.bgDomNode.style.top = '0px';
|
||||
}
|
||||
if (typeof opts.left !== 'undefined') {
|
||||
this.bgDomNode.style.left = '0px';
|
||||
}
|
||||
if (typeof opts.bottom !== 'undefined') {
|
||||
this.bgDomNode.style.bottom = '0px';
|
||||
}
|
||||
if (typeof opts.right !== 'undefined') {
|
||||
this.bgDomNode.style.right = '0px';
|
||||
}
|
||||
|
||||
this.domNode = document.createElement('div');
|
||||
this.domNode.className = opts.className;
|
||||
this.domNode.style.position = 'absolute';
|
||||
this.domNode.style.width = ARROW_IMG_SIZE + 'px';
|
||||
this.domNode.style.height = ARROW_IMG_SIZE + 'px';
|
||||
if (typeof opts.top !== 'undefined') {
|
||||
this.domNode.style.top = opts.top + 'px';
|
||||
}
|
||||
if (typeof opts.left !== 'undefined') {
|
||||
this.domNode.style.left = opts.left + 'px';
|
||||
}
|
||||
if (typeof opts.bottom !== 'undefined') {
|
||||
this.domNode.style.bottom = opts.bottom + 'px';
|
||||
}
|
||||
if (typeof opts.right !== 'undefined') {
|
||||
this.domNode.style.right = opts.right + 'px';
|
||||
}
|
||||
|
||||
this._mouseMoveMonitor = this._register(new GlobalMouseMoveMonitor<IStandardMouseMoveEventData>());
|
||||
this.onmousedown(this.bgDomNode, (e) => this._arrowMouseDown(e));
|
||||
this.onmousedown(this.domNode, (e) => this._arrowMouseDown(e));
|
||||
|
||||
this._mousedownRepeatTimer = this._register(new IntervalTimer());
|
||||
this._mousedownScheduleRepeatTimer = this._register(new TimeoutTimer());
|
||||
}
|
||||
|
||||
private _arrowMouseDown(e: IMouseEvent): void {
|
||||
let scheduleRepeater = () => {
|
||||
this._mousedownRepeatTimer.cancelAndSet(() => this._onActivate(), 1000 / 24);
|
||||
};
|
||||
|
||||
this._onActivate();
|
||||
this._mousedownRepeatTimer.cancel();
|
||||
this._mousedownScheduleRepeatTimer.cancelAndSet(scheduleRepeater, 200);
|
||||
|
||||
this._mouseMoveMonitor.startMonitoring(
|
||||
standardMouseMoveMerger,
|
||||
(mouseMoveData: IStandardMouseMoveEventData) => {
|
||||
/* Intentional empty */
|
||||
},
|
||||
() => {
|
||||
this._mousedownRepeatTimer.cancel();
|
||||
this._mousedownScheduleRepeatTimer.cancel();
|
||||
}
|
||||
);
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
222
src/vs/base/browser/ui/scrollbar/scrollbarState.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* The minimal size of the slider (such that it can still be clickable) -- it is artificially enlarged.
|
||||
*/
|
||||
const MINIMUM_SLIDER_SIZE = 20;
|
||||
|
||||
export class ScrollbarState {
|
||||
|
||||
/**
|
||||
* For the vertical scrollbar: the width.
|
||||
* For the horizontal scrollbar: the height.
|
||||
*/
|
||||
private readonly _scrollbarSize: number;
|
||||
|
||||
/**
|
||||
* For the vertical scrollbar: the height of the pair horizontal scrollbar.
|
||||
* For the horizontal scrollbar: the width of the pair vertical scrollbar.
|
||||
*/
|
||||
private readonly _oppositeScrollbarSize: number;
|
||||
|
||||
/**
|
||||
* For the vertical scrollbar: the height of the scrollbar's arrows.
|
||||
* For the horizontal scrollbar: the width of the scrollbar's arrows.
|
||||
*/
|
||||
private readonly _arrowSize: number;
|
||||
|
||||
// --- variables
|
||||
/**
|
||||
* For the vertical scrollbar: the viewport height.
|
||||
* For the horizontal scrollbar: the viewport width.
|
||||
*/
|
||||
private _visibleSize: number;
|
||||
|
||||
/**
|
||||
* For the vertical scrollbar: the scroll height.
|
||||
* For the horizontal scrollbar: the scroll width.
|
||||
*/
|
||||
private _scrollSize: number;
|
||||
|
||||
/**
|
||||
* For the vertical scrollbar: the scroll top.
|
||||
* For the horizontal scrollbar: the scroll left.
|
||||
*/
|
||||
private _scrollPosition: number;
|
||||
|
||||
// --- computed variables
|
||||
|
||||
/**
|
||||
* `visibleSize` - `oppositeScrollbarSize`
|
||||
*/
|
||||
private _computedAvailableSize: number;
|
||||
/**
|
||||
* (`scrollSize` > 0 && `scrollSize` > `visibleSize`)
|
||||
*/
|
||||
private _computedIsNeeded: boolean;
|
||||
|
||||
private _computedSliderSize: number;
|
||||
private _computedSliderRatio: number;
|
||||
private _computedSliderPosition: number;
|
||||
|
||||
constructor(arrowSize: number, scrollbarSize: number, oppositeScrollbarSize: number) {
|
||||
this._scrollbarSize = Math.round(scrollbarSize);
|
||||
this._oppositeScrollbarSize = Math.round(oppositeScrollbarSize);
|
||||
this._arrowSize = Math.round(arrowSize);
|
||||
|
||||
this._visibleSize = 0;
|
||||
this._scrollSize = 0;
|
||||
this._scrollPosition = 0;
|
||||
|
||||
this._computedAvailableSize = 0;
|
||||
this._computedIsNeeded = false;
|
||||
this._computedSliderSize = 0;
|
||||
this._computedSliderRatio = 0;
|
||||
this._computedSliderPosition = 0;
|
||||
|
||||
this._refreshComputedValues();
|
||||
}
|
||||
|
||||
public clone(): ScrollbarState {
|
||||
let r = new ScrollbarState(this._arrowSize, this._scrollbarSize, this._oppositeScrollbarSize);
|
||||
r.setVisibleSize(this._visibleSize);
|
||||
r.setScrollSize(this._scrollSize);
|
||||
r.setScrollPosition(this._scrollPosition);
|
||||
return r;
|
||||
}
|
||||
|
||||
public setVisibleSize(visibleSize: number): boolean {
|
||||
let iVisibleSize = Math.round(visibleSize);
|
||||
if (this._visibleSize !== iVisibleSize) {
|
||||
this._visibleSize = iVisibleSize;
|
||||
this._refreshComputedValues();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public setScrollSize(scrollSize: number): boolean {
|
||||
let iScrollSize = Math.round(scrollSize);
|
||||
if (this._scrollSize !== iScrollSize) {
|
||||
this._scrollSize = iScrollSize;
|
||||
this._refreshComputedValues();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public setScrollPosition(scrollPosition: number): boolean {
|
||||
let iScrollPosition = Math.round(scrollPosition);
|
||||
if (this._scrollPosition !== iScrollPosition) {
|
||||
this._scrollPosition = iScrollPosition;
|
||||
this._refreshComputedValues();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static _computeValues(oppositeScrollbarSize: number, arrowSize: number, visibleSize: number, scrollSize: number, scrollPosition: number) {
|
||||
const computedAvailableSize = Math.max(0, visibleSize - oppositeScrollbarSize);
|
||||
const computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * arrowSize);
|
||||
const computedIsNeeded = (scrollSize > 0 && scrollSize > visibleSize);
|
||||
|
||||
if (!computedIsNeeded) {
|
||||
// There is no need for a slider
|
||||
return {
|
||||
computedAvailableSize: Math.round(computedAvailableSize),
|
||||
computedIsNeeded: computedIsNeeded,
|
||||
computedSliderSize: Math.round(computedRepresentableSize),
|
||||
computedSliderRatio: 0,
|
||||
computedSliderPosition: 0,
|
||||
};
|
||||
}
|
||||
|
||||
// We must artificially increase the size of the slider if needed, since the slider would be too small to grab with the mouse otherwise
|
||||
const computedSliderSize = Math.round(Math.max(MINIMUM_SLIDER_SIZE, Math.floor(visibleSize * computedRepresentableSize / scrollSize)));
|
||||
|
||||
// The slider can move from 0 to `computedRepresentableSize` - `computedSliderSize`
|
||||
// in the same way `scrollPosition` can move from 0 to `scrollSize` - `visibleSize`.
|
||||
const computedSliderRatio = (computedRepresentableSize - computedSliderSize) / (scrollSize - visibleSize);
|
||||
const computedSliderPosition = (scrollPosition * computedSliderRatio);
|
||||
|
||||
return {
|
||||
computedAvailableSize: Math.round(computedAvailableSize),
|
||||
computedIsNeeded: computedIsNeeded,
|
||||
computedSliderSize: Math.round(computedSliderSize),
|
||||
computedSliderRatio: computedSliderRatio,
|
||||
computedSliderPosition: Math.round(computedSliderPosition),
|
||||
};
|
||||
}
|
||||
|
||||
private _refreshComputedValues(): void {
|
||||
const r = ScrollbarState._computeValues(this._oppositeScrollbarSize, this._arrowSize, this._visibleSize, this._scrollSize, this._scrollPosition);
|
||||
this._computedAvailableSize = r.computedAvailableSize;
|
||||
this._computedIsNeeded = r.computedIsNeeded;
|
||||
this._computedSliderSize = r.computedSliderSize;
|
||||
this._computedSliderRatio = r.computedSliderRatio;
|
||||
this._computedSliderPosition = r.computedSliderPosition;
|
||||
}
|
||||
|
||||
public getArrowSize(): number {
|
||||
return this._arrowSize;
|
||||
}
|
||||
|
||||
public getScrollPosition(): number {
|
||||
return this._scrollPosition;
|
||||
}
|
||||
|
||||
public getRectangleLargeSize(): number {
|
||||
return this._computedAvailableSize;
|
||||
}
|
||||
|
||||
public getRectangleSmallSize(): number {
|
||||
return this._scrollbarSize;
|
||||
}
|
||||
|
||||
public isNeeded(): boolean {
|
||||
return this._computedIsNeeded;
|
||||
}
|
||||
|
||||
public getSliderSize(): number {
|
||||
return this._computedSliderSize;
|
||||
}
|
||||
|
||||
public getSliderPosition(): number {
|
||||
return this._computedSliderPosition;
|
||||
}
|
||||
|
||||
public getSliderCenter(): number {
|
||||
return (this._computedSliderPosition + this._computedSliderSize / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a desired `scrollPosition` such that `offset` ends up in the center of the slider.
|
||||
* `offset` is based on the same coordinate system as the `sliderPosition`.
|
||||
*/
|
||||
public getDesiredScrollPositionFromOffset(offset: number): number {
|
||||
if (!this._computedIsNeeded) {
|
||||
// no need for a slider
|
||||
return 0;
|
||||
}
|
||||
|
||||
let desiredSliderPosition = offset - this._arrowSize - this._computedSliderSize / 2;
|
||||
return Math.round(desiredSliderPosition / this._computedSliderRatio);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a desired `scrollPosition` such that the slider moves by `delta`.
|
||||
*/
|
||||
public getDesiredScrollPositionFromDelta(delta: number): number {
|
||||
if (!this._computedIsNeeded) {
|
||||
// no need for a slider
|
||||
return 0;
|
||||
}
|
||||
|
||||
let desiredSliderPosition = this._computedSliderPosition + delta;
|
||||
return Math.round(desiredSliderPosition / this._computedSliderRatio);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { TimeoutTimer } from 'vs/base/common/async';
|
||||
import { FastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
|
||||
export class ScrollbarVisibilityController extends Disposable {
|
||||
private _visibility: ScrollbarVisibility;
|
||||
private _visibleClassName: string;
|
||||
private _invisibleClassName: string;
|
||||
private _domNode: FastDomNode<HTMLElement>;
|
||||
private _shouldBeVisible: boolean;
|
||||
private _isNeeded: boolean;
|
||||
private _isVisible: boolean;
|
||||
private _revealTimer: TimeoutTimer;
|
||||
|
||||
constructor(visibility: ScrollbarVisibility, visibleClassName: string, invisibleClassName: string) {
|
||||
super();
|
||||
this._visibility = visibility;
|
||||
this._visibleClassName = visibleClassName;
|
||||
this._invisibleClassName = invisibleClassName;
|
||||
this._domNode = null;
|
||||
this._isVisible = false;
|
||||
this._isNeeded = false;
|
||||
this._shouldBeVisible = false;
|
||||
this._revealTimer = this._register(new TimeoutTimer());
|
||||
}
|
||||
|
||||
// ----------------- Hide / Reveal
|
||||
|
||||
private applyVisibilitySetting(shouldBeVisible: boolean): boolean {
|
||||
if (this._visibility === ScrollbarVisibility.Hidden) {
|
||||
return false;
|
||||
}
|
||||
if (this._visibility === ScrollbarVisibility.Visible) {
|
||||
return true;
|
||||
}
|
||||
return shouldBeVisible;
|
||||
}
|
||||
|
||||
public setShouldBeVisible(rawShouldBeVisible: boolean): void {
|
||||
let shouldBeVisible = this.applyVisibilitySetting(rawShouldBeVisible);
|
||||
|
||||
if (this._shouldBeVisible !== shouldBeVisible) {
|
||||
this._shouldBeVisible = shouldBeVisible;
|
||||
this.ensureVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
public setIsNeeded(isNeeded: boolean): void {
|
||||
if (this._isNeeded !== isNeeded) {
|
||||
this._isNeeded = isNeeded;
|
||||
this.ensureVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
public setDomNode(domNode: FastDomNode<HTMLElement>): void {
|
||||
this._domNode = domNode;
|
||||
this._domNode.setClassName(this._invisibleClassName);
|
||||
|
||||
// Now that the flags & the dom node are in a consistent state, ensure the Hidden/Visible configuration
|
||||
this.setShouldBeVisible(false);
|
||||
}
|
||||
|
||||
public ensureVisibility(): void {
|
||||
|
||||
if (!this._isNeeded) {
|
||||
// Nothing to be rendered
|
||||
this._hide(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._shouldBeVisible) {
|
||||
this._reveal();
|
||||
} else {
|
||||
this._hide(true);
|
||||
}
|
||||
}
|
||||
|
||||
private _reveal(): void {
|
||||
if (this._isVisible) {
|
||||
return;
|
||||
}
|
||||
this._isVisible = true;
|
||||
|
||||
// The CSS animation doesn't play otherwise
|
||||
this._revealTimer.setIfNotSet(() => {
|
||||
this._domNode.setClassName(this._visibleClassName);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
private _hide(withFadeAway: boolean): void {
|
||||
this._revealTimer.cancel();
|
||||
if (!this._isVisible) {
|
||||
return;
|
||||
}
|
||||
this._isVisible = false;
|
||||
this._domNode.setClassName(this._invisibleClassName + (withFadeAway ? ' fade' : ''));
|
||||
}
|
||||
}
|
||||
95
src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { AbstractScrollbar, ScrollbarHost, ISimplifiedMouseEvent } from 'vs/base/browser/ui/scrollbar/abstractScrollbar';
|
||||
import { StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
import { Scrollable, ScrollEvent, ScrollbarVisibility, INewScrollPosition } from 'vs/base/common/scrollable';
|
||||
import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState';
|
||||
import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow';
|
||||
|
||||
export class VerticalScrollbar extends AbstractScrollbar {
|
||||
|
||||
constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) {
|
||||
super({
|
||||
lazyRender: options.lazyRender,
|
||||
host: host,
|
||||
scrollbarState: new ScrollbarState(
|
||||
(options.verticalHasArrows ? options.arrowSize : 0),
|
||||
(options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize),
|
||||
// give priority to vertical scroll bar over horizontal and let it scroll all the way to the bottom
|
||||
0
|
||||
),
|
||||
visibility: options.vertical,
|
||||
extraScrollbarClassName: 'vertical',
|
||||
scrollable: scrollable
|
||||
});
|
||||
|
||||
if (options.verticalHasArrows) {
|
||||
let arrowDelta = (options.arrowSize - ARROW_IMG_SIZE) / 2;
|
||||
let scrollbarDelta = (options.verticalScrollbarSize - ARROW_IMG_SIZE) / 2;
|
||||
|
||||
this._createArrow({
|
||||
className: 'up-arrow',
|
||||
top: arrowDelta,
|
||||
left: scrollbarDelta,
|
||||
bottom: void 0,
|
||||
right: void 0,
|
||||
bgWidth: options.verticalScrollbarSize,
|
||||
bgHeight: options.arrowSize,
|
||||
onActivate: () => this._host.onMouseWheel(new StandardMouseWheelEvent(null, 0, 1)),
|
||||
});
|
||||
|
||||
this._createArrow({
|
||||
className: 'down-arrow',
|
||||
top: void 0,
|
||||
left: scrollbarDelta,
|
||||
bottom: arrowDelta,
|
||||
right: void 0,
|
||||
bgWidth: options.verticalScrollbarSize,
|
||||
bgHeight: options.arrowSize,
|
||||
onActivate: () => this._host.onMouseWheel(new StandardMouseWheelEvent(null, 0, -1)),
|
||||
});
|
||||
}
|
||||
|
||||
this._createSlider(0, Math.floor((options.verticalScrollbarSize - options.verticalSliderSize) / 2), options.verticalSliderSize, null);
|
||||
}
|
||||
|
||||
protected _updateSlider(sliderSize: number, sliderPosition: number): void {
|
||||
this.slider.setHeight(sliderSize);
|
||||
this.slider.setTop(sliderPosition);
|
||||
}
|
||||
|
||||
protected _renderDomNode(largeSize: number, smallSize: number): void {
|
||||
this.domNode.setWidth(smallSize);
|
||||
this.domNode.setHeight(largeSize);
|
||||
this.domNode.setRight(0);
|
||||
this.domNode.setTop(0);
|
||||
}
|
||||
|
||||
public onDidScroll(e: ScrollEvent): boolean {
|
||||
this._shouldRender = this._onElementScrollSize(e.scrollHeight) || this._shouldRender;
|
||||
this._shouldRender = this._onElementScrollPosition(e.scrollTop) || this._shouldRender;
|
||||
this._shouldRender = this._onElementSize(e.height) || this._shouldRender;
|
||||
return this._shouldRender;
|
||||
}
|
||||
|
||||
protected _mouseDownRelativePosition(offsetX: number, offsetY: number): number {
|
||||
return offsetY;
|
||||
}
|
||||
|
||||
protected _sliderMousePosition(e: ISimplifiedMouseEvent): number {
|
||||
return e.posy;
|
||||
}
|
||||
|
||||
protected _sliderOrthogonalMousePosition(e: ISimplifiedMouseEvent): number {
|
||||
return e.posx;
|
||||
}
|
||||
|
||||
public writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void {
|
||||
target.scrollTop = scrollPosition;
|
||||
}
|
||||
}
|
||||
9
src/vs/base/browser/ui/selectBox/selectBox.css
Normal file
@@ -0,0 +1,9 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench .select-box {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
}
|
||||
162
src/vs/base/browser/ui/selectBox/selectBox.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./selectBox';
|
||||
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { clone } from 'vs/base/common/objects';
|
||||
|
||||
export interface ISelectBoxStyles {
|
||||
selectBackground?: Color;
|
||||
selectForeground?: Color;
|
||||
selectBorder?: Color;
|
||||
}
|
||||
|
||||
export const defaultStyles = {
|
||||
selectBackground: Color.fromHex('#3C3C3C'),
|
||||
selectForeground: Color.fromHex('#F0F0F0'),
|
||||
selectBorder: Color.fromHex('#3C3C3C')
|
||||
};
|
||||
|
||||
export interface ISelectData {
|
||||
selected: string;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export class SelectBox extends Widget {
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
protected selectElement: HTMLSelectElement;
|
||||
protected options: string[];
|
||||
|
||||
private selected: number;
|
||||
private container: HTMLElement;
|
||||
private _onDidSelect: Emitter<ISelectData>;
|
||||
private toDispose: IDisposable[];
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
protected selectBackground: Color;
|
||||
protected selectForeground: Color;
|
||||
protected selectBorder: Color;
|
||||
|
||||
constructor(options: string[], selected: number, styles: ISelectBoxStyles = clone(defaultStyles)) {
|
||||
super();
|
||||
|
||||
this.selectElement = document.createElement('select');
|
||||
this.selectElement.className = 'select-box';
|
||||
|
||||
this.setOptions(options, selected);
|
||||
this.toDispose = [];
|
||||
this._onDidSelect = new Emitter<ISelectData>();
|
||||
|
||||
this.selectBackground = styles.selectBackground;
|
||||
this.selectForeground = styles.selectForeground;
|
||||
this.selectBorder = styles.selectBorder;
|
||||
|
||||
this.toDispose.push(dom.addStandardDisposableListener(this.selectElement, 'change', (e) => {
|
||||
this.selectElement.title = e.target.value;
|
||||
this._onDidSelect.fire({
|
||||
index: e.target.selectedIndex,
|
||||
selected: e.target.value
|
||||
});
|
||||
}));
|
||||
this.toDispose.push(dom.addStandardDisposableListener(this.selectElement, 'keydown', (e) => {
|
||||
if (e.equals(KeyCode.Space) || e.equals(KeyCode.Enter)) {
|
||||
// Space is used to expand select box, do not propagate it (prevent action bar action run)
|
||||
e.stopPropagation();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public get onDidSelect(): Event<ISelectData> {
|
||||
return this._onDidSelect.event;
|
||||
}
|
||||
|
||||
public setOptions(options: string[], selected?: number, disabled?: number): void {
|
||||
if (!this.options || !arrays.equals(this.options, options)) {
|
||||
this.options = options;
|
||||
|
||||
this.selectElement.options.length = 0;
|
||||
let i = 0;
|
||||
this.options.forEach((option) => {
|
||||
this.selectElement.add(this.createOption(option, disabled === i++));
|
||||
});
|
||||
}
|
||||
this.select(selected);
|
||||
}
|
||||
|
||||
public select(index: number): void {
|
||||
if (index >= 0 && index < this.options.length) {
|
||||
this.selected = index;
|
||||
} else if (this.selected < 0) {
|
||||
this.selected = 0;
|
||||
}
|
||||
|
||||
this.selectElement.selectedIndex = this.selected;
|
||||
this.selectElement.title = this.options[this.selected];
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
if (this.selectElement) {
|
||||
this.selectElement.focus();
|
||||
}
|
||||
}
|
||||
|
||||
public blur(): void {
|
||||
if (this.selectElement) {
|
||||
this.selectElement.blur();
|
||||
}
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
this.container = container;
|
||||
dom.addClass(container, 'select-container');
|
||||
container.appendChild(this.selectElement);
|
||||
this.setOptions(this.options, this.selected);
|
||||
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
public style(styles: ISelectBoxStyles): void {
|
||||
this.selectBackground = styles.selectBackground;
|
||||
this.selectForeground = styles.selectForeground;
|
||||
this.selectBorder = styles.selectBorder;
|
||||
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
protected applyStyles(): void {
|
||||
if (this.selectElement) {
|
||||
const background = this.selectBackground ? this.selectBackground.toString() : null;
|
||||
const foreground = this.selectForeground ? this.selectForeground.toString() : null;
|
||||
const border = this.selectBorder ? this.selectBorder.toString() : null;
|
||||
|
||||
this.selectElement.style.backgroundColor = background;
|
||||
this.selectElement.style.color = foreground;
|
||||
this.selectElement.style.borderColor = border;
|
||||
}
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
protected createOption(value: string, disabled?: boolean): HTMLOptionElement {
|
||||
let option = document.createElement('option');
|
||||
option.value = value;
|
||||
option.text = value;
|
||||
option.disabled = disabled;
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
1
src/vs/base/browser/ui/splitview/arrow-collapse-dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e8e8e8" d="M6 4v8l4-4-4-4zm1 2.414l1.586 1.586-1.586 1.586v-3.172z"/></svg>
|
||||
|
After Width: | Height: | Size: 151 B |
1
src/vs/base/browser/ui/splitview/arrow-collapse.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#646465" d="M6 4v8l4-4-4-4zm1 2.414l1.586 1.586-1.586 1.586v-3.172z"/></svg>
|
||||
|
After Width: | Height: | Size: 151 B |
1
src/vs/base/browser/ui/splitview/arrow-expand-dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e8e8e8" d="M11 10.07h-5.656l5.656-5.656v5.656z"/></svg>
|
||||
|
After Width: | Height: | Size: 131 B |
1
src/vs/base/browser/ui/splitview/arrow-expand.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#646465" d="M11 10.07h-5.656l5.656-5.656v5.656z"/></svg>
|
||||
|
After Width: | Height: | Size: 131 B |
98
src/vs/base/browser/ui/splitview/splitview.css
Normal file
@@ -0,0 +1,98 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-split-view {
|
||||
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;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-split-view > .split-view-view > .header.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 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-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;
|
||||
}
|
||||
1043
src/vs/base/browser/ui/splitview/splitview.ts
Normal file
1
src/vs/base/browser/ui/toolbar/ellipsis-inverse.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#262626"><circle cx="3.5" cy="7.5" r="2.5"/><circle cx="8.5" cy="7.5" r="2.5"/><circle cx="13.5" cy="7.5" r="2.5"/></g><g fill="#C5C5C5"><circle cx="3.5" cy="7.5" r="1.5"/><circle cx="8.5" cy="7.5" r="1.5"/><circle cx="13.5" cy="7.5" r="1.5"/></g></svg>
|
||||
|
After Width: | Height: | Size: 325 B |
1
src/vs/base/browser/ui/toolbar/ellipsis.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#F6F6F6"><circle cx="3.5" cy="7.5" r="2.5"/><circle cx="8.5" cy="7.5" r="2.5"/><circle cx="13.5" cy="7.5" r="2.5"/></g><g fill="#424242"><circle cx="3.5" cy="7.5" r="1.5"/><circle cx="8.5" cy="7.5" r="1.5"/><circle cx="13.5" cy="7.5" r="1.5"/></g></svg>
|
||||
|
After Width: | Height: | Size: 325 B |
22
src/vs/base/browser/ui/toolbar/toolbar.css
Normal file
@@ -0,0 +1,22 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-toolbar .dropdown > .dropdown-label:not(:empty) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.monaco-toolbar .toolbar-toggle-more {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.vs .monaco-toolbar .action-label.toolbar-toggle-more {
|
||||
background-image: url('ellipsis.svg');
|
||||
}
|
||||
|
||||
.hc-black .monaco-toolbar .action-label.toolbar-toggle-more,
|
||||
.vs-dark .monaco-toolbar .action-label.toolbar-toggle-more {
|
||||
background-image: url('ellipsis-inverse.svg');
|
||||
}
|
||||
266
src/vs/base/browser/ui/toolbar/toolbar.ts
Normal file
@@ -0,0 +1,266 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./toolbar';
|
||||
import nls = require('vs/nls');
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import types = require('vs/base/common/types');
|
||||
import { Action, IActionRunner, IAction } from 'vs/base/common/actions';
|
||||
import { ActionBar, ActionsOrientation, IActionItemProvider, BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IContextMenuProvider, DropdownMenu, IActionProvider, ILabelRenderer, IDropdownMenuOptions } from 'vs/base/browser/ui/dropdown/dropdown';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
|
||||
export const CONTEXT = 'context.toolbar';
|
||||
|
||||
export interface IToolBarOptions {
|
||||
orientation?: ActionsOrientation;
|
||||
actionItemProvider?: IActionItemProvider;
|
||||
ariaLabel?: string;
|
||||
getKeyBinding?: (action: IAction) => ResolvedKeybinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* A widget that combines an action bar for primary actions and a dropdown for secondary actions.
|
||||
*/
|
||||
export class ToolBar {
|
||||
private options: IToolBarOptions;
|
||||
private actionBar: ActionBar;
|
||||
private toggleMenuAction: ToggleMenuAction;
|
||||
private toggleMenuActionItem: DropdownMenuActionItem;
|
||||
private hasSecondaryActions: boolean;
|
||||
private lookupKeybindings: boolean;
|
||||
|
||||
constructor(container: HTMLElement, contextMenuProvider: IContextMenuProvider, options: IToolBarOptions = { orientation: ActionsOrientation.HORIZONTAL }) {
|
||||
this.options = options;
|
||||
this.lookupKeybindings = typeof this.options.getKeyBinding === 'function';
|
||||
|
||||
this.toggleMenuAction = new ToggleMenuAction(() => this.toggleMenuActionItem && this.toggleMenuActionItem.show());
|
||||
|
||||
let element = document.createElement('div');
|
||||
element.className = 'monaco-toolbar';
|
||||
container.appendChild(element);
|
||||
|
||||
this.actionBar = new ActionBar($(element), {
|
||||
orientation: options.orientation,
|
||||
ariaLabel: options.ariaLabel,
|
||||
actionItemProvider: (action: Action) => {
|
||||
|
||||
// Return special action item for the toggle menu action
|
||||
if (action.id === ToggleMenuAction.ID) {
|
||||
|
||||
// Dispose old
|
||||
if (this.toggleMenuActionItem) {
|
||||
this.toggleMenuActionItem.dispose();
|
||||
}
|
||||
|
||||
// Create new
|
||||
this.toggleMenuActionItem = new DropdownMenuActionItem(
|
||||
action,
|
||||
(<ToggleMenuAction>action).menuActions,
|
||||
contextMenuProvider,
|
||||
this.options.actionItemProvider,
|
||||
this.actionRunner,
|
||||
this.options.getKeyBinding,
|
||||
'toolbar-toggle-more'
|
||||
);
|
||||
this.toggleMenuActionItem.setActionContext(this.actionBar.context);
|
||||
|
||||
return this.toggleMenuActionItem;
|
||||
}
|
||||
|
||||
return options.actionItemProvider ? options.actionItemProvider(action) : null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public set actionRunner(actionRunner: IActionRunner) {
|
||||
this.actionBar.actionRunner = actionRunner;
|
||||
}
|
||||
|
||||
public get actionRunner(): IActionRunner {
|
||||
return this.actionBar.actionRunner;
|
||||
}
|
||||
|
||||
public set context(context: any) {
|
||||
this.actionBar.context = context;
|
||||
if (this.toggleMenuActionItem) {
|
||||
this.toggleMenuActionItem.setActionContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
public getContainer(): Builder {
|
||||
return this.actionBar.getContainer();
|
||||
}
|
||||
|
||||
public setAriaLabel(label: string): void {
|
||||
this.actionBar.setAriaLabel(label);
|
||||
}
|
||||
|
||||
public setActions(primaryActions: IAction[], secondaryActions?: IAction[]): () => void {
|
||||
return () => {
|
||||
let primaryActionsToSet = primaryActions ? primaryActions.slice(0) : [];
|
||||
|
||||
// Inject additional action to open secondary actions if present
|
||||
this.hasSecondaryActions = secondaryActions && secondaryActions.length > 0;
|
||||
if (this.hasSecondaryActions) {
|
||||
this.toggleMenuAction.menuActions = secondaryActions.slice(0);
|
||||
primaryActionsToSet.push(this.toggleMenuAction);
|
||||
}
|
||||
|
||||
this.actionBar.clear();
|
||||
|
||||
primaryActionsToSet.forEach(action => {
|
||||
this.actionBar.push(action, { icon: true, label: false, keybinding: this.getKeybindingLabel(action) });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
private getKeybindingLabel(action: IAction): string {
|
||||
const key = this.lookupKeybindings ? this.options.getKeyBinding(action) : void 0;
|
||||
|
||||
return key ? key.getLabel() : void 0;
|
||||
}
|
||||
|
||||
public addPrimaryAction(primaryAction: IAction): () => void {
|
||||
return () => {
|
||||
|
||||
// Add after the "..." action if we have secondary actions
|
||||
if (this.hasSecondaryActions) {
|
||||
let itemCount = this.actionBar.length();
|
||||
this.actionBar.push(primaryAction, { icon: true, label: false, index: itemCount, keybinding: this.getKeybindingLabel(primaryAction) });
|
||||
}
|
||||
|
||||
// Otherwise just add to the end
|
||||
else {
|
||||
this.actionBar.push(primaryAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(primaryAction) });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.actionBar.dispose();
|
||||
this.toggleMenuAction.dispose();
|
||||
|
||||
if (this.toggleMenuActionItem) {
|
||||
this.toggleMenuActionItem.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ToggleMenuAction extends Action {
|
||||
|
||||
public static ID = 'toolbar.toggle.more';
|
||||
|
||||
private _menuActions: IAction[];
|
||||
private toggleDropdownMenu: () => void;
|
||||
|
||||
constructor(toggleDropdownMenu: () => void) {
|
||||
super(ToggleMenuAction.ID, nls.localize('more', "More"), null, true);
|
||||
|
||||
this.toggleDropdownMenu = toggleDropdownMenu;
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
this.toggleDropdownMenu();
|
||||
|
||||
return TPromise.as(true);
|
||||
}
|
||||
|
||||
public get menuActions() {
|
||||
return this._menuActions;
|
||||
}
|
||||
|
||||
public set menuActions(actions: IAction[]) {
|
||||
this._menuActions = actions;
|
||||
}
|
||||
}
|
||||
|
||||
export class DropdownMenuActionItem extends BaseActionItem {
|
||||
private menuActionsOrProvider: any;
|
||||
private dropdownMenu: DropdownMenu;
|
||||
private toUnbind: IDisposable;
|
||||
private contextMenuProvider: IContextMenuProvider;
|
||||
private actionItemProvider: IActionItemProvider;
|
||||
private keybindings: (action: IAction) => ResolvedKeybinding;
|
||||
private clazz: string;
|
||||
|
||||
constructor(action: IAction, menuActions: IAction[], contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string);
|
||||
constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string);
|
||||
constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string) {
|
||||
super(null, action);
|
||||
|
||||
this.menuActionsOrProvider = menuActionsOrProvider;
|
||||
this.contextMenuProvider = contextMenuProvider;
|
||||
this.actionItemProvider = actionItemProvider;
|
||||
this.actionRunner = actionRunner;
|
||||
this.keybindings = keybindings;
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
let labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable => {
|
||||
this.builder = $('a.action-label').attr({
|
||||
tabIndex: '0',
|
||||
role: 'button',
|
||||
'aria-haspopup': 'true',
|
||||
title: this._action.label || '',
|
||||
class: this.clazz
|
||||
});
|
||||
|
||||
this.builder.appendTo(el);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
let options: IDropdownMenuOptions = {
|
||||
contextMenuProvider: this.contextMenuProvider,
|
||||
labelRenderer: labelRenderer
|
||||
};
|
||||
|
||||
// Render the DropdownMenu around a simple action to toggle it
|
||||
if (types.isArray(this.menuActionsOrProvider)) {
|
||||
options.actions = this.menuActionsOrProvider;
|
||||
} else {
|
||||
options.actionProvider = this.menuActionsOrProvider;
|
||||
}
|
||||
|
||||
this.dropdownMenu = new DropdownMenu(container, options);
|
||||
|
||||
this.dropdownMenu.menuOptions = {
|
||||
actionItemProvider: this.actionItemProvider,
|
||||
actionRunner: this.actionRunner,
|
||||
getKeyBinding: this.keybindings,
|
||||
context: this._context
|
||||
};
|
||||
|
||||
// Reemit events for running actions
|
||||
this.toUnbind = this.addEmitter(this.dropdownMenu);
|
||||
}
|
||||
|
||||
public setActionContext(newContext: any): void {
|
||||
super.setActionContext(newContext);
|
||||
|
||||
if (this.dropdownMenu) {
|
||||
this.dropdownMenu.menuOptions.context = newContext;
|
||||
}
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
if (this.dropdownMenu) {
|
||||
this.dropdownMenu.show();
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toUnbind.dispose();
|
||||
this.dropdownMenu.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
54
src/vs/base/browser/ui/widget.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { StandardMouseEvent, IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import * as DomUtils from 'vs/base/browser/dom';
|
||||
|
||||
export abstract class Widget extends Disposable {
|
||||
|
||||
protected onclick(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void {
|
||||
this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.CLICK, (e: MouseEvent) => listener(new StandardMouseEvent(e))));
|
||||
}
|
||||
|
||||
protected onmousedown(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void {
|
||||
this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.MOUSE_DOWN, (e: MouseEvent) => listener(new StandardMouseEvent(e))));
|
||||
}
|
||||
|
||||
protected onmouseover(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void {
|
||||
this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.MOUSE_OVER, (e: MouseEvent) => listener(new StandardMouseEvent(e))));
|
||||
}
|
||||
|
||||
protected onnonbubblingmouseout(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void {
|
||||
this._register(DomUtils.addDisposableNonBubblingMouseOutListener(domNode, (e: MouseEvent) => listener(new StandardMouseEvent(e))));
|
||||
}
|
||||
|
||||
protected onkeydown(domNode: HTMLElement, listener: (e: IKeyboardEvent) => void): void {
|
||||
this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.KEY_DOWN, (e: KeyboardEvent) => listener(new StandardKeyboardEvent(e))));
|
||||
}
|
||||
|
||||
protected onkeyup(domNode: HTMLElement, listener: (e: IKeyboardEvent) => void): void {
|
||||
this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.KEY_UP, (e: KeyboardEvent) => listener(new StandardKeyboardEvent(e))));
|
||||
}
|
||||
|
||||
protected oninput(domNode: HTMLElement, listener: (e: Event) => void): void {
|
||||
this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.INPUT, listener));
|
||||
}
|
||||
|
||||
protected onblur(domNode: HTMLElement, listener: (e: Event) => void): void {
|
||||
this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.BLUR, listener));
|
||||
}
|
||||
|
||||
protected onfocus(domNode: HTMLElement, listener: (e: Event) => void): void {
|
||||
this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.FOCUS, listener));
|
||||
}
|
||||
|
||||
protected onchange(domNode: HTMLElement, listener: (e: Event) => void): void {
|
||||
this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.CHANGE, listener));
|
||||
}
|
||||
}
|
||||