mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Differentiated server icons by server type: box, big data cluster, cloud... (#5241)
This commit is contained in:
7
src/sql/azdata.proposed.d.ts
vendored
7
src/sql/azdata.proposed.d.ts
vendored
@@ -26,6 +26,8 @@ declare module 'azdata' {
|
||||
|
||||
export function registerObjectExplorerNodeProvider(provider: ObjectExplorerNodeProvider): vscode.Disposable;
|
||||
|
||||
export function registerIconProvider(provider: IconProvider): vscode.Disposable;
|
||||
|
||||
export function registerTaskServicesProvider(provider: TaskServicesProvider): vscode.Disposable;
|
||||
|
||||
export function registerFileBrowserProvider(provider: FileBrowserProvider): vscode.Disposable;
|
||||
@@ -1228,6 +1230,10 @@ declare module 'azdata' {
|
||||
handleSessionClose(closeSessionInfo: ObjectExplorerCloseSessionInfo): void;
|
||||
}
|
||||
|
||||
export interface IconProvider extends DataProvider {
|
||||
getConnectionIconId(connection: IConnectionProfile, serverInfo: ServerInfo): Thenable<string>;
|
||||
}
|
||||
|
||||
// Admin Services interfaces -----------------------------------------------------------------------
|
||||
export interface DatabaseInfo {
|
||||
options: {};
|
||||
@@ -3963,6 +3969,7 @@ declare module 'azdata' {
|
||||
DacFxServicesProvider = 'DacFxServicesProvider',
|
||||
SchemaCompareServicesProvider = 'SchemaCompareServicesProvider',
|
||||
ObjectExplorerNodeProvider = 'ObjectExplorerNodeProvider',
|
||||
IconProvider = 'IconProvider'
|
||||
}
|
||||
|
||||
export namespace dataprotocol {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ConnectionProfile } from 'sql/platform/connection/common/connectionProf
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
|
||||
import { IServerGroupDialogCallbacks } from 'sql/platform/serverGroup/common/serverGroupController';
|
||||
import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
|
||||
|
||||
export const VIEWLET_ID = 'workbench.view.connections';
|
||||
|
||||
@@ -182,6 +183,8 @@ export interface IConnectionManagementService {
|
||||
*/
|
||||
registerProvider(providerId: string, provider: azdata.ConnectionProvider): void;
|
||||
|
||||
registerIconProvider(providerId: string, provider: azdata.IconProvider): void;
|
||||
|
||||
editGroup(group: ConnectionProfileGroup): Promise<void>;
|
||||
|
||||
getConnectionProfile(fileUri: string): IConnectionProfile;
|
||||
@@ -273,6 +276,10 @@ export interface IConnectionManagementService {
|
||||
* Get connection profile by id
|
||||
*/
|
||||
getConnectionProfileById(profileId: string): IConnectionProfile;
|
||||
|
||||
getProviderProperties(providerName: string): ConnectionProviderProperties;
|
||||
|
||||
getConnectionIconId(connectionId: string): string;
|
||||
}
|
||||
|
||||
export enum RunQueryOnConnectionMode {
|
||||
|
||||
@@ -52,12 +52,15 @@ import { IConnectionDialogService } from 'sql/workbench/services/connection/comm
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import * as interfaces from './interfaces';
|
||||
|
||||
export class ConnectionManagementService extends Disposable implements IConnectionManagementService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private _providers = new Map<string, { onReady: Thenable<azdata.ConnectionProvider>, properties: ConnectionProviderProperties }>();
|
||||
private _iconProviders = new Map<string, azdata.IconProvider>();
|
||||
private _connectionIconIdCache = new Map<string, string>();
|
||||
|
||||
private _uriToProvider: { [uri: string]: string; } = Object.create(null);
|
||||
|
||||
@@ -176,6 +179,10 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
(this._providers.get(providerId).onReady as Deferred<azdata.ConnectionProvider>).resolve(provider);
|
||||
}
|
||||
|
||||
public registerIconProvider(providerId: string, iconProvider: azdata.IconProvider): void {
|
||||
this._iconProviders.set(providerId, iconProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the connection dialog
|
||||
* @param params Include the uri, type of connection
|
||||
@@ -543,10 +550,27 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
if (options.showDashboard) {
|
||||
this.showDashboardForConnectionManagementInfo(connectionManagementInfo.connectionProfile);
|
||||
}
|
||||
|
||||
let connectionProfile = connectionManagementInfo.connectionProfile;
|
||||
this._onConnect.fire(<IConnectionParams>{
|
||||
connectionUri: uri,
|
||||
connectionProfile: connectionManagementInfo.connectionProfile
|
||||
connectionProfile: connectionProfile
|
||||
});
|
||||
|
||||
let iconProvider = this._iconProviders.get(connectionManagementInfo.providerId);
|
||||
if (iconProvider) {
|
||||
let serverInfo: azdata.ServerInfo = this.getServerInfo(connectionProfile.id);
|
||||
let profile: interfaces.IConnectionProfile = connectionProfile.toIConnectionProfile();
|
||||
iconProvider.getConnectionIconId(profile, serverInfo).then(iconId => {
|
||||
if (iconId) {
|
||||
this._connectionIconIdCache.set(connectionProfile.id, iconId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public getConnectionIconId(connectionId: string): string {
|
||||
return this._connectionIconIdCache.get(connectionId);
|
||||
}
|
||||
|
||||
public showDashboard(connection: IConnectionProfile): Thenable<boolean> {
|
||||
@@ -1392,4 +1416,9 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
public getProviderProperties(providerName: string): ConnectionProviderProperties {
|
||||
let connectionProvider = this._providers.get(providerName);
|
||||
return connectionProvider && connectionProvider.properties;
|
||||
}
|
||||
}
|
||||
|
||||
3
src/sql/sqlops.proposed.d.ts
vendored
3
src/sql/sqlops.proposed.d.ts
vendored
@@ -1598,7 +1598,8 @@ declare module 'sqlops' {
|
||||
CapabilitiesProvider = 'CapabilitiesProvider',
|
||||
DacFxServicesProvider = 'DacFxServicesProvider',
|
||||
SchemaCompareServicesProvider = 'SchemaCompareServicesProvider',
|
||||
ObjectExplorerNodeProvider = 'ObjectExplorerNodeProvider'
|
||||
ObjectExplorerNodeProvider = 'ObjectExplorerNodeProvider',
|
||||
IconProvider = 'IconProvider'
|
||||
}
|
||||
|
||||
export namespace dataprotocol {
|
||||
|
||||
@@ -292,7 +292,8 @@ export enum DataProviderType {
|
||||
CapabilitiesProvider = 'CapabilitiesProvider',
|
||||
DacFxServicesProvider = 'DacFxServicesProvider',
|
||||
SchemaCompareServicesProvider = 'SchemaCompareServicesProvider',
|
||||
ObjectExplorerNodeProvider = 'ObjectExplorerNodeProvider'
|
||||
ObjectExplorerNodeProvider = 'ObjectExplorerNodeProvider',
|
||||
IconProvider = 'IconProvider'
|
||||
}
|
||||
|
||||
export enum DeclarativeDataType {
|
||||
|
||||
@@ -136,6 +136,12 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
|
||||
return rt;
|
||||
}
|
||||
|
||||
$registerIconProvider(provider: azdata.IconProvider): vscode.Disposable {
|
||||
let rt = this.registerProvider(provider, DataProviderType.IconProvider);
|
||||
this._proxy.$registerIconProvider(provider.providerId, provider.handle);
|
||||
return rt;
|
||||
}
|
||||
|
||||
$registerProfilerProvider(provider: azdata.ProfilerProvider): vscode.Disposable {
|
||||
let rt = this.registerProvider(provider, DataProviderType.ProfilerProvider);
|
||||
this._proxy.$registerProfilerProvider(provider.providerId, provider.handle);
|
||||
@@ -330,6 +336,10 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
|
||||
this._proxy.$onEditSessionReady(handle, ownerUri, success, message);
|
||||
}
|
||||
|
||||
public $getConnectionIconId(handle: number, connection: azdata.IConnectionProfile, serverInfo: azdata.ServerInfo): Thenable<string> {
|
||||
return this._resolveProvider<azdata.IconProvider>(handle).getConnectionIconId(connection, serverInfo);
|
||||
}
|
||||
|
||||
// Metadata handlers
|
||||
public $getMetadata(handle: number, connectionUri: string): Thenable<azdata.ProviderMetadata> {
|
||||
return this._resolveProvider<azdata.MetadataProvider>(handle).getMetadata(connectionUri);
|
||||
|
||||
@@ -278,6 +278,16 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public $registerIconProvider(providerId: string, handle: number): Promise<any> {
|
||||
const self = this;
|
||||
this._connectionManagementService.registerIconProvider(providerId, <azdata.IconProvider>{
|
||||
getConnectionIconId(connection: azdata.IConnectionProfile, serverInfo: azdata.ServerInfo): Thenable<string> {
|
||||
return self._proxy.$getConnectionIconId(handle, connection, serverInfo);
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public $registerTaskServicesProvider(providerId: string, handle: number): Promise<any> {
|
||||
const self = this;
|
||||
this._taskService.registerProvider(providerId, <azdata.TaskServicesProvider>{
|
||||
|
||||
@@ -269,6 +269,10 @@ export function createApiFactory(
|
||||
return extHostDataProvider.$registerObjectExplorerNodeProvider(provider);
|
||||
};
|
||||
|
||||
let registerIconProvider = (provider: azdata.IconProvider): vscode.Disposable => {
|
||||
return extHostDataProvider.$registerIconProvider(provider);
|
||||
};
|
||||
|
||||
let registerTaskServicesProvider = (provider: azdata.TaskServicesProvider): vscode.Disposable => {
|
||||
provider.registerOnTaskCreated((response: azdata.TaskInfo) => {
|
||||
extHostDataProvider.$onTaskCreated(provider.handle, response);
|
||||
@@ -365,6 +369,7 @@ export function createApiFactory(
|
||||
registerMetadataProvider,
|
||||
registerObjectExplorerProvider,
|
||||
registerObjectExplorerNodeProvider,
|
||||
registerIconProvider,
|
||||
registerProfilerProvider,
|
||||
registerRestoreProvider,
|
||||
registerScriptingProvider,
|
||||
|
||||
@@ -102,6 +102,8 @@ export abstract class ExtHostDataProtocolShape {
|
||||
|
||||
$getServerCapabilities(handle: number, client: azdata.DataProtocolClientCapabilities): Thenable<azdata.DataProtocolServerCapabilities> { throw ni(); }
|
||||
|
||||
$getConnectionIconId(handle: number, connection: azdata.IConnectionProfile, serverInfo: azdata.ServerInfo): Thenable<string> { throw ni(); }
|
||||
|
||||
/**
|
||||
* Metadata service methods
|
||||
*
|
||||
@@ -541,6 +543,7 @@ export interface MainThreadDataProtocolShape extends IDisposable {
|
||||
$registerProfilerProvider(providerId: string, handle: number): Promise<any>;
|
||||
$registerObjectExplorerProvider(providerId: string, handle: number): Promise<any>;
|
||||
$registerObjectExplorerNodeProvider(providerId: string, supportedProviderId: string, group: string, handle: number): Promise<any>;
|
||||
$registerIconProvider(providerId: string, handle: number): Promise<any>;
|
||||
$registerMetadataProvider(providerId: string, handle: number): Promise<any>;
|
||||
$registerTaskServicesProvider(providerId: string, handle: number): Promise<any>;
|
||||
$registerFileBrowserProvider(providerId: string, handle: number): Promise<any>;
|
||||
|
||||
@@ -11,6 +11,8 @@ import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as path from 'path';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export interface ConnectionProviderProperties {
|
||||
providerId: string;
|
||||
@@ -66,6 +68,47 @@ const ConnectionProviderContrib: IJSONSchema = {
|
||||
type: 'string',
|
||||
description: localize('schema.displayName', "Display Name for the provider")
|
||||
},
|
||||
iconPath: {
|
||||
description: localize('schema.iconPath', 'Icon path for the server type'),
|
||||
oneOf: [
|
||||
{
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
},
|
||||
path: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
light: {
|
||||
type: 'string',
|
||||
},
|
||||
dark: {
|
||||
type: 'string',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
light: {
|
||||
type: 'string',
|
||||
},
|
||||
dark: {
|
||||
type: 'string',
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'string'
|
||||
}
|
||||
]
|
||||
},
|
||||
connectionOptions: {
|
||||
type: 'array',
|
||||
description: localize('schema.connectionOptions', "Options for connection"),
|
||||
@@ -123,6 +166,7 @@ ExtensionsRegistry.registerExtensionPoint<ConnectionProviderProperties | Connect
|
||||
|
||||
for (let extension of extensions) {
|
||||
const { value } = extension;
|
||||
resolveIconPath(extension);
|
||||
if (Array.isArray<ConnectionProviderProperties>(value)) {
|
||||
for (let command of value) {
|
||||
handleCommand(command, extension);
|
||||
@@ -132,3 +176,39 @@ ExtensionsRegistry.registerExtensionPoint<ConnectionProviderProperties | Connect
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function resolveIconPath(extension: IExtensionPointUser<any>): void {
|
||||
if (!extension || !extension.value) { return undefined; }
|
||||
|
||||
let toAbsolutePath = (iconPath: any, baseDir: string) => {
|
||||
if (!iconPath || !baseDir) { return; }
|
||||
if (Array.isArray(iconPath)) {
|
||||
for (let e of iconPath) {
|
||||
e.path = {
|
||||
light: URI.file(path.join(baseDir, e.path.light)),
|
||||
dark: URI.file(path.join(baseDir, e.path.dark))
|
||||
};
|
||||
}
|
||||
} else if (typeof iconPath === 'string') {
|
||||
iconPath = {
|
||||
light: URI.file(path.join(baseDir, iconPath)),
|
||||
dark: URI.file(path.join(baseDir, iconPath))
|
||||
};
|
||||
} else {
|
||||
iconPath = {
|
||||
light: URI.file(path.join(baseDir, iconPath.light)),
|
||||
dark: URI.file(path.join(baseDir, iconPath.dark))
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let baseDir = extension.description.extensionLocation.fsPath;
|
||||
let properties: ConnectionProviderProperties = extension.value;
|
||||
if (Array.isArray<ConnectionProviderProperties>(properties)) {
|
||||
for (let p of properties) {
|
||||
toAbsolutePath(p['iconPath'], baseDir);
|
||||
}
|
||||
} else {
|
||||
toAbsolutePath(properties['iconPath'], baseDir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,25 +10,35 @@ export class ServerInfoContextKey implements IContextKey<ServerInfo> {
|
||||
|
||||
static ServerInfo = new RawContextKey<ServerInfo>('serverInfo', undefined);
|
||||
static ServerMajorVersion = new RawContextKey<string>('serverMajorVersion', undefined);
|
||||
static IsCloud = new RawContextKey<boolean>('isCloud', undefined);
|
||||
static IsBigDataCluster = new RawContextKey<boolean>('isBigDataCluster', undefined);
|
||||
|
||||
private _serverInfo: IContextKey<ServerInfo>;
|
||||
private _serverMajorVersion: IContextKey<string>;
|
||||
private _isCloud: IContextKey<boolean>;
|
||||
private _isBigDataCluster: IContextKey<boolean>;
|
||||
|
||||
constructor(
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
) {
|
||||
this._serverInfo = ServerInfoContextKey.ServerInfo.bindTo(contextKeyService);
|
||||
this._serverMajorVersion = ServerInfoContextKey.ServerMajorVersion.bindTo(contextKeyService);
|
||||
this._isCloud = ServerInfoContextKey.IsCloud.bindTo(contextKeyService);
|
||||
this._isBigDataCluster = ServerInfoContextKey.IsBigDataCluster.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
set(value: ServerInfo) {
|
||||
this._serverInfo.set(value);
|
||||
let majorVersion = value && value.serverMajorVersion;
|
||||
this._serverMajorVersion.set(majorVersion && `${majorVersion}`);
|
||||
this._isCloud.set(value && value.isCloud);
|
||||
this._isBigDataCluster.set(value && value.options && value.options['isBigDataCluster']);
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this._serverMajorVersion.reset();
|
||||
this._isCloud.reset();
|
||||
this._isBigDataCluster.reset();
|
||||
}
|
||||
|
||||
public get(): ServerInfo {
|
||||
|
||||
@@ -98,22 +98,13 @@ margin-bottom: 2px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.connected {
|
||||
background: url('connected_active_server.svg') center center no-repeat;
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page {
|
||||
background: url('default_server.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.connected,
|
||||
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.connected{
|
||||
background: url('connected_active_server_inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.disconnected {
|
||||
background: url('disconnected_server.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.disconnected,
|
||||
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.disconnected{
|
||||
background: url('disconnected_server_inverse.svg') center center no-repeat;
|
||||
.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page,
|
||||
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page{
|
||||
background: url('default_server_inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
/* loading for OE node */
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
viewBox="0 0 16 16"
|
||||
data-name="Layer 1"
|
||||
id="Layer_1">
|
||||
<metadata
|
||||
id="metadata15">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>server_16x16</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs4">
|
||||
<style
|
||||
id="style2">.cls-1{fill:#212121;}.cls-2{fill:#231f20;}</style>
|
||||
</defs>
|
||||
<title
|
||||
id="title6">server_16x16</title>
|
||||
<path
|
||||
style="fill:#212121"
|
||||
id="path8"
|
||||
d="m 2.735,0 v 16 h 10.53 V 0 Z m 1,1 h 8.53 v 9 h -8.53 z m 8.53,14 h -8.53 v -4 h 8.53 z"
|
||||
class="cls-1" />
|
||||
<path
|
||||
style="fill:#231f20"
|
||||
id="path10"
|
||||
d="M 7.125,4.23 H 4.675 V 1.77 h 2.45 z m -2,-0.5 h 1.5 V 2.27 h -1.45 z"
|
||||
class="cls-2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
viewBox="0 0 16 16"
|
||||
data-name="Layer 1"
|
||||
id="Layer_1">
|
||||
<metadata
|
||||
id="metadata15">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>server_16x16</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs4">
|
||||
<style
|
||||
id="style2">.cls-1{fill:#212121;}.cls-2{fill:#231f20;}</style>
|
||||
</defs>
|
||||
<title
|
||||
id="title6">server_16x16</title>
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
id="path8"
|
||||
d="m 2.735,0 v 16 h 10.53 V 0 Z m 1,1 h 8.53 v 9 h -8.53 z m 8.53,14 h -8.53 v -4 h 8.53 z"
|
||||
class="cls-1" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
id="path10"
|
||||
d="M 7.125,4.23 H 4.675 V 1.77 h 2.45 z m -2,-0.5 h 1.5 V 2.27 h -1.45 z"
|
||||
class="cls-2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
137
src/sql/workbench/parts/objectExplorer/browser/iconRenderer.ts
Normal file
137
src/sql/workbench/parts/objectExplorer/browser/iconRenderer.ts
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -11,6 +11,8 @@ import { ConnectionProfile } from 'sql/platform/connection/common/connectionProf
|
||||
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
|
||||
import * as azdata from 'azdata';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
|
||||
|
||||
// Test stubs for commonly used objects
|
||||
|
||||
@@ -35,6 +37,10 @@ export class TestConnectionManagementService implements IConnectionManagementSer
|
||||
|
||||
}
|
||||
|
||||
registerIconProvider(providerId: string, provider: azdata.IconProvider): void {
|
||||
|
||||
}
|
||||
|
||||
showConnectionDialog(params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): Promise<void> {
|
||||
return undefined;
|
||||
}
|
||||
@@ -273,4 +279,12 @@ export class TestConnectionManagementService implements IConnectionManagementSer
|
||||
getConnectionProfileById(profileId: string): IConnectionProfile {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getProviderProperties(providerName: string): ConnectionProviderProperties {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getConnectionIconId(connectionId: string): string {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user