Differentiated server icons by server type: box, big data cluster, cloud... (#5241)

This commit is contained in:
Gene Lee
2019-05-13 14:52:56 -07:00
committed by GitHub
parent 7da0dddaa9
commit 99d00e2057
25 changed files with 693 additions and 19 deletions

View File

@@ -0,0 +1,137 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createCSSRule } from 'vs/base/browser/dom';
import { hash } from 'vs/base/common/hash';
import { URI } from 'vs/base/common/uri';
class IconRenderer {
private iconRegistered: Set<string> = new Set<string>();
public registerIcon(path: URI | IconPath): string {
if (!path) { return undefined; }
let iconPath: IconPath = this.toIconPath(path);
let iconUid: string = this.getIconUid(iconPath);
if (!this.iconRegistered.has(iconUid)) {
createCSSRule(`.icon#${iconUid}`, `background: url("${iconPath.light.toString()}") center center no-repeat`);
createCSSRule(`.vs-dark .icon#${iconUid}, .hc-black .icon#${iconUid}`, `background: url("${iconPath.dark.toString()}") center center no-repeat`);
this.iconRegistered.add(iconUid);
}
return iconUid;
}
public getIconUid(path: URI | IconPath): string {
if (!path) { return undefined; }
let iconPath: IconPath = this.toIconPath(path);
return `icon${hash(iconPath.light.toString() + iconPath.dark.toString())}`;
}
private toIconPath(path: URI | IconPath): IconPath {
if (path['light']) {
return path as IconPath;
} else {
let singlePath = path as URI;
return { light: singlePath, dark: singlePath };
}
}
public putIcon(element: HTMLElement, path: URI | IconPath): void {
if (!element || !path) { return undefined; }
let iconUid: string = this.registerIcon(path);
element.id = iconUid;
}
public removeIcon(element: HTMLElement): void {
if (!element) { return undefined; }
element.id = undefined;
}
}
export const iconRenderer: IconRenderer = new IconRenderer();
class BadgeRenderer {
public readonly serverConnected: string = 'serverConnected';
public readonly serverDisconnected: string = 'serverDisconnected';
public readonly newTag: string = 'newTag';
private badgeCreated: Set<string> = new Set<string>();
constructor() {
this.createBadge(this.serverConnected, this.getConnectionStatusBadge(true));
this.createBadge(this.serverDisconnected, this.getConnectionStatusBadge(false));
this.createBadge(this.newTag, this.getNewTagBadge());
}
private getConnectionStatusBadge(isConnected: boolean) {
let circleColor: string = isConnected ? 'rgba(59, 180, 74, 100%)' : 'rgba(208, 46, 0, 100%)';
let bgColor: string = isConnected ? 'rgba(59, 180, 74, 100%)' : 'rgba(255, 255, 255, 80%)';
return `position: absolute;
height: 0.25rem;
width: 0.25rem;
top: 14px;
left: 19px;
border: 0.12rem solid ${circleColor};
border-radius: 100%;
background: ${bgColor};
content:"";
font-size: 100%;
line-height: 100%;
color:white;
text-align:center;
vertical-align:middle;`
.replace(/\t/g, ' ').replace(/\r?\n/g, ' ').replace(/ +/g, ' ');
}
private getNewTagBadge(): string {
return `position: absolute;
height: 0.4rem;
width: 0.4rem;
top: 3px;
left: 5px;
border: 1px solid green;
border-radius: 15%;
background: green;
content:"N";
font-size: 0.3rem;
font-weight: bold;
line-height: 0.4rem;
color: white;
text-align:center;
vertical-align:middle;`
.replace(/\t/g, ' ').replace(/\r?\n/g, ' ').replace(/ +/g, ' ');
}
private createBadge(badgeClass: string, badge: string): void {
if (!this.badgeCreated.has(badgeClass)) {
createCSSRule(`.${badgeClass}:after`, badge);
this.badgeCreated.add(badgeClass);
}
}
public addBadge(element: HTMLElement, badgeClass: string): void {
element.innerHTML = (element.innerHTML || '') +
`<div class="${badgeClass}" style="width: 0px; height: 0px;"><div>`;
}
public removeBadge(element: HTMLElement, badgeClass: string): void {
let children: HTMLCollection = element.children;
let current = children[0];
while (current) {
let next = current.nextElementSibling;
if (current.classList.contains(badgeClass)) {
current.remove();
break;
}
current = next;
}
}
}
export const badgeRenderer: BadgeRenderer = new BadgeRenderer();
interface IconPath {
light: URI;
dark: URI;
}

View File

@@ -14,6 +14,9 @@ import { ITree, IRenderer } from 'vs/base/parts/tree/browser/tree';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { TreeNode } from 'sql/workbench/parts/objectExplorer/common/treeNode';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { badgeRenderer, iconRenderer } from 'sql/workbench/parts/objectExplorer/browser/iconRenderer';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { URI } from 'vs/base/common/uri';
export interface IConnectionTemplateData {
root: HTMLElement;
@@ -56,7 +59,8 @@ export class ServerTreeRenderer implements IRenderer {
constructor(
isCompact: boolean,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IContextKeyService private _contextKeyService: IContextKeyService
) {
// isCompact defaults to false unless explicitly set by instantiation call.
if (isCompact) {
@@ -152,19 +156,70 @@ export class ServerTreeRenderer implements IRenderer {
let iconLowerCaseName = iconName.toLocaleLowerCase();
templateData.icon.classList.add(iconLowerCaseName);
if (treeNode.iconPath) {
iconRenderer.putIcon(templateData.icon, treeNode.iconPath);
}
templateData.label.textContent = treeNode.label;
templateData.root.title = treeNode.label;
}
private getIconPath(connection: ConnectionProfile): IconPath {
if (!connection) { return undefined; }
if (connection['iconPath']) {
return connection['iconPath'];
}
let iconId = this._connectionManagementService.getConnectionIconId(connection.id);
if (!iconId) { return undefined; }
let providerProperties = this._connectionManagementService.getProviderProperties(connection.providerName);
if (!providerProperties) { return undefined; }
let iconPath: IconPath = undefined;
let pathConfig: URI | IconPath | { id: string, path: IconPath }[] = providerProperties['iconPath'];
if (Array.isArray(pathConfig)) {
for (const e of pathConfig) {
if (!e.id || e.id === iconId) {
iconPath = e.path;
connection['iconPath'] = iconPath;
break;
}
}
} else if (pathConfig['light']) {
iconPath = pathConfig as IconPath;
connection['iconPath'] = iconPath;
} else {
let singlePath = pathConfig as URI;
iconPath = { light: singlePath, dark: singlePath };
connection['iconPath'] = iconPath;
}
return iconPath;
}
private renderServerIcon(element: HTMLElement, iconPath: IconPath, isConnected: boolean): void {
if (!element) { return; }
if (iconPath) {
iconRenderer.putIcon(element, iconPath);
}
let badgeToRemove: string = isConnected ? badgeRenderer.serverDisconnected : badgeRenderer.serverConnected;
let badgeToAdd: string = isConnected ? badgeRenderer.serverConnected : badgeRenderer.serverDisconnected;
badgeRenderer.removeBadge(element, badgeToRemove);
badgeRenderer.addBadge(element, badgeToAdd);
}
private renderConnection(connection: ConnectionProfile, templateData: IConnectionTemplateData): void {
if (!this._isCompact) {
let iconPath: IconPath = this.getIconPath(connection);
if (this._connectionManagementService.isConnected(undefined, connection)) {
templateData.icon.classList.remove('disconnected');
templateData.icon.classList.add('connected');
this.renderServerIcon(templateData.icon, iconPath, true);
} else {
templateData.icon.classList.remove('connected');
templateData.icon.classList.add('disconnected');
this.renderServerIcon(templateData.icon, iconPath, false);
}
}
@@ -217,3 +272,7 @@ export class ServerTreeRenderer implements IRenderer {
}
}
interface IconPath {
light: URI;
dark: URI;
}

View File

@@ -8,6 +8,7 @@ import { NodeType, SqlThemeIcon } from 'sql/workbench/parts/objectExplorer/commo
import * as azdata from 'sqlops';
import * as UUID from 'vs/base/common/uuid';
import { URI } from 'vs/base/common/uri';
export enum TreeItemCollapsibleState {
None = 0,
@@ -91,6 +92,8 @@ export class TreeNode {
public iconType: string | SqlThemeIcon;
public iconPath: URI | { light: URI, dark: URI };
constructor(nodeTypeId: string, label: string, isAlwaysLeaf: boolean, nodePath: string,
nodeSubType: string, nodeStatus: string, parent: TreeNode, metadata: azdata.ObjectMetadata,
iconType: string | SqlThemeIcon,