Extension host (#20)
* Implement net.Server * Move Socket class into Client This way we don't need to expose anything. * Remove some unused imports * Pass environment variables to bootstrap fork * Add debug log for when socket disconnects from server * Use VSCODE_ALLOW_IO for shared process only * Extension host can send messages now * Support callback for logging This lets us do potentially expensive operations which will only be performed if the log level is sufficiently low. * Stop extension host from committing suicide * Blank line * Add static serve (#21) * Add extension URLs * how did i remove this * Fix writing an empty string * Implement dialogs on window service
This commit is contained in:
@ -1,277 +0,0 @@
|
||||
|
||||
export function classSplice(element: HTMLElement, removeClasses: string, addClasses: string): HTMLElement {
|
||||
if (removeClasses) { removeClasses.split(/\s+/g).forEach((className) => element.classList.remove(className)); }
|
||||
if (addClasses) { addClasses.split(/\s+/g).forEach((className) => element.classList.add(className)); }
|
||||
return element;
|
||||
}
|
||||
|
||||
export type Side = "LEFT" | "RIGHT" | "TOP" | "BOTTOM";
|
||||
export type BoundaryPos = [Side, Side];
|
||||
export interface IBoundary {
|
||||
top: number;
|
||||
left: number;
|
||||
right: number;
|
||||
bottom: number;
|
||||
}
|
||||
|
||||
export type PointPos = ["LEFT" | "CENTER" | "RIGHT", "TOP" | "CENTER" | "BOTTOM"];
|
||||
|
||||
export class FloaterPositioning {
|
||||
private static positionClasses = [
|
||||
"--boundary_top_left",
|
||||
"--boundary_top_right",
|
||||
"--boundary_left_top",
|
||||
"--boundary_right_top",
|
||||
"--boundary_left_bottom",
|
||||
"--boundary_right_bottom",
|
||||
"--boundary_bottom_left",
|
||||
"--boundary_bottom_right",
|
||||
|
||||
"--point_top_left",
|
||||
"--point_top_center",
|
||||
"--point_top_right",
|
||||
"--point_center_left",
|
||||
"--point_center_center",
|
||||
"--point_center_right",
|
||||
"--point_bottom_left",
|
||||
"--point_bottom_center",
|
||||
"--point_bottom_right",
|
||||
].join(" ");
|
||||
|
||||
public readonly target: HTMLElement;
|
||||
constructor(target: HTMLElement) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
// this function was surprisingly difficult
|
||||
public moveToBoundary(boundary: IBoundary, pos: BoundaryPos, keepInBounds: boolean = true) {
|
||||
if (keepInBounds) {
|
||||
const height = this.target.offsetHeight;
|
||||
const width = this.target.offsetWidth;
|
||||
if (height === 0 && width === 0) {
|
||||
throw new Error("target must be added to page before it can be in bounds positioned");
|
||||
}
|
||||
const flip = {
|
||||
BOTTOM: "TOP",
|
||||
LEFT: "RIGHT",
|
||||
RIGHT: "LEFT",
|
||||
TOP: "BOTTOM",
|
||||
} as any;
|
||||
|
||||
const getOverlap = (side: string, strong: boolean) => {
|
||||
switch (side) {
|
||||
case "BOTTOM": return ((strong ? boundary.bottom : boundary.top) + height) - window.innerHeight;
|
||||
case "TOP": return 0 - (strong ? boundary.top : boundary.bottom) - height;
|
||||
case "RIGHT": return ((strong ? boundary.right : boundary.left) + width) - window.innerWidth;
|
||||
case "LEFT": return 0 - (strong ? boundary.left : boundary.right) - width;
|
||||
}
|
||||
};
|
||||
|
||||
const firstA = getOverlap(pos[0], true);
|
||||
if (firstA > 0) {
|
||||
const firstB = getOverlap(flip[pos[0]], true);
|
||||
if (firstB < firstA) {
|
||||
pos[0] = flip[pos[0]];
|
||||
}
|
||||
}
|
||||
|
||||
const secA = getOverlap(pos[1], false);
|
||||
if (secA > 0) {
|
||||
const secB = getOverlap(flip[pos[1]], false);
|
||||
if (secB < secA) {
|
||||
pos[1] = flip[pos[1]];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
classSplice(this.target, FloaterPositioning.positionClasses, undefined);
|
||||
this.target.classList.add(`--boundary_${pos.map((val) => val.toLowerCase()).join("_")}`);
|
||||
|
||||
const displayPos: IBoundary = {} as any;
|
||||
switch (pos[0]) {
|
||||
case "BOTTOM": displayPos.top = boundary.bottom; break;
|
||||
case "TOP": displayPos.bottom = window.innerHeight - boundary.top; break;
|
||||
case "LEFT": displayPos.right = window.innerWidth - boundary.left; break;
|
||||
case "RIGHT": displayPos.left = boundary.right; break;
|
||||
}
|
||||
switch (pos[1]) {
|
||||
case "BOTTOM": displayPos.top = boundary.top; break;
|
||||
case "TOP": displayPos.bottom = window.innerHeight - boundary.bottom; break;
|
||||
case "LEFT": displayPos.right = window.innerWidth - boundary.right; break;
|
||||
case "RIGHT": displayPos.left = boundary.left; break;
|
||||
}
|
||||
this.applyPos(displayPos);
|
||||
}
|
||||
|
||||
public moveToPoint(point: { top: number, left: number }, pos: PointPos, keepInBounds: boolean = true): void {
|
||||
if (keepInBounds) {
|
||||
const height = this.target.offsetHeight;
|
||||
const width = this.target.offsetWidth;
|
||||
if (height === 0 && width === 0) {
|
||||
throw new Error("target must be added to page before it can be in bounds positioned");
|
||||
}
|
||||
const flip = {
|
||||
BOTTOM: "TOP",
|
||||
LEFT: "RIGHT",
|
||||
RIGHT: "LEFT",
|
||||
TOP: "BOTTOM",
|
||||
} as any;
|
||||
|
||||
const getOverlap = (side: string) => {
|
||||
switch (side) {
|
||||
case "BOTTOM": return (point.top + height) - window.innerHeight;
|
||||
case "TOP": return -1 * (point.top - height);
|
||||
case "RIGHT": return (point.left + width) - window.innerWidth;
|
||||
case "LEFT": return -1 * (point.left - width);
|
||||
default: return 0;
|
||||
}
|
||||
};
|
||||
|
||||
const xAlign = pos[0];
|
||||
const normalXOffset = getOverlap(xAlign);
|
||||
if (normalXOffset > 0 && normalXOffset > getOverlap(flip[xAlign])) {
|
||||
pos[0] = flip[xAlign];
|
||||
}
|
||||
|
||||
const yAlign = pos[1];
|
||||
const normalYOffset = getOverlap(yAlign);
|
||||
if (normalYOffset > 0 && normalYOffset > getOverlap(flip[yAlign])) {
|
||||
pos[1] = flip[yAlign];
|
||||
}
|
||||
}
|
||||
|
||||
const displayPos: IBoundary = {} as any;
|
||||
let centerX = false;
|
||||
let centerY = false;
|
||||
switch (pos[0]) {
|
||||
case "CENTER": centerX = true;
|
||||
case "RIGHT": displayPos.left = point.left; break;
|
||||
case "LEFT": displayPos.right = window.innerWidth - point.left; break;
|
||||
}
|
||||
switch (pos[1]) {
|
||||
case "CENTER": centerY = true;
|
||||
case "BOTTOM": displayPos.top = point.top; break;
|
||||
case "TOP": displayPos.bottom = window.innerHeight - point.top; break;
|
||||
}
|
||||
|
||||
classSplice(this.target, FloaterPositioning.positionClasses, undefined);
|
||||
this.target.classList.add(`--point_${pos.map((val) => val.toLowerCase()).reverse().join("_")}`);
|
||||
|
||||
this.applyPos(displayPos);
|
||||
this.target.style.transform = `${centerX ? "translateX(-50)" : ""} ${centerY ? "translateY(-50)" : ""}`;
|
||||
}
|
||||
|
||||
private applyPos(pos: IBoundary) {
|
||||
this.target.style.top = pos.top !== undefined ? (pos.top + "px") : "";
|
||||
this.target.style.bottom = pos.bottom !== undefined ? (pos.bottom + "px") : "";
|
||||
this.target.style.left = pos.left !== undefined ? (pos.left + "px") : "";
|
||||
this.target.style.right = pos.right !== undefined ? (pos.right + "px") : "";
|
||||
}
|
||||
}
|
||||
|
||||
export type Boolable = ((item: HTMLElement) => boolean) | boolean;
|
||||
|
||||
export interface IMakeChildrenSelectableArgs {
|
||||
maxSelectable?: number;
|
||||
selectOnKeyHover?: Boolable;
|
||||
selectOnMouseHover?: Boolable;
|
||||
onHover?: (selectedItem: HTMLElement) => void;
|
||||
onSelect: (selectedItem: HTMLElement, wasAlreadySelected?: boolean) => void;
|
||||
isItemSelectable?: (item: HTMLElement) => boolean;
|
||||
}
|
||||
|
||||
export class SelectableChildren {
|
||||
|
||||
public readonly target: HTMLElement;
|
||||
private keyHoveredItem: HTMLElement;
|
||||
private _selectedItem: HTMLElement;
|
||||
private selectOnMouseHover: Boolable;
|
||||
private onHover: (selectedItem: HTMLElement) => void;
|
||||
private onSelect: (selectedItem: HTMLElement) => void;
|
||||
private isItemSelectable: (item: HTMLElement) => boolean;
|
||||
|
||||
constructor(target: HTMLElement, args: IMakeChildrenSelectableArgs) {
|
||||
this.target = target;
|
||||
|
||||
this.onHover = args.onHover;
|
||||
this.onSelect = args.onSelect;
|
||||
this.selectOnMouseHover = args.selectOnMouseHover || false;
|
||||
this.isItemSelectable = args.isItemSelectable;
|
||||
|
||||
// this.target.addEventListener("keydown", (event) => this.onTargetKeydown(event));
|
||||
this.target.addEventListener("mousemove", (event) => this.onTargetMousemove(event));
|
||||
|
||||
Array.from(this.target.children).forEach((child: HTMLElement) => this.registerChild(child));
|
||||
}
|
||||
|
||||
public registerChild(child: HTMLElement) {
|
||||
child.addEventListener("mouseover", (event) => this.onItemHover(child, event));
|
||||
child.addEventListener("mousedown", (event) => this.onItemMousedown(child, event));
|
||||
}
|
||||
|
||||
public get selectedItem() { return this._selectedItem; }
|
||||
|
||||
public unsetSelection() {
|
||||
if (this.selectedItem) { this.selectedItem.classList.remove("--is_selected"); }
|
||||
this._selectedItem = undefined;
|
||||
}
|
||||
|
||||
public trySelectItem(item: HTMLElement): boolean {
|
||||
if (this.checkItemSelectable(item) === false) { return false; }
|
||||
const alreadySelected = item === this.selectedItem;
|
||||
if (!alreadySelected) {
|
||||
this.unsetSelection();
|
||||
this._selectedItem = item;
|
||||
this.selectedItem.classList.add("--is_selected");
|
||||
this.onSelect(this.selectedItem);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public updateAllItemIsSelectableStates() {
|
||||
this.updateItemIsSelectableState(Array.from(this.target.childNodes) as any);
|
||||
}
|
||||
|
||||
public updateItemIsSelectableState(itemOrItems?: HTMLElement | HTMLElement[]) {
|
||||
const items: HTMLElement[] = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
|
||||
|
||||
items.forEach((item) => {
|
||||
if (!this.isItemSelectable || this.isItemSelectable(item)) {
|
||||
item.classList.remove("--not_selectable");
|
||||
} else {
|
||||
item.classList.add("--not_selectable");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private checkItemSelectable(item: HTMLElement): boolean {
|
||||
this.updateItemIsSelectableState(item);
|
||||
return item.classList.contains("--not_selectable") === false;
|
||||
}
|
||||
|
||||
private onTargetMousemove(event: MouseEvent) {
|
||||
classSplice(this.target, "--key_naving", "--mouse_naving");
|
||||
if (this.keyHoveredItem) {
|
||||
this.keyHoveredItem.classList.remove("--key_hovered");
|
||||
this.keyHoveredItem = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private onItemHover(item: HTMLElement, event: Event) {
|
||||
if (this.onHover) { this.onHover(item); }
|
||||
if (
|
||||
this.checkItemSelectable(item)
|
||||
&& typeof this.selectOnMouseHover === "boolean"
|
||||
? this.selectOnMouseHover
|
||||
: (this.selectOnMouseHover as any)(item)
|
||||
) {
|
||||
this.trySelectItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
private onItemMousedown(item: HTMLElement, event: Event) {
|
||||
this.trySelectItem(item);
|
||||
}
|
||||
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
.context-menu-overlay {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.command-menu {
|
||||
position: fixed;
|
||||
background-color: var(--floater, rgba(67, 67, 61, 1));
|
||||
border: 2px solid rgba(66, 66, 60, 1);
|
||||
color: var(--fg, rgb(216, 216, 216));
|
||||
font-size: 14px;
|
||||
box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.1);
|
||||
/* border-radius: 4px; */
|
||||
overflow: hidden;
|
||||
}
|
||||
.command-menu.--boundary_bottom_right, .command-menu.--boundary_right_bottom, .command-menu.--point_bottom_right {
|
||||
border-top-left-radius: 0px;
|
||||
}
|
||||
.command-menu.--boundary_bottom_left, .command-menu.--boundary_left_bottom, .command-menu.--point_bottom_left {
|
||||
border-top-right-radius: 0px;
|
||||
}
|
||||
.command-menu.--boundary_top_right, .command-menu.--boundary_right_top, .command-menu.--point_top_right {
|
||||
border-bottom-left-radius: 0px;
|
||||
}
|
||||
.command-menu.--boundary_top_left, .command-menu.--boundary_left_top, .command-menu.--point_top_left {
|
||||
border-bottom-right-radius: 0px;
|
||||
}
|
||||
.command-menu .menuitem {
|
||||
white-space: nowrap;
|
||||
padding: 5px 20px;
|
||||
cursor: pointer;
|
||||
min-width: 150px;
|
||||
}
|
||||
.command-menu .menuitem:not(.--not_selectable):not(.--is_selected):hover {
|
||||
background: var(--floaterHover, rgba(0, 0, 0, 0.2));
|
||||
}
|
||||
.command-menu .menuitem.--is_selected {
|
||||
background: var(--floaterActive, rgba(0, 0, 0, 0.25));
|
||||
}
|
||||
.command-menu .menuitem:not(.--non_selection_item).--not_selectable {
|
||||
color: var(--fgFade7, rgba(255, 255, 255, 0.3));
|
||||
cursor: unset;
|
||||
}
|
||||
.command-menu .menuitem.entry {
|
||||
display: flex;
|
||||
}
|
||||
.command-menu .menuitem.spacer {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
.command-menu .menuitem.spacer > hr {
|
||||
margin: 0px;
|
||||
border: none;
|
||||
background: var(--fgFade7, rgba(47, 47, 41, 1));
|
||||
opacity: 0.4;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.command-menu .menuitem.entry > .keybind {
|
||||
margin-left: auto;
|
||||
padding-left: 50px;
|
||||
font-size: 12px;
|
||||
opacity: 0.6;
|
||||
}
|
@ -1,250 +0,0 @@
|
||||
/**
|
||||
* SHOULD BE MOVED. THIS IS NOT A UI SECTION
|
||||
*/
|
||||
|
||||
import * as augment from './augment';
|
||||
import "./contextmenu.css";
|
||||
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
|
||||
export enum MenuItemType {
|
||||
COMMAND,
|
||||
SUB_MENU,
|
||||
SPACER,
|
||||
CUSTOM_ITEM,
|
||||
GENERATIVE_SUBMENU,
|
||||
}
|
||||
|
||||
export interface IMenuItem {
|
||||
type: MenuItemType;
|
||||
domNode: HTMLElement;
|
||||
priority: number;
|
||||
selectOnHover: boolean;
|
||||
refreshDomNode?: () => void;
|
||||
isSelectable?: (() => boolean) | boolean;
|
||||
onSelect?: () => void;
|
||||
}
|
||||
|
||||
export class ContextMenuManager {
|
||||
|
||||
private readonly domNode: FastDomNode<HTMLDivElement>;
|
||||
|
||||
public constructor() {
|
||||
this.domNode = createFastDomNode(document.createElement("div"));
|
||||
this.domNode.setClassName("context-menu-overlay");
|
||||
// this.display = false;
|
||||
this.domNode.domNode.addEventListener("mousedown", (event) => {
|
||||
event.preventDefault();
|
||||
if (event.target === this.domNode.domNode) {
|
||||
this.display = false;
|
||||
}
|
||||
});
|
||||
this.domNode.domNode.addEventListener("closeAllContextMenus", (event) => {
|
||||
this.display = false;
|
||||
event.stopPropagation();
|
||||
});
|
||||
this.domNode.domNode.addEventListener("contextMenuActive", (event) => {
|
||||
// this.clearStackTill(event.target as HTMLElement);
|
||||
event.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
public onceClose(cb: () => void): void {
|
||||
const l = () => {
|
||||
cb();
|
||||
this.domNode.domNode.removeEventListener("closed", l);
|
||||
};
|
||||
this.domNode.domNode.addEventListener("closed", l);
|
||||
}
|
||||
|
||||
public set display(value: boolean) {
|
||||
if (value) {
|
||||
document.body.appendChild(this.domNode.domNode);
|
||||
} else {
|
||||
this.domNode.domNode.remove();
|
||||
this.domNode.domNode.dispatchEvent(new Event("closed"));
|
||||
}
|
||||
}
|
||||
|
||||
public displayMenuAtBoundary<T>(
|
||||
menu: ContextMenu,
|
||||
boundary: augment.IBoundary,
|
||||
positioning: augment.BoundaryPos = ["BOTTOM", "RIGHT"],
|
||||
clearStack: boolean = true,
|
||||
): void {
|
||||
this.displayMenu(menu, clearStack);
|
||||
menu.positioningAugment.moveToBoundary(boundary, positioning);
|
||||
}
|
||||
|
||||
public displayMenuAtPoint<T>(
|
||||
menu: ContextMenu,
|
||||
point: { top: number, left: number },
|
||||
positioning: augment.PointPos = ["RIGHT", "BOTTOM"],
|
||||
clearStack: boolean = true,
|
||||
): void {
|
||||
this.displayMenu(menu, clearStack);
|
||||
menu.positioningAugment.moveToPoint(point, positioning);
|
||||
}
|
||||
|
||||
private displayMenu(menu: ContextMenu, clearStack: boolean) {
|
||||
while (this.domNode.domNode.lastChild) {
|
||||
this.domNode.domNode.removeChild(this.domNode.domNode.lastChild);
|
||||
}
|
||||
this.domNode.appendChild(menu.domNode);
|
||||
this.display = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class ContextMenu {
|
||||
|
||||
public readonly id: string;
|
||||
public readonly positioningAugment: augment.FloaterPositioning;
|
||||
public readonly selectionAugment: augment.SelectableChildren;
|
||||
public readonly domNode: FastDomNode<HTMLDivElement>;
|
||||
private readonly manager: ContextMenuManager;
|
||||
|
||||
private cachedActive: HTMLElement;
|
||||
private domNodeToItemMap: Map<HTMLElement, IMenuItem>;
|
||||
private items: IMenuItem[];
|
||||
|
||||
constructor(id: string, manager: ContextMenuManager) {
|
||||
this.id = id;
|
||||
this.manager = manager;
|
||||
this.items = [];
|
||||
this.domNodeToItemMap = new Map();
|
||||
this.domNode = createFastDomNode(document.createElement("div"));
|
||||
this.domNode.setClassName("command-menu");
|
||||
this.positioningAugment = new augment.FloaterPositioning(this.domNode.domNode);
|
||||
|
||||
const selectOnHover = (itemDomNode: HTMLElement) => this.domNodeToItemMap.get(itemDomNode).selectOnHover;
|
||||
this.selectionAugment = new augment.SelectableChildren(this.domNode.domNode, {
|
||||
isItemSelectable: (itemDomNode) => {
|
||||
const item = this.domNodeToItemMap.get(itemDomNode);
|
||||
return typeof item.isSelectable === "boolean" ? item.isSelectable : item.isSelectable();
|
||||
},
|
||||
onHover: (itemDomNode) => {
|
||||
const item = this.domNodeToItemMap.get(itemDomNode);
|
||||
if (item.type !== MenuItemType.SUB_MENU && item.type !== MenuItemType.GENERATIVE_SUBMENU) {
|
||||
this.domNode.domNode.dispatchEvent(new Event("contextMenuActive", { bubbles: true }));
|
||||
this.selectionAugment.unsetSelection();
|
||||
}
|
||||
},
|
||||
onSelect: (itemDomNode) => {
|
||||
const item = this.domNodeToItemMap.get(itemDomNode);
|
||||
if (item.onSelect) { item.onSelect(); }
|
||||
},
|
||||
selectOnKeyHover: selectOnHover,
|
||||
selectOnMouseHover: selectOnHover,
|
||||
});
|
||||
}
|
||||
|
||||
public set display(onOff: boolean) {
|
||||
if (onOff === true) {
|
||||
this.cachedActive = document.activeElement as HTMLElement;
|
||||
if (this.cachedActive) {
|
||||
this.cachedActive.blur();
|
||||
}
|
||||
this.items.forEach((item) => !!item.refreshDomNode ? item.refreshDomNode() : null);
|
||||
this.selectionAugment.updateAllItemIsSelectableStates();
|
||||
} else if (this.cachedActive) {
|
||||
this.cachedActive.focus();
|
||||
this.cachedActive = null;
|
||||
}
|
||||
this.domNode.domNode.style.display = onOff ? "" : "none";
|
||||
}
|
||||
|
||||
public addSpacer(priority: number) {
|
||||
const rootNode = createFastDomNode(document.createElement("div"));
|
||||
rootNode.setClassName("menuitem spacer");
|
||||
const hrNode = createFastDomNode(document.createElement("hr"));
|
||||
rootNode.appendChild(hrNode);
|
||||
this.appendMenuItem({
|
||||
domNode: rootNode.domNode,
|
||||
isSelectable: false,
|
||||
priority,
|
||||
selectOnHover: false,
|
||||
type: MenuItemType.SPACER,
|
||||
});
|
||||
}
|
||||
|
||||
public addEntry(priority: number, label: string, accelerator: string, enabled: boolean, callback: () => void) {
|
||||
const domNode = createFastDomNode(document.createElement("div"));
|
||||
domNode.setClassName("menuitem entry");
|
||||
const labelNode = createFastDomNode(document.createElement("div"));
|
||||
labelNode.setClassName("entrylabel");
|
||||
labelNode.domNode.innerText = label;
|
||||
domNode.appendChild(labelNode);
|
||||
|
||||
if (accelerator) {
|
||||
const accelNode = createFastDomNode(document.createElement("div"));
|
||||
accelNode.setClassName("keybind");
|
||||
accelNode.domNode.innerText = accelerator;
|
||||
domNode.appendChild(accelNode);
|
||||
}
|
||||
|
||||
|
||||
const menuItem: IMenuItem = {
|
||||
domNode: domNode.domNode,
|
||||
isSelectable: () => enabled,
|
||||
onSelect: () => {
|
||||
if (this.cachedActive) {
|
||||
this.cachedActive.focus();
|
||||
this.cachedActive = null;
|
||||
}
|
||||
callback();
|
||||
domNode.domNode.dispatchEvent(new Event("closeAllContextMenus", { bubbles: true }));
|
||||
},
|
||||
priority,
|
||||
selectOnHover: false,
|
||||
type: MenuItemType.COMMAND,
|
||||
};
|
||||
this.appendMenuItem(menuItem);
|
||||
}
|
||||
|
||||
public addSubMenu(priority: number, subMenu: ContextMenu, label: string, description?: string) {
|
||||
const rootNode = createFastDomNode(document.createElement("div"));
|
||||
rootNode.setClassName("menuitem");
|
||||
const subLabel = createFastDomNode(document.createElement("div"));
|
||||
subLabel.setClassName("seg submenulabel");
|
||||
subLabel.domNode.innerText = label;
|
||||
const subArrow = createFastDomNode(document.createElement("div"));
|
||||
subArrow.setClassName("seg submenuarrow");
|
||||
subArrow.domNode.innerText = "->";
|
||||
rootNode.appendChild(subLabel);
|
||||
rootNode.appendChild(subArrow);
|
||||
this.appendMenuItem({
|
||||
domNode: rootNode.domNode,
|
||||
isSelectable: true,
|
||||
onSelect: () => {
|
||||
this.manager.displayMenuAtBoundary(subMenu, rootNode.domNode.getBoundingClientRect(), ["RIGHT", "BOTTOM"], false);
|
||||
},
|
||||
priority,
|
||||
selectOnHover: true,
|
||||
type: MenuItemType.SUB_MENU,
|
||||
});
|
||||
}
|
||||
|
||||
// used for generative sub menu... needs to be less public
|
||||
public removeAllItems() {
|
||||
while (this.items.length) {
|
||||
const removeMe = this.items.pop();
|
||||
removeMe.domNode.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private appendMenuItem(item: IMenuItem) {
|
||||
this.items.push(item);
|
||||
this.domNodeToItemMap.set(item.domNode, item);
|
||||
this.selectionAugment.registerChild(item.domNode);
|
||||
this.items = this.items.sort((a, b) => a.priority - b.priority);
|
||||
this.sortDomNode();
|
||||
}
|
||||
|
||||
private sortDomNode() {
|
||||
while (this.domNode.domNode.lastChild) {
|
||||
this.domNode.domNode.removeChild(this.domNode.domNode.lastChild);
|
||||
}
|
||||
this.items.forEach((item) => this.domNode.domNode.appendChild(item.domNode));
|
||||
}
|
||||
|
||||
}
|
9
packages/vscode/src/fill/native-watchdog.ts
Normal file
9
packages/vscode/src/fill/native-watchdog.ts
Normal file
@ -0,0 +1,9 @@
|
||||
class Watchdog {
|
||||
|
||||
public start(): void {
|
||||
// TODO: Should it do something?
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export = new Watchdog();
|
@ -5,7 +5,11 @@ const product = {
|
||||
nameLong: "vscode online",
|
||||
dataFolderName: ".vscode-online",
|
||||
extensionsGallery: {
|
||||
serviceUrl: "",
|
||||
serviceUrl: "https://marketplace.visualstudio.com/_apis/public/gallery",
|
||||
cacheUrl: "https://vscode.blob.core.windows.net/gallery/index",
|
||||
itemUrl: "https://marketplace.visualstudio.com/items",
|
||||
controlUrl: "https://az764295.vo.msecnd.net/extensions/marketplace.json",
|
||||
recommendationsUrl: "https://az764295.vo.msecnd.net/extensions/workspaceRecommendations.json.gz",
|
||||
},
|
||||
extensionExecutionEnvironments: {
|
||||
"wayou.vscode-todo-highlight": "worker",
|
||||
|
@ -51,16 +51,31 @@ class WindowsService implements IWindowsService {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public showMessageBox(_windowId: number, _options: MessageBoxOptions): Promise<IMessageBoxResult> {
|
||||
throw new Error("not implemented");
|
||||
public showMessageBox(windowId: number, options: MessageBoxOptions): Promise<IMessageBoxResult> {
|
||||
return new Promise((resolve): void => {
|
||||
electron.dialog.showMessageBox(this.getWindowById(windowId), options, (response, checkboxChecked) => {
|
||||
resolve({
|
||||
button: response,
|
||||
checkboxChecked,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public showSaveDialog(_windowId: number, _options: SaveDialogOptions): Promise<string> {
|
||||
throw new Error("not implemented");
|
||||
public showSaveDialog(windowId: number, options: SaveDialogOptions): Promise<string> {
|
||||
return new Promise((resolve): void => {
|
||||
electron.dialog.showSaveDialog(this.getWindowById(windowId), options, (filename, _bookmark) => {
|
||||
resolve(filename);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public showOpenDialog(_windowId: number, _options: OpenDialogOptions): Promise<string[]> {
|
||||
throw new Error("not implemented");
|
||||
public showOpenDialog(windowId: number, options: OpenDialogOptions): Promise<string[]> {
|
||||
return new Promise((resolve): void => {
|
||||
electron.dialog.showOpenDialog(this.getWindowById(windowId), options, (filePaths, _bookmarks) => {
|
||||
resolve(filePaths);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public reloadWindow(windowId: number, _args?: ParsedArgs): Promise<void> {
|
||||
|
@ -64,6 +64,7 @@ module.exports = (env) => {
|
||||
"windows-process-tree": path.resolve(fills, "empty.ts"),
|
||||
|
||||
"electron": path.join(vscodeFills, "stdioElectron.ts"),
|
||||
"native-watchdog": path.join(vscodeFills, "native-watchdog.ts"),
|
||||
"vs/platform/node/product": path.resolve(vscodeFills, "product.ts"),
|
||||
"vs/platform/node/package": path.resolve(vscodeFills, "package.ts"),
|
||||
"vs/base/node/paths": path.resolve(vscodeFills, "paths.ts"),
|
||||
|
Reference in New Issue
Block a user