mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-09 01:32:34 -05:00
Merge from vscode 79a1f5a5ca0c6c53db617aa1fa5a2396d2caebe2
This commit is contained in:
@@ -34,4 +34,5 @@ export interface IContextMenuDelegate {
|
||||
actionRunner?: IActionRunner;
|
||||
autoSelectFirstItem?: boolean;
|
||||
anchorAlignment?: AnchorAlignment;
|
||||
anchorAsContainer?: boolean;
|
||||
}
|
||||
|
||||
@@ -16,16 +16,22 @@ import { escape } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { renderCodicons, markdownEscapeEscapedCodicons } from 'vs/base/common/codicons';
|
||||
import { resolvePath } from 'vs/base/common/resources';
|
||||
|
||||
export interface MarkedOptions extends marked.MarkedOptions {
|
||||
baseUrl?: never;
|
||||
}
|
||||
|
||||
export interface MarkdownRenderOptions extends FormattedTextRenderOptions {
|
||||
codeBlockRenderer?: (modeId: string, value: string) => Promise<string>;
|
||||
codeBlockRenderCallback?: () => void;
|
||||
baseUrl?: URI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create html nodes for the given content element.
|
||||
*/
|
||||
export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRenderOptions = {}, markedOptions: marked.MarkedOptions = {}): HTMLElement {
|
||||
export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRenderOptions = {}, markedOptions: MarkedOptions = {}): HTMLElement {
|
||||
const element = createElement(options);
|
||||
|
||||
const _uriMassage = function (part: string): string {
|
||||
@@ -82,6 +88,9 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
|
||||
if (href) {
|
||||
({ href, dimensions } = parseHrefAndDimensions(href));
|
||||
href = _href(href, true);
|
||||
if (options.baseUrl) {
|
||||
href = resolvePath(options.baseUrl, href).toString();
|
||||
}
|
||||
attributes.push(`src="${href}"`);
|
||||
}
|
||||
if (text) {
|
||||
@@ -101,6 +110,12 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
|
||||
text = removeMarkdownEscapes(text);
|
||||
}
|
||||
href = _href(href, false);
|
||||
if (options.baseUrl) {
|
||||
const hasScheme = /^\w[\w\d+.-]*:/.test(href);
|
||||
if (!hasScheme) {
|
||||
href = resolvePath(options.baseUrl, href).toString();
|
||||
}
|
||||
}
|
||||
title = removeMarkdownEscapes(title);
|
||||
href = removeMarkdownEscapes(href);
|
||||
if (
|
||||
@@ -187,6 +202,13 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
|
||||
}));
|
||||
}
|
||||
|
||||
// Use our own sanitizer so that we can let through only spans.
|
||||
// Otherwise, we'd be letting all html be rendered.
|
||||
// If we want to allow markdown permitted tags, then we can delete sanitizer and sanitize.
|
||||
markedOptions.sanitizer = (html: string): string => {
|
||||
const match = markdown.isTrusted ? html.match(/^(<span[^<]+>)|(<\/\s*span>)$/) : undefined;
|
||||
return match ? html : '';
|
||||
};
|
||||
markedOptions.sanitize = true;
|
||||
markedOptions.renderer = renderer;
|
||||
|
||||
@@ -202,18 +224,32 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
|
||||
markedOptions
|
||||
);
|
||||
|
||||
function filter(token: { tag: string, attrs: { readonly [key: string]: string } }): boolean {
|
||||
if (token.tag === 'span' && markdown.isTrusted) {
|
||||
if (token.attrs['style'] && Object.keys(token.attrs).length === 1) {
|
||||
return !!token.attrs['style'].match(/^(color\:#[0-9a-fA-F]+;)?(background-color\:#[0-9a-fA-F]+;)?$/);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
element.innerHTML = insane(renderedMarkdown, {
|
||||
allowedSchemes,
|
||||
// allowedTags should included everything that markdown renders to.
|
||||
// Since we have our own sanitize function for marked, it's possible we missed some tag so let insane make sure.
|
||||
// HTML tags that can result from markdown are from reading https://spec.commonmark.org/0.29/
|
||||
allowedTags: ['ul', 'li', 'p', 'code', 'blockquote', 'ol', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'em', 'pre', 'table', 'tr', 'td', 'div', 'del', 'a', 'strong', 'br', 'img', 'span'],
|
||||
allowedAttributes: {
|
||||
'a': ['href', 'name', 'target', 'data-href'],
|
||||
'iframe': ['allowfullscreen', 'frameborder', 'src'],
|
||||
'img': ['src', 'title', 'alt', 'width', 'height'],
|
||||
'div': ['class', 'data-code'],
|
||||
'span': ['class'],
|
||||
'span': ['class', 'style'],
|
||||
// https://github.com/microsoft/vscode/issues/95937
|
||||
'th': ['align'],
|
||||
'td': ['align']
|
||||
}
|
||||
},
|
||||
filter
|
||||
});
|
||||
|
||||
signalInnerHTML!();
|
||||
|
||||
@@ -31,6 +31,7 @@ export interface IActionViewItem extends IDisposable {
|
||||
export interface IBaseActionViewItemOptions {
|
||||
draggable?: boolean;
|
||||
isMenu?: boolean;
|
||||
useEventAsContext?: boolean;
|
||||
}
|
||||
|
||||
export class BaseActionViewItem extends Disposable implements IActionViewItem {
|
||||
@@ -178,7 +179,7 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
|
||||
onClick(event: DOM.EventLike): void {
|
||||
DOM.EventHelper.stop(event, true);
|
||||
|
||||
const context = types.isUndefinedOrNull(this._context) ? undefined : this._context;
|
||||
const context = types.isUndefinedOrNull(this._context) ? this.options?.useEventAsContext ? event : undefined : this._context;
|
||||
this.actionRunner.run(this._action, context);
|
||||
}
|
||||
|
||||
@@ -404,6 +405,7 @@ export interface IActionBarOptions {
|
||||
ariaLabel?: string;
|
||||
animated?: boolean;
|
||||
triggerKeys?: ActionTrigger;
|
||||
allowContextMenu?: boolean;
|
||||
}
|
||||
|
||||
const defaultOptions: IActionBarOptions = {
|
||||
@@ -633,9 +635,11 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
actionViewItemElement.setAttribute('role', 'presentation');
|
||||
|
||||
// Prevent native context menu on actions
|
||||
this._register(DOM.addDisposableListener(actionViewItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
}));
|
||||
if (!this.options.allowContextMenu) {
|
||||
this._register(DOM.addDisposableListener(actionViewItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
}));
|
||||
}
|
||||
|
||||
let item: IActionViewItem | undefined;
|
||||
|
||||
|
||||
Binary file not shown.
@@ -7,3 +7,12 @@
|
||||
position: absolute;
|
||||
z-index: 2500;
|
||||
}
|
||||
|
||||
.context-view.fixed {
|
||||
all: initial;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
position: fixed;
|
||||
z-index: 2500;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export interface IDelegate {
|
||||
}
|
||||
|
||||
export interface IContextViewProvider {
|
||||
showContextView(delegate: IDelegate): void;
|
||||
showContextView(delegate: IDelegate, container?: HTMLElement): void;
|
||||
hideContextView(): void;
|
||||
layout(): void;
|
||||
}
|
||||
@@ -104,23 +104,25 @@ export class ContextView extends Disposable {
|
||||
|
||||
private container: HTMLElement | null = null;
|
||||
private view: HTMLElement;
|
||||
private useFixedPosition: boolean;
|
||||
private delegate: IDelegate | null = null;
|
||||
private toDisposeOnClean: IDisposable = Disposable.None;
|
||||
private toDisposeOnSetContainer: IDisposable = Disposable.None;
|
||||
|
||||
constructor(container: HTMLElement) {
|
||||
constructor(container: HTMLElement, useFixedPosition: boolean) {
|
||||
super();
|
||||
|
||||
this.view = DOM.$('.context-view');
|
||||
this.useFixedPosition = false;
|
||||
|
||||
DOM.hide(this.view);
|
||||
|
||||
this.setContainer(container);
|
||||
this.setContainer(container, useFixedPosition);
|
||||
|
||||
this._register(toDisposable(() => this.setContainer(null)));
|
||||
this._register(toDisposable(() => this.setContainer(null, false)));
|
||||
}
|
||||
|
||||
setContainer(container: HTMLElement | null): void {
|
||||
setContainer(container: HTMLElement | null, useFixedPosition: boolean): void {
|
||||
if (this.container) {
|
||||
this.toDisposeOnSetContainer.dispose();
|
||||
this.container.removeChild(this.view);
|
||||
@@ -146,6 +148,8 @@ export class ContextView extends Disposable {
|
||||
|
||||
this.toDisposeOnSetContainer = toDisposeOnSetContainer;
|
||||
}
|
||||
|
||||
this.useFixedPosition = useFixedPosition;
|
||||
}
|
||||
|
||||
show(delegate: IDelegate): void {
|
||||
@@ -254,10 +258,11 @@ export class ContextView extends Disposable {
|
||||
DOM.removeClasses(this.view, 'top', 'bottom', 'left', 'right');
|
||||
DOM.addClass(this.view, anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top');
|
||||
DOM.addClass(this.view, anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right');
|
||||
DOM.toggleClass(this.view, 'fixed', this.useFixedPosition);
|
||||
|
||||
const containerPosition = DOM.getDomNodePagePosition(this.container!);
|
||||
this.view.style.top = `${top - containerPosition.top}px`;
|
||||
this.view.style.left = `${left - containerPosition.left}px`;
|
||||
this.view.style.top = `${top - (this.useFixedPosition ? DOM.getDomNodePagePosition(this.view).top : containerPosition.top)}px`;
|
||||
this.view.style.left = `${left - (this.useFixedPosition ? DOM.getDomNodePagePosition(this.view).left : containerPosition.left)}px`;
|
||||
this.view.style.width = 'initial';
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { EventHelper, EventType, removeClass, addClass, append, $, addDisposableListener, addClasses } from 'vs/base/browser/dom';
|
||||
import { IContextMenuDelegate } from 'vs/base/browser/contextmenu';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
export interface ILabelRenderer {
|
||||
(container: HTMLElement): IDisposable | null;
|
||||
@@ -29,7 +30,10 @@ export class BaseDropdown extends ActionRunner {
|
||||
private boxContainer?: HTMLElement;
|
||||
private _label?: HTMLElement;
|
||||
private contents?: HTMLElement;
|
||||
|
||||
private visible: boolean | undefined;
|
||||
private _onDidChangeVisibility = new Emitter<boolean>();
|
||||
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
|
||||
|
||||
constructor(container: HTMLElement, options: IBaseDropdownOptions) {
|
||||
super();
|
||||
@@ -48,7 +52,7 @@ export class BaseDropdown extends ActionRunner {
|
||||
}
|
||||
|
||||
for (const event of [EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap]) {
|
||||
this._register(addDisposableListener(this._label, event, e => EventHelper.stop(e, true))); // prevent default click behaviour to trigger
|
||||
this._register(addDisposableListener(this.element, event, e => EventHelper.stop(e, true))); // prevent default click behaviour to trigger
|
||||
}
|
||||
|
||||
for (const event of [EventType.MOUSE_DOWN, GestureEventType.Tap]) {
|
||||
@@ -101,11 +105,17 @@ export class BaseDropdown extends ActionRunner {
|
||||
}
|
||||
|
||||
show(): void {
|
||||
this.visible = true;
|
||||
if (!this.visible) {
|
||||
this.visible = true;
|
||||
this._onDidChangeVisibility.fire(true);
|
||||
}
|
||||
}
|
||||
|
||||
hide(): void {
|
||||
this.visible = false;
|
||||
if (this.visible) {
|
||||
this.visible = false;
|
||||
this._onDidChangeVisibility.fire(false);
|
||||
}
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
@@ -256,7 +266,8 @@ export class DropdownMenu extends BaseDropdown {
|
||||
getMenuClassName: () => this.menuClassName,
|
||||
onHide: () => this.onHide(),
|
||||
actionRunner: this.menuOptions ? this.menuOptions.actionRunner : undefined,
|
||||
anchorAlignment: this.menuOptions ? this.menuOptions.anchorAlignment : AnchorAlignment.LEFT
|
||||
anchorAlignment: this.menuOptions ? this.menuOptions.anchorAlignment : AnchorAlignment.LEFT,
|
||||
anchorAsContainer: true
|
||||
});
|
||||
}
|
||||
|
||||
@@ -303,6 +314,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
|
||||
this.element.tabIndex = 0;
|
||||
this.element.setAttribute('role', 'button');
|
||||
this.element.setAttribute('aria-haspopup', 'true');
|
||||
this.element.setAttribute('aria-expanded', 'false');
|
||||
this.element.title = this._action.label || '';
|
||||
|
||||
return null;
|
||||
@@ -321,6 +333,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
|
||||
}
|
||||
|
||||
this.dropdownMenu = this._register(new DropdownMenu(container, options));
|
||||
this._register(this.dropdownMenu.onDidChangeVisibility(visible => this.element?.setAttribute('aria-expanded', `${visible}`)));
|
||||
|
||||
this.dropdownMenu.menuOptions = {
|
||||
actionViewItemProvider: this.actionViewItemProvider,
|
||||
|
||||
@@ -380,7 +380,7 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
throw new Error('Invalid index');
|
||||
}
|
||||
|
||||
this.splitview.addView(node, size, index);
|
||||
this.splitview.addView(node, size, index, skipLayout);
|
||||
this._addChild(node, index);
|
||||
this.onDidChildrenChange();
|
||||
}
|
||||
@@ -791,7 +791,7 @@ function flipNode<T extends Node>(node: T, size: number, orthogonalSize: number)
|
||||
newSize += size - totalSize;
|
||||
}
|
||||
|
||||
result.addChild(flipNode(child, orthogonalSize, newSize), newSize, 0);
|
||||
result.addChild(flipNode(child, orthogonalSize, newSize), newSize, 0, true);
|
||||
}
|
||||
|
||||
return result as T;
|
||||
|
||||
130
src/vs/base/browser/ui/hover/hover.css
Normal file
130
src/vs/base/browser/ui/hover/hover.css
Normal file
@@ -0,0 +1,130 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-hover {
|
||||
cursor: default;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
z-index: 50;
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
-ms-user-select: text;
|
||||
box-sizing: initial;
|
||||
animation: fadein 100ms linear;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.monaco-hover.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-hover .hover-contents {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.monaco-hover .markdown-hover > .hover-contents:not(.code-hover-contents) {
|
||||
max-width: 500px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.monaco-hover .markdown-hover > .hover-contents:not(.code-hover-contents) hr {
|
||||
/* This is a strange rule but it avoids https://github.com/microsoft/vscode/issues/96795, just 100vw on its own caused the actual hover width to increase */
|
||||
min-width: calc(100% + 100vw);
|
||||
}
|
||||
|
||||
.monaco-hover p,
|
||||
.monaco-hover ul {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.monaco-hover code {
|
||||
font-family: var(--monaco-monospace-font);
|
||||
}
|
||||
|
||||
.monaco-hover hr {
|
||||
margin-top: 4px;
|
||||
margin-bottom: -6px;
|
||||
margin-left: -10px;
|
||||
margin-right: -10px;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.monaco-hover p:first-child,
|
||||
.monaco-hover ul:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.monaco-hover p:last-child,
|
||||
.monaco-hover ul:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* MarkupContent Layout */
|
||||
.monaco-hover ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.monaco-hover ol {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.monaco-hover li > p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.monaco-hover li > ul {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.monaco-hover code {
|
||||
border-radius: 3px;
|
||||
padding: 0 0.4em;
|
||||
}
|
||||
|
||||
.monaco-hover .monaco-tokenized-source {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.monaco-hover .hover-row.status-bar {
|
||||
font-size: 12px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.monaco-hover .hover-row.status-bar .actions {
|
||||
display: flex;
|
||||
padding: 0px 8px;
|
||||
}
|
||||
|
||||
.monaco-hover .hover-row.status-bar .actions .action-container {
|
||||
margin-right: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-hover .hover-row.status-bar .actions .action-container .action .icon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.monaco-hover .markdown-hover .hover-contents .codicon {
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.monaco-hover .hover-contents a.code-link:before {
|
||||
content: '(';
|
||||
}
|
||||
.monaco-hover .hover-contents a.code-link:after {
|
||||
content: ')';
|
||||
}
|
||||
|
||||
.monaco-hover .hover-contents a.code-link {
|
||||
color: inherit;
|
||||
}
|
||||
.monaco-hover .hover-contents a.code-link > span {
|
||||
text-decoration: underline;
|
||||
/** Hack to force underline to show **/
|
||||
border-bottom: 1px solid transparent;
|
||||
text-underline-position: under;
|
||||
}
|
||||
54
src/vs/base/browser/ui/hover/hoverWidget.ts
Normal file
54
src/vs/base/browser/ui/hover/hoverWidget.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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./hover';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
export class HoverWidget extends Disposable {
|
||||
|
||||
public readonly containerDomNode: HTMLElement;
|
||||
public readonly contentsDomNode: HTMLElement;
|
||||
private readonly _scrollbar: DomScrollableElement;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.containerDomNode = document.createElement('div');
|
||||
this.containerDomNode.className = 'monaco-hover';
|
||||
this.containerDomNode.tabIndex = 0;
|
||||
this.containerDomNode.setAttribute('role', 'tooltip');
|
||||
|
||||
this.contentsDomNode = document.createElement('div');
|
||||
this.contentsDomNode.className = 'monaco-hover-content';
|
||||
|
||||
this._scrollbar = this._register(new DomScrollableElement(this.contentsDomNode, {}));
|
||||
this.containerDomNode.appendChild(this._scrollbar.getDomNode());
|
||||
}
|
||||
|
||||
public onContentsChanged(): void {
|
||||
this._scrollbar.scanDomNode();
|
||||
}
|
||||
}
|
||||
|
||||
export function renderHoverAction(parent: HTMLElement, actionOptions: { label: string, iconClass?: string, run: (target: HTMLElement) => void, commandId: string }, keybindingLabel: string | null): IDisposable {
|
||||
const actionContainer = dom.append(parent, $('div.action-container'));
|
||||
const action = dom.append(actionContainer, $('a.action'));
|
||||
action.setAttribute('href', '#');
|
||||
action.setAttribute('role', 'button');
|
||||
if (actionOptions.iconClass) {
|
||||
dom.append(action, $(`span.icon.${actionOptions.iconClass}`));
|
||||
}
|
||||
const label = dom.append(action, $('span'));
|
||||
label.textContent = keybindingLabel ? `${actionOptions.label} (${keybindingLabel})` : actionOptions.label;
|
||||
return dom.addDisposableListener(actionContainer, dom.EventType.CLICK, e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
actionOptions.run(actionContainer);
|
||||
});
|
||||
}
|
||||
@@ -165,7 +165,7 @@ export class PagedList<T> implements IDisposable {
|
||||
}
|
||||
|
||||
get onDidChangeFocus(): Event<IListEvent<T>> {
|
||||
return Event.map(this.list.onDidChangeFocus, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
|
||||
return Event.map(this.list.onDidChangeFocus, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent }));
|
||||
}
|
||||
|
||||
get onDidOpen(): Event<IListEvent<T>> {
|
||||
@@ -173,11 +173,11 @@ export class PagedList<T> implements IDisposable {
|
||||
}
|
||||
|
||||
get onDidChangeSelection(): Event<IListEvent<T>> {
|
||||
return Event.map(this.list.onDidChangeSelection, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
|
||||
return Event.map(this.list.onDidChangeSelection, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent }));
|
||||
}
|
||||
|
||||
get onPin(): Event<IListEvent<T>> {
|
||||
return Event.map(this.list.onDidPin, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
|
||||
return Event.map(this.list.onDidPin, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent }));
|
||||
}
|
||||
|
||||
get onContextMenu(): Event<IListContextMenuEvent<T>> {
|
||||
|
||||
@@ -9,8 +9,8 @@ import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/brow
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable';
|
||||
import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions, Scrollable } from 'vs/base/common/scrollable';
|
||||
import { RangeMap, shift } from './rangeMap';
|
||||
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListDragEvent, IListDragAndDrop, ListDragOverEffect } from './list';
|
||||
import { RowCache, IRow } from './rowCache';
|
||||
@@ -44,11 +44,16 @@ export interface IListViewDragAndDrop<T> extends IListDragAndDrop<T> {
|
||||
export interface IListViewAccessibilityProvider<T> {
|
||||
getSetSize?(element: T, index: number, listLength: number): number;
|
||||
getPosInSet?(element: T, index: number): number;
|
||||
getRole?(element: T): string;
|
||||
getRole?(element: T): string | undefined;
|
||||
isChecked?(element: T): boolean | undefined;
|
||||
}
|
||||
|
||||
export interface IListViewOptions<T> {
|
||||
export interface IListViewOptionsUpdate {
|
||||
readonly additionalScrollHeight?: number;
|
||||
readonly smoothScrolling?: boolean;
|
||||
}
|
||||
|
||||
export interface IListViewOptions<T> extends IListViewOptionsUpdate {
|
||||
readonly dnd?: IListViewDragAndDrop<T>;
|
||||
readonly useShadows?: boolean;
|
||||
readonly verticalScrollMode?: ScrollbarVisibility;
|
||||
@@ -58,7 +63,6 @@ export interface IListViewOptions<T> {
|
||||
readonly mouseSupport?: boolean;
|
||||
readonly horizontalScrolling?: boolean;
|
||||
readonly accessibilityProvider?: IListViewAccessibilityProvider<T>;
|
||||
readonly additionalScrollHeight?: number;
|
||||
readonly transformOptimization?: boolean;
|
||||
}
|
||||
|
||||
@@ -158,7 +162,7 @@ class ListViewAccessibilityProvider<T> implements Required<IListViewAccessibilit
|
||||
|
||||
readonly getSetSize: (element: any, index: number, listLength: number) => number;
|
||||
readonly getPosInSet: (element: any, index: number) => number;
|
||||
readonly getRole: (element: T) => string;
|
||||
readonly getRole: (element: T) => string | undefined;
|
||||
readonly isChecked: (element: T) => boolean | undefined;
|
||||
|
||||
constructor(accessibilityProvider?: IListViewAccessibilityProvider<T>) {
|
||||
@@ -204,7 +208,8 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
private lastRenderHeight: number;
|
||||
private renderWidth = 0;
|
||||
private rowsContainer: HTMLElement;
|
||||
private scrollableElement: ScrollableElement;
|
||||
private scrollable: Scrollable;
|
||||
private scrollableElement: SmoothScrollableElement;
|
||||
private _scrollHeight: number = 0;
|
||||
private scrollableElementUpdateDisposable: IDisposable | null = null;
|
||||
private scrollableElementWidthDelayer = new Delayer<void>(50);
|
||||
@@ -278,18 +283,20 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
this.rowsContainer = document.createElement('div');
|
||||
this.rowsContainer.className = 'monaco-list-rows';
|
||||
|
||||
if (options.transformOptimization) {
|
||||
const transformOptimization = getOrDefault(options, o => o.transformOptimization, DefaultOptions.transformOptimization);
|
||||
if (transformOptimization) {
|
||||
this.rowsContainer.style.transform = 'translate3d(0px, 0px, 0px)';
|
||||
}
|
||||
|
||||
this.disposables.add(Gesture.addTarget(this.rowsContainer));
|
||||
|
||||
this.scrollableElement = this.disposables.add(new ScrollableElement(this.rowsContainer, {
|
||||
this.scrollable = new Scrollable(getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0, cb => DOM.scheduleAtNextAnimationFrame(cb));
|
||||
this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, {
|
||||
alwaysConsumeMouseWheel: true,
|
||||
horizontal: this.horizontalScrolling ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden,
|
||||
vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode),
|
||||
useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows)
|
||||
}));
|
||||
useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows),
|
||||
}, this.scrollable));
|
||||
|
||||
this.domNode.appendChild(this.scrollableElement.getDomNode());
|
||||
container.appendChild(this.domNode);
|
||||
@@ -319,6 +326,10 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
if (options.additionalScrollHeight !== undefined) {
|
||||
this.additionalScrollHeight = options.additionalScrollHeight;
|
||||
}
|
||||
|
||||
if (options.smoothScrolling !== undefined) {
|
||||
this.scrollable.setSmoothScrollDuration(options.smoothScrolling ? 125 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) {
|
||||
@@ -651,7 +662,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
|
||||
if (!item.row) {
|
||||
item.row = this.cache.alloc(item.templateId);
|
||||
const role = this.accessibilityProvider.getRole(item.element);
|
||||
const role = this.accessibilityProvider.getRole(item.element) || 'listitem';
|
||||
item.row!.domNode!.setAttribute('role', role);
|
||||
const checked = this.accessibilityProvider.isChecked(item.element);
|
||||
if (typeof checked !== 'undefined') {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./list';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { isNumber } from 'vs/base/common/types';
|
||||
import { range, firstIndex, binarySearch } from 'vs/base/common/arrays';
|
||||
@@ -17,7 +16,7 @@ import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardE
|
||||
import { Event, Emitter, EventBufferer } from 'vs/base/common/event';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListError, IKeyboardNavigationDelegate } from './list';
|
||||
import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider } from './listView';
|
||||
import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider, IListViewOptionsUpdate } from './listView';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable';
|
||||
@@ -26,6 +25,7 @@ import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice';
|
||||
import { clamp } from 'vs/base/common/numbers';
|
||||
import { matchesPrefix } from 'vs/base/common/filters';
|
||||
import { IDragAndDropData } from 'vs/base/browser/dnd';
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
|
||||
interface ITraitChangeEvent {
|
||||
indexes: number[];
|
||||
@@ -344,6 +344,7 @@ class TypeLabelController<T> implements IDisposable {
|
||||
|
||||
private automaticKeyboardNavigation = true;
|
||||
private triggered = false;
|
||||
private previouslyFocused = -1;
|
||||
|
||||
private readonly enabledDisposables = new DisposableStore();
|
||||
private readonly disposables = new DisposableStore();
|
||||
@@ -393,6 +394,7 @@ class TypeLabelController<T> implements IDisposable {
|
||||
const onInput = Event.reduce<string | null, string | null>(Event.any(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i));
|
||||
|
||||
onInput(this.onInput, this, this.enabledDisposables);
|
||||
onClear(this.onClear, this, this.enabledDisposables);
|
||||
|
||||
this.enabled = true;
|
||||
this.triggered = false;
|
||||
@@ -408,6 +410,19 @@ class TypeLabelController<T> implements IDisposable {
|
||||
this.triggered = false;
|
||||
}
|
||||
|
||||
private onClear(): void {
|
||||
const focus = this.list.getFocus();
|
||||
if (focus.length > 0 && focus[0] === this.previouslyFocused) {
|
||||
// List: re-anounce element on typing end since typed keys will interupt aria label of focused element
|
||||
// Do not announce if there was a focus change at the end to prevent duplication https://github.com/microsoft/vscode/issues/95961
|
||||
const ariaLabel = this.list.options.accessibilityProvider?.getAriaLabel(this.list.element(focus[0]));
|
||||
if (ariaLabel) {
|
||||
alert(ariaLabel);
|
||||
}
|
||||
}
|
||||
this.previouslyFocused = -1;
|
||||
}
|
||||
|
||||
private onInput(word: string | null): void {
|
||||
if (!word) {
|
||||
this.state = TypeLabelControllerState.Idle;
|
||||
@@ -426,6 +441,7 @@ class TypeLabelController<T> implements IDisposable {
|
||||
const labelStr = label && label.toString();
|
||||
|
||||
if (typeof labelStr === 'undefined' || matchesPrefix(word, labelStr)) {
|
||||
this.previouslyFocused = start;
|
||||
this.list.setFocus([index]);
|
||||
this.list.reveal(index);
|
||||
return;
|
||||
@@ -1091,10 +1107,9 @@ class ListViewDragAndDrop<T> implements IListViewDragAndDrop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
export interface IListOptionsUpdate {
|
||||
export interface IListOptionsUpdate extends IListViewOptionsUpdate {
|
||||
readonly enableKeyboardNavigation?: boolean;
|
||||
readonly automaticKeyboardNavigation?: boolean;
|
||||
readonly additionalScrollHeight?: number;
|
||||
}
|
||||
|
||||
export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
@@ -1272,9 +1287,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
this.typeLabelController.updateOptions(this._options);
|
||||
}
|
||||
|
||||
if (optionsUpdate.additionalScrollHeight !== undefined) {
|
||||
this.view.updateOptions(optionsUpdate);
|
||||
}
|
||||
this.view.updateOptions(optionsUpdate);
|
||||
}
|
||||
|
||||
get options(): IListOptions<T> {
|
||||
@@ -1363,7 +1376,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
|
||||
|
||||
set ariaLabel(value: string) {
|
||||
this._ariaLabel = value;
|
||||
this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", value));
|
||||
this.view.domNode.setAttribute('aria-label', value);
|
||||
}
|
||||
|
||||
domFocus(): void {
|
||||
|
||||
@@ -88,6 +88,9 @@
|
||||
padding: 0.5em 0 0 0;
|
||||
margin-bottom: 0.5em;
|
||||
width: 100%;
|
||||
height: 0px !important;
|
||||
margin-left: .8em !important;
|
||||
margin-right: .8em !important;
|
||||
}
|
||||
|
||||
.monaco-menu .monaco-action-bar.vertical .action-label.separator.text {
|
||||
|
||||
@@ -41,6 +41,7 @@ export interface IMenuOptions {
|
||||
enableMnemonics?: boolean;
|
||||
anchorAlignment?: AnchorAlignment;
|
||||
expandDirection?: Direction;
|
||||
useEventAsContext?: boolean;
|
||||
}
|
||||
|
||||
export interface IMenuStyles {
|
||||
@@ -316,7 +317,7 @@ export class Menu extends ActionBar {
|
||||
|
||||
return menuActionViewItem;
|
||||
} else {
|
||||
const menuItemOptions: IMenuItemOptions = { enableMnemonics: options.enableMnemonics };
|
||||
const menuItemOptions: IMenuItemOptions = { enableMnemonics: options.enableMnemonics, useEventAsContext: options.useEventAsContext };
|
||||
if (options.getKeyBinding) {
|
||||
const keybinding = options.getKeyBinding(action);
|
||||
if (keybinding) {
|
||||
|
||||
@@ -140,8 +140,8 @@ export class MenuBar extends Disposable {
|
||||
eventHandled = false;
|
||||
}
|
||||
|
||||
// Never allow default tab behavior
|
||||
if (event.equals(KeyCode.Tab | KeyMod.Shift) || event.equals(KeyCode.Tab)) {
|
||||
// Never allow default tab behavior when not compact
|
||||
if (this.options.compactMode === undefined && (event.equals(KeyCode.Tab | KeyMod.Shift) || event.equals(KeyCode.Tab))) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
@@ -315,7 +315,7 @@ export class MenuBar extends Disposable {
|
||||
createOverflowMenu(): void {
|
||||
const label = this.options.compactMode !== undefined ? nls.localize('mAppMenu', 'Application Menu') : nls.localize('mMore', 'More');
|
||||
const title = this.options.compactMode !== undefined ? label : undefined;
|
||||
const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': -1, 'aria-label': label, 'title': title, 'aria-haspopup': true });
|
||||
const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': this.options.compactMode !== undefined ? 0 : -1, 'aria-label': label, 'title': title, 'aria-haspopup': true });
|
||||
const titleElement = $('div.menubar-menu-title.toolbar-toggle-more' + menuBarMoreIcon.cssSelector, { 'role': 'none', 'aria-hidden': true });
|
||||
|
||||
buttonElement.appendChild(titleElement);
|
||||
@@ -326,7 +326,7 @@ export class MenuBar extends Disposable {
|
||||
let event = new StandardKeyboardEvent(e as KeyboardEvent);
|
||||
let eventHandled = true;
|
||||
|
||||
if ((event.equals(KeyCode.DownArrow) || event.equals(KeyCode.Enter)) && !this.isOpen) {
|
||||
if ((event.equals(KeyCode.DownArrow) || event.equals(KeyCode.Enter) || (this.options.compactMode !== undefined && event.equals(KeyCode.Space))) && !this.isOpen) {
|
||||
this.focusedMenu = { index: MenuBar.OVERFLOW_INDEX };
|
||||
this.openedViaKeyboard = true;
|
||||
this.focusState = MenubarState.OPEN;
|
||||
@@ -945,7 +945,8 @@ export class MenuBar extends Disposable {
|
||||
actionRunner: this.actionRunner,
|
||||
enableMnemonics: this.options.alwaysOnMnemonics || (this.mnemonicsInUse && this.options.enableMnemonics),
|
||||
ariaLabel: withNullAsUndefined(customMenu.buttonElement.getAttribute('aria-label')),
|
||||
expandDirection: this.options.compactMode !== undefined ? this.options.compactMode : Direction.Right
|
||||
expandDirection: this.options.compactMode !== undefined ? this.options.compactMode : Direction.Right,
|
||||
useEventAsContext: true
|
||||
};
|
||||
|
||||
let menuWidget = this._register(new Menu(menuHolder, customMenu.actions, menuOptions));
|
||||
|
||||
14
src/vs/base/browser/ui/mouseCursor/mouseCursor.css
Normal file
14
src/vs/base/browser/ui/mouseCursor/mouseCursor.css
Normal file
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-mouse-cursor-text {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
/* The following selector looks a bit funny, but that is needed to cover all the workbench and the editor!! */
|
||||
.vs-dark .mac .monaco-mouse-cursor-text, .hc-black .mac .monaco-mouse-cursor-text,
|
||||
.vs-dark.mac .monaco-mouse-cursor-text, .hc-black.mac .monaco-mouse-cursor-text {
|
||||
cursor: -webkit-image-set(url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAL0lEQVQoz2NgCD3x//9/BhBYBWdhgFVAiVW4JBFKGIa4AqD0//9D3pt4I4tAdAMAHTQ/j5Zom30AAAAASUVORK5CYII=') 1x, url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAz0lEQVRIx2NgYGBY/R8I/vx5eelX3n82IJ9FxGf6tksvf/8FiTMQAcAGQMDvSwu09abffY8QYSAScNk45G198eX//yev73/4///701eh//kZSARckrNBRvz//+8+6ZohwCzjGNjdgQxkAg7B9WADeBjIBqtJCbhRA0YNoIkBSNmaPEMoNmA0FkYNoFKhapJ6FGyAH3nauaSmPfwI0v/3OukVi0CIZ+F25KrtYcx/CTIy0e+rC7R1Z4KMICVTQQ14feVXIbR695u14+Ir4gwAAD49E54wc1kWAAAAAElFTkSuQmCC') 2x) 5 8, text;
|
||||
}
|
||||
8
src/vs/base/browser/ui/mouseCursor/mouseCursor.ts
Normal file
8
src/vs/base/browser/ui/mouseCursor/mouseCursor.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./mouseCursor';
|
||||
|
||||
export const MOUSE_CURSOR_TEXT_CSS_CLASS_NAME = `monaco-mouse-cursor-text`;
|
||||
@@ -13,13 +13,6 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.monaco-sash.vertical {
|
||||
cursor: ew-resize;
|
||||
top: 0;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-sash.mac.vertical {
|
||||
cursor: col-resize;
|
||||
}
|
||||
@@ -32,13 +25,6 @@
|
||||
cursor: w-resize;
|
||||
}
|
||||
|
||||
.monaco-sash.horizontal {
|
||||
cursor: ns-resize;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.monaco-sash.mac.horizontal {
|
||||
cursor: row-resize;
|
||||
}
|
||||
@@ -51,52 +37,11 @@
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.monaco-sash:not(.disabled).orthogonal-start::before,
|
||||
.monaco-sash:not(.disabled).orthogonal-end::after {
|
||||
content: ' ';
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
z-index: 100;
|
||||
display: block;
|
||||
cursor: all-scroll;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.monaco-sash.orthogonal-start.vertical::before {
|
||||
left: -2px;
|
||||
top: -4px;
|
||||
}
|
||||
|
||||
.monaco-sash.orthogonal-end.vertical::after {
|
||||
left: -2px;
|
||||
bottom: -4px;
|
||||
}
|
||||
|
||||
.monaco-sash.orthogonal-start.horizontal::before {
|
||||
top: -2px;
|
||||
left: -4px;
|
||||
}
|
||||
|
||||
.monaco-sash.orthogonal-end.horizontal::after {
|
||||
top: -2px;
|
||||
right: -4px;
|
||||
}
|
||||
|
||||
.monaco-sash.disabled {
|
||||
cursor: default !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
/** Touch **/
|
||||
|
||||
.monaco-sash.touch.vertical {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.monaco-sash.touch.horizontal {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/** Debug **/
|
||||
|
||||
.monaco-sash.debug {
|
||||
@@ -110,4 +55,4 @@
|
||||
.monaco-sash.debug:not(.disabled).orthogonal-start::before,
|
||||
.monaco-sash.debug:not(.disabled).orthogonal-end::after {
|
||||
background: red;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import 'vs/css!./sash';
|
||||
import { IDisposable, dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { isIPad } from 'vs/base/browser/browser';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { EventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
|
||||
@@ -39,9 +38,18 @@ export interface ISashEvent {
|
||||
}
|
||||
|
||||
export interface ISashOptions {
|
||||
orientation?: Orientation;
|
||||
orthogonalStartSash?: Sash;
|
||||
orthogonalEndSash?: Sash;
|
||||
readonly orientation: Orientation;
|
||||
readonly orthogonalStartSash?: Sash;
|
||||
readonly orthogonalEndSash?: Sash;
|
||||
readonly size?: number;
|
||||
}
|
||||
|
||||
export interface IVerticalSashOptions extends ISashOptions {
|
||||
readonly orientation: Orientation.VERTICAL;
|
||||
}
|
||||
|
||||
export interface IHorizontalSashOptions extends ISashOptions {
|
||||
readonly orientation: Orientation.HORIZONTAL;
|
||||
}
|
||||
|
||||
export const enum Orientation {
|
||||
@@ -56,12 +64,20 @@ export const enum SashState {
|
||||
Enabled
|
||||
}
|
||||
|
||||
let globalSize = 4;
|
||||
const onDidChangeGlobalSize = new Emitter<number>();
|
||||
export function setGlobalSashSize(size: number): void {
|
||||
globalSize = size;
|
||||
onDidChangeGlobalSize.fire(size);
|
||||
}
|
||||
|
||||
export class Sash extends Disposable {
|
||||
|
||||
private el: HTMLElement;
|
||||
private layoutProvider: ISashLayoutProvider;
|
||||
private hidden: boolean;
|
||||
private orientation!: Orientation;
|
||||
private size: number;
|
||||
|
||||
private _state: SashState = SashState.Enabled;
|
||||
get state(): SashState { return this._state; }
|
||||
@@ -127,7 +143,9 @@ export class Sash extends Disposable {
|
||||
this._orthogonalEndSash = sash;
|
||||
}
|
||||
|
||||
constructor(container: HTMLElement, layoutProvider: ISashLayoutProvider, options: ISashOptions = {}) {
|
||||
constructor(container: HTMLElement, layoutProvider: IVerticalSashLayoutProvider, options: ISashOptions);
|
||||
constructor(container: HTMLElement, layoutProvider: IHorizontalSashLayoutProvider, options: ISashOptions);
|
||||
constructor(container: HTMLElement, layoutProvider: ISashLayoutProvider, options: ISashOptions) {
|
||||
super();
|
||||
|
||||
this.el = append(container, $('.monaco-sash'));
|
||||
@@ -142,12 +160,21 @@ export class Sash extends Disposable {
|
||||
this._register(Gesture.addTarget(this.el));
|
||||
this._register(domEvent(this.el, EventType.Start)(this.onTouchStart, this));
|
||||
|
||||
if (isIPad) {
|
||||
// see also https://ux.stackexchange.com/questions/39023/what-is-the-optimum-button-size-of-touch-screen-applications
|
||||
addClass(this.el, 'touch');
|
||||
}
|
||||
if (typeof options.size === 'number') {
|
||||
this.size = options.size;
|
||||
|
||||
this.setOrientation(options.orientation || Orientation.VERTICAL);
|
||||
if (options.orientation === Orientation.VERTICAL) {
|
||||
this.el.style.width = `${this.size}px`;
|
||||
} else {
|
||||
this.el.style.height = `${this.size}px`;
|
||||
}
|
||||
} else {
|
||||
this.size = globalSize;
|
||||
this._register(onDidChangeGlobalSize.event(size => {
|
||||
this.size = size;
|
||||
this.layout();
|
||||
}));
|
||||
}
|
||||
|
||||
this.hidden = false;
|
||||
this.layoutProvider = layoutProvider;
|
||||
@@ -155,11 +182,7 @@ export class Sash extends Disposable {
|
||||
this.orthogonalStartSash = options.orthogonalStartSash;
|
||||
this.orthogonalEndSash = options.orthogonalEndSash;
|
||||
|
||||
toggleClass(this.el, 'debug', DEBUG);
|
||||
}
|
||||
|
||||
setOrientation(orientation: Orientation): void {
|
||||
this.orientation = orientation;
|
||||
this.orientation = options.orientation || Orientation.VERTICAL;
|
||||
|
||||
if (this.orientation === Orientation.HORIZONTAL) {
|
||||
addClass(this.el, 'horizontal');
|
||||
@@ -169,9 +192,9 @@ export class Sash extends Disposable {
|
||||
addClass(this.el, 'vertical');
|
||||
}
|
||||
|
||||
if (this.layoutProvider) {
|
||||
this.layout();
|
||||
}
|
||||
toggleClass(this.el, 'debug', DEBUG);
|
||||
|
||||
this.layout();
|
||||
}
|
||||
|
||||
private onMouseDown(e: MouseEvent): void {
|
||||
@@ -331,11 +354,9 @@ export class Sash extends Disposable {
|
||||
}
|
||||
|
||||
layout(): void {
|
||||
const size = isIPad ? 20 : 4;
|
||||
|
||||
if (this.orientation === Orientation.VERTICAL) {
|
||||
const verticalProvider = (<IVerticalSashLayoutProvider>this.layoutProvider);
|
||||
this.el.style.left = verticalProvider.getVerticalSashLeft(this) - (size / 2) + 'px';
|
||||
this.el.style.left = verticalProvider.getVerticalSashLeft(this) - (this.size / 2) + 'px';
|
||||
|
||||
if (verticalProvider.getVerticalSashTop) {
|
||||
this.el.style.top = verticalProvider.getVerticalSashTop(this) + 'px';
|
||||
@@ -346,7 +367,7 @@ export class Sash extends Disposable {
|
||||
}
|
||||
} else {
|
||||
const horizontalProvider = (<IHorizontalSashLayoutProvider>this.layoutProvider);
|
||||
this.el.style.top = horizontalProvider.getHorizontalSashTop(this) - (size / 2) + 'px';
|
||||
this.el.style.top = horizontalProvider.getHorizontalSashTop(this) - (this.size / 2) + 'px';
|
||||
|
||||
if (horizontalProvider.getHorizontalSashLeft) {
|
||||
this.el.style.left = horizontalProvider.getHorizontalSashLeft(this) + 'px';
|
||||
@@ -384,15 +405,15 @@ export class Sash extends Disposable {
|
||||
|
||||
private getOrthogonalSash(e: MouseEvent): Sash | undefined {
|
||||
if (this.orientation === Orientation.VERTICAL) {
|
||||
if (e.offsetY <= 4) {
|
||||
if (e.offsetY <= this.size) {
|
||||
return this.orthogonalStartSash;
|
||||
} else if (e.offsetY >= this.el.clientHeight - 4) {
|
||||
} else if (e.offsetY >= this.el.clientHeight - this.size) {
|
||||
return this.orthogonalEndSash;
|
||||
}
|
||||
} else {
|
||||
if (e.offsetX <= 4) {
|
||||
if (e.offsetX <= this.size) {
|
||||
return this.orthogonalStartSash;
|
||||
} else if (e.offsetX >= this.el.clientWidth - 4) {
|
||||
} else if (e.offsetX >= this.el.clientWidth - this.size) {
|
||||
return this.orthogonalEndSash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,6 +533,14 @@ export class SmoothScrollableElement extends AbstractScrollableElement {
|
||||
super(element, options, scrollable);
|
||||
}
|
||||
|
||||
public setScrollPosition(update: INewScrollPosition): void {
|
||||
this._scrollable.setScrollPositionNow(update);
|
||||
}
|
||||
|
||||
public getScrollPosition(): IScrollPosition {
|
||||
return this._scrollable.getCurrentScrollPosition();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class DomScrollableElement extends ScrollableElement {
|
||||
|
||||
@@ -6,3 +6,9 @@
|
||||
.monaco-select-box {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.monaco-select-box-dropdown-container {
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ export interface ISelectBoxOptions {
|
||||
useCustomDrawn?: boolean;
|
||||
ariaLabel?: string;
|
||||
minBottomMargin?: number;
|
||||
optionsAsChildren?: boolean;
|
||||
}
|
||||
|
||||
// Utilize optionItem interface to capture all option parameters
|
||||
|
||||
@@ -90,8 +90,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
|
||||
|
||||
private _isVisible: boolean;
|
||||
private selectBoxOptions: ISelectBoxOptions;
|
||||
|
||||
public selectElement: HTMLSelectElement; // {{SQL CARBON EDIT}}
|
||||
public selectElement: HTMLSelectElement; // {{SQL CARBON EDIT}}
|
||||
private container?: HTMLElement;
|
||||
private options: ISelectOptionItem[] = [];
|
||||
private selected: number;
|
||||
private readonly _onDidSelect: Emitter<ISelectData>;
|
||||
@@ -310,6 +310,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
this.container = container;
|
||||
dom.addClass(container, 'select-container');
|
||||
container.appendChild(this.selectElement);
|
||||
this.applyStyles();
|
||||
@@ -454,7 +455,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
|
||||
dom.toggleClass(this.selectElement, 'synthetic-focus', false);
|
||||
},
|
||||
anchorPosition: this._dropDownPosition
|
||||
});
|
||||
}, this.selectBoxOptions.optionsAsChildren ? this.container : undefined);
|
||||
|
||||
// Hide so we can relay out
|
||||
this._isVisible = true;
|
||||
@@ -469,7 +470,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
|
||||
dom.toggleClass(this.selectElement, 'synthetic-focus', false);
|
||||
},
|
||||
anchorPosition: this._dropDownPosition
|
||||
});
|
||||
}, this.selectBoxOptions.optionsAsChildren ? this.container : undefined);
|
||||
|
||||
// Track initial selection the case user escape, blur
|
||||
this._currentSelection = this.selected;
|
||||
@@ -739,7 +740,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
|
||||
mouseSupport: false,
|
||||
accessibilityProvider: {
|
||||
getAriaLabel: (element) => element.text,
|
||||
getWidgetAriaLabel: () => localize('selectBox', "Select Box"),
|
||||
getWidgetAriaLabel: () => localize({ key: 'selectBox', comment: ['Behave like native select dropdown element.'] }, "Select Box"),
|
||||
getRole: () => 'option',
|
||||
getWidgetRole: () => 'listbox'
|
||||
}
|
||||
|
||||
@@ -16,7 +16,12 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.monaco-pane-view .pane.horizontal:not(.expanded) {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.monaco-pane-view .pane > .pane-header {
|
||||
height: 22px;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
@@ -26,6 +31,12 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header {
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 22px;
|
||||
}
|
||||
|
||||
.monaco-pane-view .pane > .pane-header > .twisties {
|
||||
width: 20px;
|
||||
display: flex;
|
||||
@@ -36,6 +47,11 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header > .twisties {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.monaco-pane-view .pane > .pane-header.expanded > .twisties::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
@@ -132,6 +148,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 22px;
|
||||
min-width: 19px;
|
||||
|
||||
pointer-events: none; /* very important to not take events away from the parent */
|
||||
transition: opacity 150ms ease-out;
|
||||
|
||||
@@ -106,7 +106,7 @@ export abstract class Pane extends Disposable implements IView {
|
||||
get minimumSize(): number {
|
||||
const headerSize = this.headerSize;
|
||||
const expanded = !this.headerVisible || this.isExpanded();
|
||||
const minimumBodySize = expanded ? this.minimumBodySize : this._orientation === Orientation.HORIZONTAL ? 50 : 0;
|
||||
const minimumBodySize = expanded ? this.minimumBodySize : 0;
|
||||
|
||||
return headerSize + minimumBodySize;
|
||||
}
|
||||
@@ -114,7 +114,7 @@ export abstract class Pane extends Disposable implements IView {
|
||||
get maximumSize(): number {
|
||||
const headerSize = this.headerSize;
|
||||
const expanded = !this.headerVisible || this.isExpanded();
|
||||
const maximumBodySize = expanded ? this.maximumBodySize : this._orientation === Orientation.HORIZONTAL ? 50 : 0;
|
||||
const maximumBodySize = expanded ? this.maximumBodySize : 0;
|
||||
|
||||
return headerSize + maximumBodySize;
|
||||
}
|
||||
@@ -126,7 +126,7 @@ export abstract class Pane extends Disposable implements IView {
|
||||
this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded;
|
||||
this._orientation = typeof options.orientation === 'undefined' ? Orientation.VERTICAL : options.orientation;
|
||||
this.ariaHeaderLabel = localize('viewSection', "{0} Section", options.title);
|
||||
this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : 120;
|
||||
this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : this._orientation === Orientation.HORIZONTAL ? 200 : 120;
|
||||
this._maximumBodySize = typeof options.maximumBodySize === 'number' ? options.maximumBodySize : Number.POSITIVE_INFINITY;
|
||||
|
||||
this.element = $('.pane');
|
||||
@@ -141,6 +141,10 @@ export abstract class Pane extends Disposable implements IView {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.element) {
|
||||
toggleClass(this.element, 'expanded', expanded);
|
||||
}
|
||||
|
||||
this._expanded = !!expanded;
|
||||
this.updateHeader();
|
||||
|
||||
@@ -185,12 +189,21 @@ export abstract class Pane extends Disposable implements IView {
|
||||
|
||||
this._orientation = orientation;
|
||||
|
||||
if (this.element) {
|
||||
toggleClass(this.element, 'horizontal', this.orientation === Orientation.HORIZONTAL);
|
||||
toggleClass(this.element, 'vertical', this.orientation === Orientation.VERTICAL);
|
||||
}
|
||||
|
||||
if (this.header) {
|
||||
this.updateHeader();
|
||||
}
|
||||
}
|
||||
|
||||
render(): void {
|
||||
toggleClass(this.element, 'expanded', this.isExpanded());
|
||||
toggleClass(this.element, 'horizontal', this.orientation === Orientation.HORIZONTAL);
|
||||
toggleClass(this.element, 'vertical', this.orientation === Orientation.VERTICAL);
|
||||
|
||||
this.header = $('.pane-header');
|
||||
append(this.element, this.header);
|
||||
this.header.setAttribute('tabindex', '0');
|
||||
@@ -250,8 +263,6 @@ export abstract class Pane extends Disposable implements IView {
|
||||
style(styles: IPaneStyles): void {
|
||||
this.styles = styles;
|
||||
|
||||
this.element.style.borderLeft = this.styles.leftBorder && this.orientation === Orientation.HORIZONTAL ? `1px solid ${this.styles.leftBorder}` : '';
|
||||
|
||||
if (!this.header) {
|
||||
return;
|
||||
}
|
||||
@@ -262,7 +273,6 @@ export abstract class Pane extends Disposable implements IView {
|
||||
protected updateHeader(): void {
|
||||
const expanded = !this.headerVisible || this.isExpanded();
|
||||
|
||||
this.header.style.height = `${this.headerSize}px`;
|
||||
this.header.style.lineHeight = `${this.headerSize}px`;
|
||||
toggleClass(this.header, 'hidden', !this.headerVisible);
|
||||
toggleClass(this.header, 'expanded', expanded);
|
||||
@@ -272,6 +282,7 @@ export abstract class Pane extends Disposable implements IView {
|
||||
this.header.style.backgroundColor = this.styles.headerBackground ? this.styles.headerBackground.toString() : '';
|
||||
this.header.style.borderTop = this.styles.headerBorder && this.orientation === Orientation.VERTICAL ? `1px solid ${this.styles.headerBorder}` : '';
|
||||
this._dropBackground = this.styles.dropBackground;
|
||||
this.element.style.borderLeft = this.styles.leftBorder && this.orientation === Orientation.HORIZONTAL ? `1px solid ${this.styles.leftBorder}` : '';
|
||||
}
|
||||
|
||||
protected abstract renderHeader(container: HTMLElement): void;
|
||||
|
||||
@@ -331,8 +331,8 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
addView(view: IView<TLayoutContext>, size: number | Sizing, index = this.viewItems.length): void {
|
||||
this.doAddView(view, size, index, false);
|
||||
addView(view: IView<TLayoutContext>, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void {
|
||||
this.doAddView(view, size, index, skipLayout);
|
||||
}
|
||||
|
||||
removeView(index: number, sizing?: Sizing): IView<TLayoutContext> {
|
||||
@@ -689,13 +689,17 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
||||
|
||||
// Add sash
|
||||
if (this.viewItems.length > 1) {
|
||||
const orientation = this.orientation === Orientation.VERTICAL ? Orientation.HORIZONTAL : Orientation.VERTICAL;
|
||||
const layoutProvider = this.orientation === Orientation.VERTICAL ? { getHorizontalSashTop: (sash: Sash) => this.getSashPosition(sash) } : { getVerticalSashLeft: (sash: Sash) => this.getSashPosition(sash) };
|
||||
const sash = new Sash(this.sashContainer, layoutProvider, {
|
||||
orientation,
|
||||
orthogonalStartSash: this.orthogonalStartSash,
|
||||
orthogonalEndSash: this.orthogonalEndSash
|
||||
});
|
||||
const sash = this.orientation === Orientation.VERTICAL
|
||||
? new Sash(this.sashContainer, { getHorizontalSashTop: (sash: Sash) => this.getSashPosition(sash) }, {
|
||||
orientation: Orientation.HORIZONTAL,
|
||||
orthogonalStartSash: this.orthogonalStartSash,
|
||||
orthogonalEndSash: this.orthogonalEndSash
|
||||
})
|
||||
: new Sash(this.sashContainer, { getVerticalSashLeft: (sash: Sash) => this.getSashPosition(sash) }, {
|
||||
orientation: Orientation.VERTICAL,
|
||||
orthogonalStartSash: this.orthogonalStartSash,
|
||||
orthogonalEndSash: this.orthogonalEndSash
|
||||
});
|
||||
|
||||
const sashEventMapper = this.orientation === Orientation.VERTICAL
|
||||
? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY, alt: e.altKey })
|
||||
@@ -951,7 +955,7 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
||||
position += this.viewItems[i].size;
|
||||
|
||||
if (this.sashItems[i].sash === sash) {
|
||||
return Math.min(position, this.contentSize - 2);
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -707,7 +707,7 @@ class TypeFilterController<T, TFilterData> implements IDisposable {
|
||||
.map(e => new StandardKeyboardEvent(e))
|
||||
.filter(this.keyboardNavigationEventFilter || (() => true))
|
||||
.filter(() => this.automaticKeyboardNavigation || this.triggered)
|
||||
.filter(e => this.keyboardNavigationDelegate.mightProducePrintableCharacter(e) || ((this.pattern.length > 0 || this.triggered) && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? (e.altKey && !e.metaKey) : e.ctrlKey) && !e.shiftKey)))
|
||||
.filter(e => (this.keyboardNavigationDelegate.mightProducePrintableCharacter(e) && !(e.keyCode === KeyCode.DownArrow || e.keyCode === KeyCode.UpArrow || e.keyCode === KeyCode.LeftArrow || e.keyCode === KeyCode.RightArrow)) || ((this.pattern.length > 0 || this.triggered) && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? (e.altKey && !e.metaKey) : e.ctrlKey) && !e.shiftKey)))
|
||||
.forEach(e => { e.stopPropagation(); e.preventDefault(); })
|
||||
.event;
|
||||
|
||||
@@ -962,6 +962,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions {
|
||||
readonly simpleKeyboardNavigation?: boolean;
|
||||
readonly filterOnType?: boolean;
|
||||
readonly openOnSingleClick?: boolean;
|
||||
readonly smoothScrolling?: boolean;
|
||||
}
|
||||
|
||||
export interface IAbstractTreeOptions<T, TFilterData = void> extends IAbstractTreeOptionsUpdate, IListOptions<T> {
|
||||
@@ -1360,7 +1361,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
|
||||
|
||||
this.view.updateOptions({
|
||||
enableKeyboardNavigation: this._options.simpleKeyboardNavigation,
|
||||
automaticKeyboardNavigation: this._options.automaticKeyboardNavigation
|
||||
automaticKeyboardNavigation: this._options.automaticKeyboardNavigation,
|
||||
smoothScrolling: this._options.smoothScrolling
|
||||
});
|
||||
|
||||
if (this.typeFilterController) {
|
||||
|
||||
@@ -13,7 +13,7 @@ export class CollapseAllAction<TInput, T, TFilterData = void> extends Action {
|
||||
super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'collapse-all', enabled);
|
||||
}
|
||||
|
||||
async run(context?: any): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
this.viewer.collapseAll();
|
||||
this.viewer.setSelection([]);
|
||||
this.viewer.setFocus([]);
|
||||
|
||||
Reference in New Issue
Block a user